Skip to content
Open
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
3 changes: 3 additions & 0 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ and libc version. The table below lists the support tier for each supported
combination. A list of [supported compile toolchains](#supported-toolchains) is
also supplied for tier 1 platforms.

Node.js requires WebAssembly for built-in functionality, including `fetch()` and
TypeScript support. JIT-less execution and V8 lite mode are not supported.

**For production applications, run Node.js on supported platforms only (Tier 1 or 2).**

Node.js does not support a platform version if a vendor has expired support
Expand Down
15 changes: 5 additions & 10 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1224,14 +1224,6 @@
default=False,
help='compile V8 with debug checks and runtime debugging features enabled')

parser.add_argument('--v8-lite-mode',
action='store_true',
dest='v8_lite_mode',
default=False,
help='compile V8 in lite mode for constrained environments (lowers V8 '+
'memory footprint, but also implies no just-in-time compilation ' +
'support, thus much slower execution)')

parser.add_argument('--v8-enable-object-print',
action='store_true',
dest='v8_enable_object_print',
Expand Down Expand Up @@ -1318,6 +1310,10 @@

(options, args) = parser.parse_known_args()

# Reject the removed option before forwarding unknown arguments to GYP.
if any(arg.split('=', 1)[0] == '--v8-lite-mode' for arg in args):
parser.error('--v8-lite-mode is no longer supported; Node.js requires WebAssembly')

# Expand ~ in the install prefix now, it gets written to multiple files.
options.prefix = str(Path(options.prefix or '').expanduser())

Expand Down Expand Up @@ -2231,9 +2227,8 @@ def configure_library(lib, output, pkgname=None):
def configure_v8(o, configs):
set_configuration_variable(configs, 'v8_enable_v8_checks', release=0, debug=1)

o['variables']['v8_enable_webassembly'] = 0 if options.v8_lite_mode else 1
o['variables']['v8_enable_webassembly'] = 1
o['variables']['v8_enable_javascript_promise_hooks'] = 1
o['variables']['v8_enable_lite_mode'] = 1 if options.v8_lite_mode else 0
is_gdbjit_supported_arch = (
'x64' in o['variables']['target_arch'] or
'ia32' in o['variables']['target_arch'] or
Expand Down
17 changes: 0 additions & 17 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -2196,19 +2196,6 @@ added: v0.7.7

Opens the REPL even if stdin does not appear to be a terminal.

### `--jitless`

<!-- YAML
added: v12.0.0
-->

> Stability: 1 - Experimental. This flag is inherited from V8 and is subject to
> change upstream.

Disable [runtime allocation of executable memory][jitless]. This may be
required on some platforms for security reasons. It can also reduce attack
surface on other platforms, but the performance impact may be severe.

### `--localstorage-file=file`

<!-- YAML
Expand Down Expand Up @@ -4423,7 +4410,6 @@ V8 options that are allowed are:
* `--enable-etw-stack-walking`
* `--expose-gc`
* `--interpreted-frames-native-stack`
* `--jitless`
* `--max-heap-size`
* `--max-old-space-size`
* `--max-semi-space-size`
Expand Down Expand Up @@ -4794,8 +4780,6 @@ documented here:

### `--interpreted-frames-native-stack`

### `--jitless`

### `--max-heap-size`

Specifies the maximum heap size (in megabytes) for the process.
Expand Down Expand Up @@ -4989,7 +4973,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
[environment_variables]: #environment-variables-1
[filtering tests by name]: test.md#filtering-tests-by-name
[global setup and teardown]: test.md#global-setup-and-teardown
[jitless]: https://v8.dev/blog/jitless
[libuv threadpool documentation]: https://docs.libuv.org/en/latest/threadpool.html
[module compile cache]: module.md#module-compile-cache
[preloading asynchronous module customization hooks]: module.md#registration-of-asynchronous-customization-hooks
Expand Down
20 changes: 12 additions & 8 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -3645,14 +3645,6 @@ The WASI instance has already started.

The WASI instance has not been started.

<a id="ERR_WEBASSEMBLY_NOT_SUPPORTED"></a>

### `ERR_WEBASSEMBLY_NOT_SUPPORTED`

A feature requiring WebAssembly was used, but WebAssembly is not supported or
has been disabled in the current environment (for example, when running with
`--jitless`).

<a id="ERR_WEBASSEMBLY_RESPONSE"></a>

### `ERR_WEBASSEMBLY_RESPONSE`
Expand Down Expand Up @@ -4488,6 +4480,18 @@ The linker function returned a module for which linking has failed.

The module must be successfully linked before instantiation.

<a id="ERR_WEBASSEMBLY_NOT_SUPPORTED"></a>

### `ERR_WEBASSEMBLY_NOT_SUPPORTED`

<!-- YAML
removed: REPLACEME
-->

A feature requiring WebAssembly was used in an environment where WebAssembly
was unavailable. Node.js now requires WebAssembly and no longer supports
JIT-less execution.

<a id="ERR_WORKER_UNSUPPORTED_EXTENSION"></a>

### `ERR_WORKER_UNSUPPORTED_EXTENSION`
Expand Down
7 changes: 0 additions & 7 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -1175,11 +1175,6 @@ See V8 Inspector integration for Node.js for further explanation on Node.js debu
.It Fl i , Fl -interactive
Opens the REPL even if stdin does not appear to be a terminal.
.
.It Fl -jitless
Disable runtime allocation of executable memory. This may be
required on some platforms for security reasons. It can also reduce attack
surface on other platforms, but the performance impact may be severe.
.
.It Fl -localstorage-file Ns = Ns Ar file
The file used to store \fBlocalStorage\fR data. If the file does not exist, it is
created the first time \fBlocalStorage\fR is accessed. The same file may be shared
Expand Down Expand Up @@ -2536,8 +2531,6 @@ V8 options that are allowed are:
.It
\fB--interpreted-frames-native-stack\fR
.It
\fB--jitless\fR
.It
\fB--max-heap-size\fR
.It
\fB--max-old-space-size\fR
Expand Down
4 changes: 2 additions & 2 deletions lib/eslint.config_partial.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ export default [
name: 'URLSearchParams',
message: "Use `const { URLSearchParams } = require('internal/url');` instead of the global.",
},
// WebAssembly is not available in primordials because it can be
// disabled with --jitless CLI flag.
// WebAssembly is unavailable during snapshot building and must be
// accessed lazily at runtime.
{
name: 'WebAssembly',
message: 'Use `const { WebAssembly } = globalThis;` instead of the global.',
Expand Down
3 changes: 0 additions & 3 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1981,9 +1981,6 @@ E('ERR_VM_MODULE_NOT_MODULE',
'Provided module is not an instance of Module', Error);
E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error);
E('ERR_WEBASSEMBLY_NOT_SUPPORTED',
'WebAssembly is not supported in this environment, but is required for %s',
Error);
E('ERR_WEBASSEMBLY_RESPONSE', 'WebAssembly response %s', TypeError);
E('ERR_WORKER_HANDLE_NOT_TRANSFERABLE',
'%s cannot be transferred in its current state; it must be a freshly ' +
Expand Down
22 changes: 10 additions & 12 deletions lib/internal/freeze_intrinsics.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,18 +363,16 @@ module.exports = function() {
ArrayPrototypePush(intrinsics, SharedArrayBuffer);
}

if (typeof WebAssembly !== 'undefined') {
ArrayPrototypePush(intrinsicPrototypes,
WebAssembly.Module.prototype,
WebAssembly.Instance.prototype,
WebAssembly.Table.prototype,
WebAssembly.Memory.prototype,
WebAssembly.CompileError.prototype,
WebAssembly.LinkError.prototype,
WebAssembly.RuntimeError.prototype,
);
ArrayPrototypePush(intrinsics, WebAssembly);
}
ArrayPrototypePush(intrinsicPrototypes,
WebAssembly.Module.prototype,
WebAssembly.Instance.prototype,
WebAssembly.Table.prototype,
WebAssembly.Memory.prototype,
WebAssembly.CompileError.prototype,
WebAssembly.LinkError.prototype,
WebAssembly.RuntimeError.prototype,
);
ArrayPrototypePush(intrinsics, WebAssembly);

if (typeof Intl !== 'undefined') {
ArrayPrototypePush(intrinsicPrototypes,
Expand Down
4 changes: 0 additions & 4 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,13 @@ const {
SymbolPrototypeGetDescription,
SymbolReplace,
SymbolSplit,
globalThis,
} = primordials;

const {
codes: {
ERR_NO_CRYPTO,
ERR_NO_TYPESCRIPT,
ERR_UNKNOWN_SIGNAL,
ERR_WEBASSEMBLY_NOT_SUPPORTED,
},
isErrorStackTraceLimitWritable,
overrideStackTrace,
Expand Down Expand Up @@ -245,8 +243,6 @@ function assertCrypto() {
function assertTypeScript() {
if (noTypeScript)
throw new ERR_NO_TYPESCRIPT();
if (globalThis.WebAssembly === undefined)
throw new ERR_WEBASSEMBLY_NOT_SUPPORTED('TypeScript');
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
#include "node_version.h"
#include "permission/env_permission.h"

#if defined(V8_JITLESS) || defined(V8_LITE_MODE)
#error Node.js does not support interpreter-only V8 builds.
#endif

#if HAVE_OPENSSL
#include "ncrypto.h"
#if OPENSSL_VERSION_MAJOR >= 3
Expand Down Expand Up @@ -1108,6 +1112,12 @@ static ExitCode InitializeNodeWithArgsInternal(

allow_env_sources.Finish();

// Node.js requires WebAssembly for built-in functionality. Override these
// modes after all option sources, before V8 applies their implications.
// Explicitly allow overriding even when contradiction checks are enabled.
V8::SetFlagsFromString("--allow-overwriting-for-next-flag --no-lite-mode "
"--allow-overwriting-for-next-flag --no-jitless");

// Every option source has now been parsed, so cross-source option
// constraints can finally be validated.
CheckGlobalBenchOptions(errors);
Expand Down
4 changes: 0 additions & 4 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1568,10 +1568,6 @@ PerIsolateOptionsParser::PerIsolateOptionsParser(
"disallow eval and friends",
V8Option{},
kAllowedInEnvvar);
AddOption("--jitless",
"disable runtime allocation of executable memory",
V8Option{},
kAllowedInEnvvar);
AddOption("--report-uncaught-exception",
"generate diagnostic report on uncaught exceptions",
BOOL_FIELD(report_uncaught_exception),
Expand Down
4 changes: 4 additions & 0 deletions src/node_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
Utf8Value flags(args.GetIsolate(), args[0]);
V8::SetFlagsFromString(flags.out(), flags.length());
// Runtime flags must not re-enable modes that disable required WebAssembly
// support, even when contradiction checks are enabled.
V8::SetFlagsFromString("--allow-overwriting-for-next-flag --no-lite-mode "
"--allow-overwriting-for-next-flag --no-jitless");
}

void StartCpuProfile(const FunctionCallbackInfo<Value>& args) {
Expand Down
2 changes: 1 addition & 1 deletion test/doctool/test-doc-api-json.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,4 @@ for await (const dirent of await fs.opendir(new URL('../../out/doc/api/', import
}

assert.strictEqual(numberOfDeprecatedSections, 49); // Increase this number every time a new API is deprecated.
assert.strictEqual(numberOfRemovedAPIs, 46); // Increase this number every time a section is marked as removed.
assert.strictEqual(numberOfRemovedAPIs, 47); // Increase this number every time a section is marked as removed.
6 changes: 0 additions & 6 deletions test/es-module/test-import-cjs-jitless.mjs

This file was deleted.

11 changes: 0 additions & 11 deletions test/es-module/test-typescript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -318,14 +318,3 @@ test('execute invalid TypeScript syntax', async () => {
assert.strictEqual(result.stdout, '');
assert.strictEqual(result.code, 1);
});

test('expect error when executing a TypeScript file with --jitless', async () => {
const result = await spawnPromisified(process.execPath, [
'--jitless',
fixtures.path('typescript/ts/test-typescript.ts'),
]);

assert.match(result.stderr, /ERR_WEBASSEMBLY_NOT_SUPPORTED/);
assert.match(result.stderr, /WebAssembly is not supported in this environment, but is required for TypeScript/);
assert.strictEqual(result.code, 1);
});
90 changes: 90 additions & 0 deletions test/parallel/test-cli-no-jitless.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const { spawnSync } = require('child_process');

// Interpreter-only modes are overridden to preserve Node's WebAssembly
// requirement, including alternate spellings and the runtime flag API.
const wasm = `
const assert = require('assert');
assert.strictEqual(typeof WebAssembly, 'object');
const bytes = Uint8Array.of(0, 97, 115, 109, 1, 0, 0, 0);
new WebAssembly.Instance(new WebAssembly.Module(bytes));
`;

for (const flag of [
'--jitless', '--lite-mode', '--lite_mode', '-jitless', '-lite_mode',
'--no-jitless', '--no-lite-mode',
]) {
const result = spawnSync(process.execPath, [flag, '-e', wasm], { encoding: 'utf8' });
assert.strictEqual(result.status, 0, result.stderr);
assert.strictEqual(result.signal, null);
assert.strictEqual(result.stderr, '');

const runtime = spawnSync(process.execPath, ['-e', `
require('v8').setFlagsFromString(${JSON.stringify(flag)});
${wasm}
// A new isolate must also retain WebAssembly after changing runtime flags.
new (require('worker_threads').Worker)(${JSON.stringify(wasm)}, { eval: true });
`], { encoding: 'utf8' });
assert.strictEqual(runtime.status, 0, runtime.stderr);
assert.strictEqual(runtime.stderr, '');
}

// V8 boolean flags reject explicit values, even for overridden modes.
for (const flag of ['--jitless=true', '--lite_mode=true']) {
const result = spawnSync(process.execPath, [flag, '-e', wasm], { encoding: 'utf8' });
assert.strictEqual(result.status, 9, result.stderr);
assert.strictEqual(result.signal, null);
assert.match(result.stderr, /illegal value for flag .* of type bool/);

// The runtime API reports invalid syntax without throwing, and Node must
// still override any flags V8 changed before reporting the error.
const runtime = spawnSync(process.execPath, ['-e', `
require('v8').setFlagsFromString(${JSON.stringify(flag)});
${wasm}
new (require('worker_threads').Worker)(${JSON.stringify(wasm)}, { eval: true });
`], { encoding: 'utf8' });
assert.strictEqual(runtime.status, 0, runtime.stderr);
assert.strictEqual(runtime.signal, null);
assert.match(runtime.stderr, /illegal value for flag .* of type bool/);
}

// Node's overrides must not be treated as contradictory user-supplied flags.
for (const flag of ['--jitless', '--lite-mode']) {
const result = spawnSync(process.execPath, [
'--abort-on-contradictory-flags', flag, '-e', wasm,
], { encoding: 'utf8' });
assert.strictEqual(result.status, 0, result.stderr);
assert.strictEqual(result.signal, null);
assert.strictEqual(result.stderr, '');
}

const options = spawnSync(process.execPath, ['--v8-options'], { encoding: 'utf8' });
assert.strictEqual(options.status, 0, options.stderr);
assert.match(options.stdout, /--jitless\b/);
assert.match(options.stdout, /--lite-mode\b/);

for (const flag of ['--jitless', '--lite-mode', '--lite_mode']) {
assert.strictEqual(process.allowedNodeEnvironmentFlags.has(flag), false);
if (!process.config.variables.node_without_node_options) {
const result = spawnSync(process.execPath, ['-e', ''], {
encoding: 'utf8',
env: { ...process.env, NODE_OPTIONS: flag },
});
assert.strictEqual(result.status, 9, result.stderr);
assert.match(result.stderr, /is not allowed in NODE_OPTIONS/);
}
}

// Workers must inherit a runtime with working WebAssembly as well.
const { Worker } = require('worker_threads');
const worker = new Worker(`
const { parentPort } = require('worker_threads');
const bytes = Uint8Array.of(0, 97, 115, 109, 1, 0, 0, 0);
new WebAssembly.Instance(new WebAssembly.Module(bytes));
parentPort.postMessage(typeof WebAssembly);
`, { eval: true });
worker.on('message', common.mustCall((value) => assert.strictEqual(value, 'object')));
worker.on('exit', common.mustCall((code) => assert.strictEqual(code, 0)));
1 change: 0 additions & 1 deletion test/parallel/test-cli-node-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ if (common.hasCrypto) {
expect('--abort_on-uncaught_exception', 'B\n');
expect('--disallow-code-generation-from-strings', 'B\n');
expect('--expose-gc', 'B\n');
expect('--jitless', 'B\n');
expect('--max-old-space-size=0', 'B\n');
expect('--max-semi-space-size=0', 'B\n');
expect('--stack-trace-limit=100',
Expand Down
Loading
Loading