diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index b9770e23a2e353..9cc673d5a62fe7 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -6,6 +6,11 @@ updates:
directory: /
schedule:
interval: monthly
+ cooldown:
+ - semver-major-days: 5
+ - semver-minor-days: 5
+ - semver-patch-days: 5
+
commit-message:
prefix: meta
open-pull-requests-limit: ${{secrets.OPEN_PR_LIMIT}}
@@ -14,6 +19,10 @@ updates:
directory: /tools/eslint
schedule:
interval: monthly
+ cooldown:
+ - semver-major-days: 5
+ - semver-minor-days: 5
+ - semver-patch-days: 5
commit-message:
prefix: tools
open-pull-requests-limit: ${{secrets.OPEN_PR_LIMIT}}
@@ -27,6 +36,10 @@ updates:
directory: /tools/lint-md
schedule:
interval: monthly
+ cooldown:
+ - semver-major-days: 5
+ - semver-minor-days: 5
+ - semver-patch-days: 5
commit-message:
prefix: tools
open-pull-requests-limit: ${{secrets.OPEN_PR_LIMIT}}
@@ -35,3 +48,20 @@ updates:
applies-to: version-updates
patterns:
- '*'
+
+ - package-ecosystem: npm
+ directory: /tools/doc
+ schedule:
+ interval: weekly
+ cooldown:
+ - semver-major-days: 5
+ - semver-minor-days: 5
+ - semver-patch-days: 5
+ commit-message:
+ prefix: tools
+ open-pull-requests-limit: ${{secrets.OPEN_PR_LIMIT}}
+ groups:
+ doc:
+ applies-to: version-updates
+ patterns:
+ - '*'
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index b258df039221ac..8ef41b0a20cf33 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -103,7 +103,31 @@ jobs:
- name: Environment Information
run: npx envinfo
- name: Lint JavaScript files
- run: NODE=$(command -v node) make lint-js
+ run: |
+ set +e
+ NODE=$(command -v node) make lint-js
+ EXIT_CODE="$?"
+ if [ "$EXIT_CODE" != "0" ]; then
+ echo
+ echo 'ERROR: The JavaScript lint validation failed (the errors are logged above).'
+ echo ' Please fix the lint errors.'
+ if NODE=$(command -v node) make lint-js-fix > /dev/null 2>&1; then
+ echo ' Run:'
+ echo ' make lint-js-fix'
+ echo ' to fix the lint issues.'
+ git --no-pager diff
+ elif git diff --quiet --exit-code; then
+ echo ' None of the issue is auto-fixable, so manual fixes for'
+ echo ' all of the issues are required.'
+ else
+ echo ' Run:'
+ echo ' make lint-js-fix'
+ echo ' to fix the auto-fixable lint issues.'
+ echo ' Note that some manual fixes are also required.'
+ fi
+ echo
+ exit "$EXIT_CODE"
+ fi
- name: Get release version numbers
if: ${{ github.event.pull_request && github.event.pull_request.base.ref == github.event.pull_request.base.repo.default_branch }}
id: get-released-versions
diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml
index 302e6e29d04ca6..38157bfc00e2bb 100644
--- a/.github/workflows/tools.yml
+++ b/.github/workflows/tools.yml
@@ -21,7 +21,6 @@ on:
- c-ares
- cjs-module-lexer
- corepack
- - doc
- googletest
- gyp-next
- histogram
@@ -119,22 +118,6 @@ jobs:
run: |
make corepack-update
echo "NEW_VERSION=$(node deps/corepack/dist/corepack.js --version)" >> $GITHUB_ENV
- - id: doc
- subsystem: tools
- label: tools
- run: |
- cd tools/doc
- npm ci
- NEW_VERSION=$(npm outdated --parseable | cut -d: -f4 | xargs)
- if [ "$NEW_VERSION" != "" ]; then
- echo "NEW_VERSION=new version" >> $GITHUB_ENV
- rm -rf package-lock.json node_modules
- # Include $NEW_VERSION to explicitly update the package.json
- # entry for the dependency and also so that semver-major updates
- # are not skipped.
- npm install --ignore-scripts $NEW_VERSION
- npm install --ignore-scripts
- fi
- id: googletest
subsystem: deps
label: dependencies, test
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67aab9a199814b..e856c01f5e5d61 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,7 +37,8 @@ release.
+22.21.1
22.21.0
22.20.0
22.19.0
@@ -66,6 +67,48 @@
* [io.js](CHANGELOG_IOJS.md)
* [Archive](CHANGELOG_ARCHIVE.md)
+
+
+## 2025-10-28, Version 22.21.1 'Jod' (LTS), @aduh95
+
+### Commits
+
+* \[[`af33e8e668`](https://github.com/nodejs/node/commit/af33e8e668)] - **benchmark**: remove unused variable from util/priority-queue (Bruno Rodrigues) [#59872](https://github.com/nodejs/node/pull/59872)
+* \[[`6764ce8756`](https://github.com/nodejs/node/commit/6764ce8756)] - **benchmark**: update count to n in permission startup (Bruno Rodrigues) [#59872](https://github.com/nodejs/node/pull/59872)
+* \[[`4e8d99f0dc`](https://github.com/nodejs/node/commit/4e8d99f0dc)] - **benchmark**: update num to n in dgram offset-length (Bruno Rodrigues) [#59872](https://github.com/nodejs/node/pull/59872)
+* \[[`af0a8ba7f8`](https://github.com/nodejs/node/commit/af0a8ba7f8)] - **benchmark**: adjust dgram offset-length len values (Bruno Rodrigues) [#59708](https://github.com/nodejs/node/pull/59708)
+* \[[`78efd1be4a`](https://github.com/nodejs/node/commit/78efd1be4a)] - **benchmark**: update num to n in dgram offset-length (Bruno Rodrigues) [#59708](https://github.com/nodejs/node/pull/59708)
+* \[[`df72dc96e9`](https://github.com/nodejs/node/commit/df72dc96e9)] - **console,util**: improve array inspection performance (Ruben Bridgewater) [#60037](https://github.com/nodejs/node/pull/60037)
+* \[[`ef67d09f50`](https://github.com/nodejs/node/commit/ef67d09f50)] - **http**: improve writeEarlyHints by avoiding for-of loop (Haram Jeong) [#59958](https://github.com/nodejs/node/pull/59958)
+* \[[`23468fd76b`](https://github.com/nodejs/node/commit/23468fd76b)] - **http2**: fix allowHttp1+Upgrade, broken by shouldUpgradeCallback (Tim Perry) [#59924](https://github.com/nodejs/node/pull/59924)
+* \[[`56abc4ac76`](https://github.com/nodejs/node/commit/56abc4ac76)] - **lib**: optimize priority queue (Gürgün Dayıoğlu) [#60039](https://github.com/nodejs/node/pull/60039)
+* \[[`ea5cfd98c5`](https://github.com/nodejs/node/commit/ea5cfd98c5)] - **lib**: implement passive listener behavior per spec (BCD1me) [#59995](https://github.com/nodejs/node/pull/59995)
+* \[[`c2dd6eed2f`](https://github.com/nodejs/node/commit/c2dd6eed2f)] - **process**: fix wrong asyncContext under unhandled-rejections=strict (Shima Ryuhei) [#60103](https://github.com/nodejs/node/pull/60103)
+* \[[`81a3055710`](https://github.com/nodejs/node/commit/81a3055710)] - **process**: fix default `env` for `process.execve` (Richard Lau) [#60029](https://github.com/nodejs/node/pull/60029)
+* \[[`fe492c7ace`](https://github.com/nodejs/node/commit/fe492c7ace)] - **process**: fix hrtime fast call signatures (Renegade334) [#59600](https://github.com/nodejs/node/pull/59600)
+* \[[`76b4cab8fc`](https://github.com/nodejs/node/commit/76b4cab8fc)] - **src**: bring permissions macros in line with general C/C++ standards (Anna Henningsen) [#60053](https://github.com/nodejs/node/pull/60053)
+* \[[`21970970c7`](https://github.com/nodejs/node/commit/21970970c7)] - **src**: remove `AnalyzeTemporaryDtors` option from .clang-tidy (iknoom) [#60008](https://github.com/nodejs/node/pull/60008)
+* \[[`609c063e81`](https://github.com/nodejs/node/commit/609c063e81)] - **src**: remove unused variables from report (Moonki Choi) [#60047](https://github.com/nodejs/node/pull/60047)
+* \[[`987841a773`](https://github.com/nodejs/node/commit/987841a773)] - **src**: avoid unnecessary string allocations in SPrintF impl (Anna Henningsen) [#60052](https://github.com/nodejs/node/pull/60052)
+* \[[`6e386c0632`](https://github.com/nodejs/node/commit/6e386c0632)] - **src**: make ToLower/ToUpper input args more flexible (Anna Henningsen) [#60052](https://github.com/nodejs/node/pull/60052)
+* \[[`c3be1226c7`](https://github.com/nodejs/node/commit/c3be1226c7)] - **src**: allow `std::string_view` arguments to `SPrintF()` and friends (Anna Henningsen) [#60058](https://github.com/nodejs/node/pull/60058)
+* \[[`764d35647d`](https://github.com/nodejs/node/commit/764d35647d)] - **src**: remove unnecessary `std::string` error messages (Anna Henningsen) [#60057](https://github.com/nodejs/node/pull/60057)
+* \[[`1289ef89ec`](https://github.com/nodejs/node/commit/1289ef89ec)] - **src**: remove unnecessary shadowed functions on Utf8Value & BufferValue (Anna Henningsen) [#60056](https://github.com/nodejs/node/pull/60056)
+* \[[`d1fb8a538d`](https://github.com/nodejs/node/commit/d1fb8a538d)] - **src**: avoid unnecessary string -> `char*` -> string round trips (Anna Henningsen) [#60055](https://github.com/nodejs/node/pull/60055)
+* \[[`54b439fb5a`](https://github.com/nodejs/node/commit/54b439fb5a)] - **src**: fill `options_args`, `options_env` after vectors are finalized (iknoom) [#59945](https://github.com/nodejs/node/pull/59945)
+* \[[`c7c597e2ca`](https://github.com/nodejs/node/commit/c7c597e2ca)] - **src**: use RAII for uv\_process\_options\_t (iknoom) [#59945](https://github.com/nodejs/node/pull/59945)
+* \[[`b928ea9716`](https://github.com/nodejs/node/commit/b928ea9716)] - **test**: ensure that the message event is fired (Luigi Pinca) [#59952](https://github.com/nodejs/node/pull/59952)
+* \[[`e4b95a5158`](https://github.com/nodejs/node/commit/e4b95a5158)] - **test**: replace diagnostics\_channel stackframe in output snapshots (Chengzhong Wu) [#60024](https://github.com/nodejs/node/pull/60024)
+* \[[`4206406694`](https://github.com/nodejs/node/commit/4206406694)] - **test**: mark test-web-locks skip on IBM i (SRAVANI GUNDEPALLI) [#59996](https://github.com/nodejs/node/pull/59996)
+* \[[`26394cd5bf`](https://github.com/nodejs/node/commit/26394cd5bf)] - **test**: expand tls-check-server-identity coverage (Diango Gavidia) [#60002](https://github.com/nodejs/node/pull/60002)
+* \[[`b58df47995`](https://github.com/nodejs/node/commit/b58df47995)] - **test**: fix typo of test-benchmark-readline.js (Deokjin Kim) [#59993](https://github.com/nodejs/node/pull/59993)
+* \[[`af3a59dba8`](https://github.com/nodejs/node/commit/af3a59dba8)] - **test**: verify tracing channel doesn't swallow unhandledRejection (Gerhard Stöbich) [#59974](https://github.com/nodejs/node/pull/59974)
+* \[[`cee362242b`](https://github.com/nodejs/node/commit/cee362242b)] - **timers**: fix binding fast call signatures (Renegade334) [#59600](https://github.com/nodejs/node/pull/59600)
+* \[[`40fea57fdd`](https://github.com/nodejs/node/commit/40fea57fdd)] - **tools**: add message on auto-fixing js lint issues in gh workflow (Dario Piotrowicz) [#59128](https://github.com/nodejs/node/pull/59128)
+* \[[`aac90d351b`](https://github.com/nodejs/node/commit/aac90d351b)] - **tools**: verify signatures when updating nghttp\* (Antoine du Hamel) [#60113](https://github.com/nodejs/node/pull/60113)
+* \[[`9fae03c7d9`](https://github.com/nodejs/node/commit/9fae03c7d9)] - **tools**: use dependabot cooldown and move tools/doc (Rafael Gonzaga) [#59978](https://github.com/nodejs/node/pull/59978)
+* \[[`81548abdf6`](https://github.com/nodejs/node/commit/81548abdf6)] - **wasi**: fix WasiFunction fast call signature (Renegade334) [#59600](https://github.com/nodejs/node/pull/59600)
+
## 2025-10-20, Version 22.21.0 'Jod' (LTS), @aduh95
diff --git a/lib/_http_server.js b/lib/_http_server.js
index 8cae06cfde9fa0..4ff976d02ca441 100644
--- a/lib/_http_server.js
+++ b/lib/_http_server.js
@@ -330,7 +330,9 @@ ServerResponse.prototype.writeEarlyHints = function writeEarlyHints(hints, cb) {
head += 'Link: ' + link + '\r\n';
- for (const key of ObjectKeys(hints)) {
+ const keys = ObjectKeys(hints);
+ for (let i = 0; i < keys.length; i++) {
+ const key = keys[i];
if (key !== 'link') {
head += key + ': ' + hints[key] + '\r\n';
}
diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js
index e6c24e3a058ca7..2f02510e3a3176 100644
--- a/lib/internal/event_target.js
+++ b/lib/internal/event_target.js
@@ -76,6 +76,7 @@ const { now } = require('internal/perf/utils');
const kType = Symbol('type');
const kDetail = Symbol('detail');
+const kInPassiveListener = Symbol('kInPassiveListener');
const isTrustedSet = new SafeWeakSet();
const isTrusted = ObjectGetOwnPropertyDescriptor({
@@ -127,6 +128,7 @@ class Event {
this[kTarget] = null;
this[kIsBeingDispatched] = false;
+ this[kInPassiveListener] = false;
}
/**
@@ -178,6 +180,7 @@ class Event {
preventDefault() {
if (!isEvent(this))
throw new ERR_INVALID_THIS('Event');
+ if (!this.#cancelable || this[kInPassiveListener]) return;
this.#defaultPrevented = true;
}
@@ -266,6 +269,19 @@ class Event {
return !this.#cancelable || !this.#defaultPrevented;
}
+ /**
+ * @type {boolean}
+ */
+ set returnValue(value) {
+ if (!isEvent(this))
+ throw new ERR_INVALID_THIS('Event');
+
+ if (!value) {
+ if (!this.#cancelable || this[kInPassiveListener]) return;
+ this.#defaultPrevented = true;
+ }
+ }
+
/**
* @type {boolean}
*/
@@ -760,7 +776,6 @@ class EventTarget {
throw new ERR_EVENT_RECURSION(event.type);
this[kHybridDispatch](event, event.type, event);
-
return event.defaultPrevented !== true;
}
@@ -813,8 +828,8 @@ class EventTarget {
this[kRemoveListener](root.size, type, listener, capture);
}
+ let arg;
try {
- let arg;
if (handler.isNodeStyleListener) {
arg = nodeValue;
} else {
@@ -824,6 +839,9 @@ class EventTarget {
handler.callback.deref() : handler.callback;
let result;
if (callback) {
+ if (handler.passive && !handler.isNodeStyleListener) {
+ arg[kInPassiveListener] = true;
+ }
result = FunctionPrototypeCall(callback, this, arg);
if (!handler.isNodeStyleListener) {
arg[kIsBeingDispatched] = false;
@@ -833,6 +851,9 @@ class EventTarget {
addCatch(result);
} catch (err) {
emitUncaughtException(err);
+ } finally {
+ if (arg?.[kInPassiveListener])
+ arg[kInPassiveListener] = false;
}
handler = next;
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index 73a478a7b708e2..335113acc8c23d 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -3379,6 +3379,9 @@ class Http2SecureServer extends TLSServer {
this.headersTimeout = 60_000; // Minimum between 60 seconds or requestTimeout
this.requestTimeout = 300_000; // 5 minutes
this.connectionsCheckingInterval = 30_000; // 30 seconds
+ this.shouldUpgradeCallback = function() {
+ return this.listenerCount('upgrade') > 0;
+ };
this.on('listening', setupConnectionsTracking);
}
if (typeof requestListener === 'function')
diff --git a/lib/internal/priority_queue.js b/lib/internal/priority_queue.js
index c172e2351c93ab..e6fdb61e9997c9 100644
--- a/lib/internal/priority_queue.js
+++ b/lib/internal/priority_queue.js
@@ -1,9 +1,5 @@
'use strict';
-const {
- Array,
-} = primordials;
-
// The PriorityQueue is a basic implementation of a binary heap that accepts
// a custom sorting function via its constructor. This function is passed
// the two nodes to compare, similar to the native Array#sort. Crucially
@@ -12,7 +8,7 @@ const {
module.exports = class PriorityQueue {
#compare = (a, b) => a - b;
- #heap = new Array(64);
+ #heap = [undefined, undefined];
#setPosition;
#size = 0;
@@ -28,9 +24,6 @@ module.exports = class PriorityQueue {
const pos = ++this.#size;
heap[pos] = value;
- if (heap.length === pos)
- heap.length *= 2;
-
this.percolateUp(pos);
}
@@ -45,6 +38,7 @@ module.exports = class PriorityQueue {
percolateDown(pos) {
const compare = this.#compare;
const setPosition = this.#setPosition;
+ const hasSetPosition = setPosition !== undefined;
const heap = this.#heap;
const size = this.#size;
const hsize = size >> 1;
@@ -62,7 +56,7 @@ module.exports = class PriorityQueue {
if (compare(item, childItem) <= 0) break;
- if (setPosition !== undefined)
+ if (hasSetPosition)
setPosition(childItem, pos);
heap[pos] = childItem;
@@ -70,7 +64,7 @@ module.exports = class PriorityQueue {
}
heap[pos] = item;
- if (setPosition !== undefined)
+ if (hasSetPosition)
setPosition(item, pos);
}
@@ -78,6 +72,7 @@ module.exports = class PriorityQueue {
const heap = this.#heap;
const compare = this.#compare;
const setPosition = this.#setPosition;
+ const hasSetPosition = setPosition !== undefined;
const item = heap[pos];
while (pos > 1) {
@@ -86,13 +81,13 @@ module.exports = class PriorityQueue {
if (compare(parentItem, item) <= 0)
break;
heap[pos] = parentItem;
- if (setPosition !== undefined)
+ if (hasSetPosition)
setPosition(parentItem, pos);
pos = parent;
}
heap[pos] = item;
- if (setPosition !== undefined)
+ if (hasSetPosition)
setPosition(item, pos);
}
diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js
index 092464c9ffd440..78805a6b5b87bb 100644
--- a/lib/internal/process/per_thread.js
+++ b/lib/internal/process/per_thread.js
@@ -283,7 +283,7 @@ function wrapProcessMethods(binding) {
return true;
}
- function execve(execPath, args = [], env) {
+ function execve(execPath, args = [], env = process.env) {
emitExperimentalWarning('process.execve');
const { isMainThread } = require('internal/worker');
@@ -305,22 +305,20 @@ function wrapProcessMethods(binding) {
}
const envArray = [];
- if (env !== undefined) {
- validateObject(env, 'env');
-
- for (const { 0: key, 1: value } of ObjectEntries(env)) {
- if (
- typeof key !== 'string' ||
- typeof value !== 'string' ||
- StringPrototypeIncludes(key, '\u0000') ||
- StringPrototypeIncludes(value, '\u0000')
- ) {
- throw new ERR_INVALID_ARG_VALUE(
- 'env', env, 'must be an object with string keys and values without null bytes',
- );
- } else {
- ArrayPrototypePush(envArray, `${key}=${value}`);
- }
+ validateObject(env, 'env');
+
+ for (const { 0: key, 1: value } of ObjectEntries(env)) {
+ if (
+ typeof key !== 'string' ||
+ typeof value !== 'string' ||
+ StringPrototypeIncludes(key, '\u0000') ||
+ StringPrototypeIncludes(value, '\u0000')
+ ) {
+ throw new ERR_INVALID_ARG_VALUE(
+ 'env', env, 'must be an object with string keys and values without null bytes',
+ );
+ } else {
+ ArrayPrototypePush(envArray, `${key}=${value}`);
}
}
diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js
index 90eaef5c773718..db5cb8a9cf362b 100644
--- a/lib/internal/process/promises.js
+++ b/lib/internal/process/promises.js
@@ -343,7 +343,7 @@ function strictUnhandledRejectionsMode(promise, promiseInfo, promiseAsyncId) {
reason : new UnhandledPromiseRejection(reason);
// This destroys the async stack, don't clear it after
triggerUncaughtException(err, true /* fromPromise */);
- if (promiseAsyncId === undefined) {
+ if (promiseAsyncId !== undefined) {
pushAsyncContext(
promise[kAsyncIdSymbol],
promise[kTriggerAsyncIdSymbol],
diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js
index e81d13e5c787c4..f76e61756175da 100644
--- a/lib/internal/util/inspect.js
+++ b/lib/internal/util/inspect.js
@@ -2022,11 +2022,12 @@ function formatArray(ctx, value, recurseTimes) {
const remaining = valLen - len;
const output = [];
for (let i = 0; i < len; i++) {
- // Special handle sparse arrays.
- if (!ObjectPrototypeHasOwnProperty(value, i)) {
+ const desc = ObjectGetOwnPropertyDescriptor(value, i);
+ if (desc === undefined) {
+ // Special handle sparse arrays.
return formatSpecialArray(ctx, value, recurseTimes, len, output, i);
}
- ArrayPrototypePush(output, formatProperty(ctx, value, recurseTimes, i, kArrayType));
+ ArrayPrototypePush(output, formatProperty(ctx, value, recurseTimes, i, kArrayType, desc));
}
if (remaining > 0) {
ArrayPrototypePush(output, remainingText(remaining));
diff --git a/src/.clang-tidy b/src/.clang-tidy
index 9cb5edbe5bee8a..91aabe9a158575 100644
--- a/src/.clang-tidy
+++ b/src/.clang-tidy
@@ -22,7 +22,6 @@ Checks: '-*,
readability-delete-null-pointer, '
WarningsAsErrors: ''
HeaderFilterRegex: ''
-AnalyzeTemporaryDtors: false
FormatStyle: none
User: nodejs/cpp
CheckOptions:
diff --git a/src/debug_utils-inl.h b/src/debug_utils-inl.h
index 3736663d9977c0..c10af8b1b618bc 100644
--- a/src/debug_utils-inl.h
+++ b/src/debug_utils-inl.h
@@ -11,25 +11,41 @@
namespace node {
+template
+concept StringViewConvertible = requires(T a) {
+ {
+ a.ToStringView()
+ } -> std::convertible_to;
+ };
+template
+concept StringConvertible = requires(T a) {
+ {
+ a.ToString()
+ } -> std::convertible_to;
+ };
+
struct ToStringHelper {
template
- static std::string Convert(
- const T& value,
- std::string(T::* to_string)() const = &T::ToString) {
- return (value.*to_string)();
+ requires(StringConvertible) && (!StringViewConvertible)
+ static std::string Convert(const T& value) {
+ return value.ToString();
}
+ template
+ requires StringViewConvertible
+ static std::string_view Convert(const T& value) {
+ return value.ToStringView();
+ }
+
template ::value, bool>::type,
typename dummy = bool>
static std::string Convert(const T& value) { return std::to_string(value); }
- static std::string Convert(const char* value) {
+ static std::string_view Convert(const char* value) {
return value != nullptr ? value : "(null)";
}
static std::string Convert(const std::string& value) { return value; }
- static std::string Convert(std::string_view value) {
- return std::string(value);
- }
+ static std::string_view Convert(std::string_view value) { return value; }
static std::string Convert(bool value) { return value ? "true" : "false"; }
template >>
- static std::string BaseConvert(T& value) { // NOLINT(runtime/references)
+ static auto BaseConvert(T&& value) {
return Convert(std::forward(value));
}
};
template
-std::string ToString(const T& value) {
+auto ToStringOrStringView(const T& value) {
return ToStringHelper::Convert(value);
}
+template
+std::string ToString(const T& value) {
+ return std::string(ToStringOrStringView(value));
+}
+
template
-std::string ToBaseString(const T& value) {
+auto ToBaseString(const T& value) {
return ToStringHelper::BaseConvert(value);
}
-inline std::string SPrintFImpl(const char* format) {
- const char* p = strchr(format, '%');
- if (p == nullptr) [[unlikely]]
- return format;
- CHECK_EQ(p[1], '%'); // Only '%%' allowed when there are no arguments.
+inline std::string SPrintFImpl(std::string_view format) {
+ auto offset = format.find('%');
+ if (offset == std::string_view::npos) return std::string(format);
+ CHECK_LT(offset + 1, format.size());
+ CHECK_EQ(format[offset + 1],
+ '%'); // Only '%%' allowed when there are no arguments.
- return std::string(format, p + 1) + SPrintFImpl(p + 2);
+ return std::string(format.substr(0, offset + 1)) +
+ SPrintFImpl(format.substr(offset + 2));
}
template
std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string)
- const char* format, Arg&& arg, Args&&... args) {
- const char* p = strchr(format, '%');
- CHECK_NOT_NULL(p); // If you hit this, you passed in too many arguments.
- std::string ret(format, p);
+ std::string_view format,
+ Arg&& arg,
+ Args&&... args) {
+ auto offset = format.find('%');
+ CHECK_NE(offset, std::string_view::npos); // If you hit this, you passed in
+ // too many arguments.
+ std::string ret(format.substr(0, offset));
// Ignore long / size_t modifiers
- while (strchr("lz", *++p) != nullptr) {}
- switch (*p) {
+ while (++offset < format.size() &&
+ (format[offset] == 'l' || format[offset] == 'z')) {
+ }
+ switch (offset == format.size() ? '\0' : format[offset]) {
case '%': {
- return ret + '%' + SPrintFImpl(p + 1,
- std::forward(arg),
- std::forward(args)...);
+ return ret + '%' +
+ SPrintFImpl(format.substr(offset + 1),
+ std::forward(arg),
+ std::forward(args)...);
}
default: {
- return ret + '%' + SPrintFImpl(p,
- std::forward(arg),
- std::forward(args)...);
+ return ret + '%' +
+ SPrintFImpl(format.substr(offset),
+ std::forward(arg),
+ std::forward(args)...);
}
case 'd':
case 'i':
case 'u':
case 's':
- ret += ToString(arg);
+ ret += ToStringOrStringView(arg);
break;
case 'o':
ret += ToBaseString<3>(arg);
@@ -120,17 +150,21 @@ std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string)
break;
}
}
- return ret + SPrintFImpl(p + 1, std::forward(args)...);
+ return ret +
+ SPrintFImpl(format.substr(offset + 1), std::forward(args)...);
}
template
std::string COLD_NOINLINE SPrintF( // NOLINT(runtime/string)
- const char* format, Args&&... args) {
+ std::string_view format,
+ Args&&... args) {
return SPrintFImpl(format, std::forward(args)...);
}
template
-void COLD_NOINLINE FPrintF(FILE* file, const char* format, Args&&... args) {
+void COLD_NOINLINE FPrintF(FILE* file,
+ std::string_view format,
+ Args&&... args) {
FWrite(file, SPrintF(format, std::forward(args)...));
}
diff --git a/src/debug_utils.h b/src/debug_utils.h
index e1768d8a06159c..8f6165e1b5faf4 100644
--- a/src/debug_utils.h
+++ b/src/debug_utils.h
@@ -26,6 +26,8 @@ class Environment;
template
inline std::string ToString(const T& value);
+template
+inline auto ToStringOrStringView(const T& value);
// C++-style variant of sprintf()/fprintf() that:
// - Returns an std::string
@@ -33,9 +35,9 @@ inline std::string ToString(const T& value);
// - Supports %p and %s. %d, %i and %u are aliases for %s.
// - Accepts any class that has a ToString() method for stringification.
template
-inline std::string SPrintF(const char* format, Args&&... args);
+inline std::string SPrintF(std::string_view format, Args&&... args);
template
-inline void FPrintF(FILE* file, const char* format, Args&&... args);
+inline void FPrintF(FILE* file, std::string_view format, Args&&... args);
void NODE_EXTERN_PRIVATE FWrite(FILE* file, const std::string& str);
// Listing the AsyncWrap provider types first enables us to cast directly
diff --git a/src/inspector/protocol_helper.h b/src/inspector/protocol_helper.h
index a0f88aa93532a4..5529ef523a0101 100644
--- a/src/inspector/protocol_helper.h
+++ b/src/inspector/protocol_helper.h
@@ -19,7 +19,7 @@ inline std::unique_ptr ToInspectorString(
inline protocol::String ToProtocolString(v8::Isolate* isolate,
v8::Local value) {
Utf8Value buffer(isolate, value);
- return *buffer;
+ return buffer.ToString();
}
} // namespace node::inspector
diff --git a/src/node_blob.cc b/src/node_blob.cc
index eb0fbebc9df152..bb019dc62c1000 100644
--- a/src/node_blob.cc
+++ b/src/node_blob.cc
@@ -439,11 +439,9 @@ void Blob::StoreDataObject(const FunctionCallbackInfo& args) {
Utf8Value type(isolate, args[3]);
binding_data->store_data_object(
- std::string(*key, key.length()),
+ key.ToString(),
BlobBindingData::StoredDataObject(
- BaseObjectPtr(blob),
- length,
- std::string(*type, type.length())));
+ BaseObjectPtr(blob), length, type.ToString()));
}
// Note: applying the V8 Fast API to the following function does not produce
@@ -483,7 +481,7 @@ void Blob::GetDataObject(const FunctionCallbackInfo& args) {
Utf8Value key(isolate, args[0]);
BlobBindingData::StoredDataObject stored =
- binding_data->get_data_object(std::string(*key, key.length()));
+ binding_data->get_data_object(key.ToString());
if (stored.blob) {
Local type;
if (!String::NewFromUtf8(isolate,
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
index 21a08a738e5c35..d52388717e5938 100644
--- a/src/node_contextify.cc
+++ b/src/node_contextify.cc
@@ -292,10 +292,10 @@ BaseObjectPtr ContextifyContext::New(
options->allow_code_gen_wasm);
Utf8Value name_val(env->isolate(), options->name);
- ContextInfo info(*name_val);
+ ContextInfo info(name_val.ToString());
if (!options->origin.IsEmpty()) {
Utf8Value origin_val(env->isolate(), options->origin);
- info.origin = *origin_val;
+ info.origin = origin_val.ToString();
}
BaseObjectPtr result;
diff --git a/src/node_errors.h b/src/node_errors.h
index 791e835c2c2851..9d0488d0dba657 100644
--- a/src/node_errors.h
+++ b/src/node_errors.h
@@ -134,7 +134,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
#define V(code, type) \
template \
inline v8::Local code( \
- v8::Isolate* isolate, const char* format, Args&&... args) { \
+ v8::Isolate* isolate, std::string_view format, Args&&... args) { \
std::string message; \
if (sizeof...(Args) == 0) { \
message = format; \
@@ -159,17 +159,18 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
} \
template \
inline void THROW_##code( \
- v8::Isolate* isolate, const char* format, Args&&... args) { \
+ v8::Isolate* isolate, std::string_view format, Args&&... args) { \
isolate->ThrowException( \
code(isolate, format, std::forward(args)...)); \
} \
template \
inline void THROW_##code( \
- Environment* env, const char* format, Args&&... args) { \
+ Environment* env, std::string_view format, Args&&... args) { \
THROW_##code(env->isolate(), format, std::forward(args)...); \
} \
template \
- inline void THROW_##code(Realm* realm, const char* format, Args&&... args) { \
+ inline void THROW_##code( \
+ Realm* realm, std::string_view format, Args&&... args) { \
THROW_##code(realm->isolate(), format, std::forward(args)...); \
}
ERRORS_WITH_CODE(V)
@@ -250,10 +251,8 @@ PREDEFINED_ERROR_MESSAGES(V)
// Errors with predefined non-static messages
inline void THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(Environment* env,
int64_t timeout) {
- std::ostringstream message;
- message << "Script execution timed out after ";
- message << timeout << "ms";
- THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, message.str().c_str());
+ THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(
+ env, "Script execution timed out after %dms", timeout);
}
inline void THROW_ERR_REQUIRE_ASYNC_MODULE(
@@ -268,14 +267,14 @@ inline void THROW_ERR_REQUIRE_ASYNC_MODULE(
if (!parent_filename.IsEmpty() && parent_filename->IsString()) {
Utf8Value utf8(env->isolate(), parent_filename);
message += "\n From ";
- message += utf8.out();
+ message += utf8.ToStringView();
}
if (!filename.IsEmpty() && filename->IsString()) {
Utf8Value utf8(env->isolate(), filename);
message += "\n Requiring ";
- message += +utf8.out();
+ message += utf8.ToStringView();
}
- THROW_ERR_REQUIRE_ASYNC_MODULE(env, message.c_str());
+ THROW_ERR_REQUIRE_ASYNC_MODULE(env, message);
}
inline v8::Local ERR_BUFFER_TOO_LARGE(v8::Isolate* isolate) {
diff --git a/src/node_file.cc b/src/node_file.cc
index 8e4847cbf89878..77f8f1bd4e8294 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -3147,24 +3147,25 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) {
if (!error_code) {
// Check if src and dest are identical.
if (std::filesystem::equivalent(src_path, dest_path)) {
- std::string message = "src and dest cannot be the same %s";
- return THROW_ERR_FS_CP_EINVAL(env, message.c_str(), dest_path_str);
+ static constexpr const char* message =
+ "src and dest cannot be the same %s";
+ return THROW_ERR_FS_CP_EINVAL(env, message, dest_path_str);
}
const bool dest_is_dir =
dest_status.type() == std::filesystem::file_type::directory;
if (src_is_dir && !dest_is_dir) {
- std::string message =
+ static constexpr const char* message =
"Cannot overwrite non-directory %s with directory %s";
return THROW_ERR_FS_CP_DIR_TO_NON_DIR(
- env, message.c_str(), dest_path_str, src_path_str);
+ env, message, dest_path_str, src_path_str);
}
if (!src_is_dir && dest_is_dir) {
- std::string message =
+ static constexpr const char* message =
"Cannot overwrite directory %s with non-directory %s";
return THROW_ERR_FS_CP_NON_DIR_TO_DIR(
- env, message.c_str(), dest_path_str, src_path_str);
+ env, message, dest_path_str, src_path_str);
}
}
@@ -3173,9 +3174,9 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) {
}
// Check if dest_path is a subdirectory of src_path.
if (src_is_dir && dest_path_str.starts_with(src_path_str)) {
- std::string message = "Cannot copy %s to a subdirectory of self %s";
- return THROW_ERR_FS_CP_EINVAL(
- env, message.c_str(), src_path_str, dest_path_str);
+ static constexpr const char* message =
+ "Cannot copy %s to a subdirectory of self %s";
+ return THROW_ERR_FS_CP_EINVAL(env, message, src_path_str, dest_path_str);
}
auto dest_parent = dest_path.parent_path();
@@ -3186,9 +3187,9 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) {
dest_parent.parent_path() != dest_parent) {
if (std::filesystem::equivalent(
src_path, dest_path.parent_path(), error_code)) {
- std::string message = "Cannot copy %s to a subdirectory of self %s";
- return THROW_ERR_FS_CP_EINVAL(
- env, message.c_str(), src_path_str, dest_path_str);
+ static constexpr const char* message =
+ "Cannot copy %s to a subdirectory of self %s";
+ return THROW_ERR_FS_CP_EINVAL(env, message, src_path_str, dest_path_str);
}
// If equivalent fails, it's highly likely that dest_parent does not exist
@@ -3200,23 +3201,24 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) {
}
if (src_is_dir && !recursive) {
- std::string message =
+ static constexpr const char* message =
"Recursive option not enabled, cannot copy a directory: %s";
- return THROW_ERR_FS_EISDIR(env, message.c_str(), src_path_str);
+ return THROW_ERR_FS_EISDIR(env, message, src_path_str);
}
switch (src_status.type()) {
case std::filesystem::file_type::socket: {
- std::string message = "Cannot copy a socket file: %s";
- return THROW_ERR_FS_CP_SOCKET(env, message.c_str(), dest_path_str);
+ static constexpr const char* message = "Cannot copy a socket file: %s";
+ return THROW_ERR_FS_CP_SOCKET(env, message, dest_path_str);
}
case std::filesystem::file_type::fifo: {
- std::string message = "Cannot copy a FIFO pipe: %s";
- return THROW_ERR_FS_CP_FIFO_PIPE(env, message.c_str(), dest_path_str);
+ static constexpr const char* message = "Cannot copy a FIFO pipe: %s";
+ return THROW_ERR_FS_CP_FIFO_PIPE(env, message, dest_path_str);
}
case std::filesystem::file_type::unknown: {
- std::string message = "Cannot copy an unknown file type: %s";
- return THROW_ERR_FS_CP_UNKNOWN(env, message.c_str(), dest_path_str);
+ static constexpr const char* message =
+ "Cannot copy an unknown file type: %s";
+ return THROW_ERR_FS_CP_UNKNOWN(env, message, dest_path_str);
}
default:
break;
diff --git a/src/node_messaging.cc b/src/node_messaging.cc
index 1eff9399ff8751..3c5f38ba4f4927 100644
--- a/src/node_messaging.cc
+++ b/src/node_messaging.cc
@@ -1357,7 +1357,7 @@ std::unique_ptr JSTransferable::TransferOrClone() const {
}
Utf8Value deserialize_info_str(env()->isolate(), deserialize_info);
if (*deserialize_info_str == nullptr) return {};
- return std::make_unique(*deserialize_info_str,
+ return std::make_unique(deserialize_info_str.ToString(),
Global(env()->isolate(), data));
}
diff --git a/src/node_process.h b/src/node_process.h
index b88a2f99483ad2..64393302d2cfd9 100644
--- a/src/node_process.h
+++ b/src/node_process.h
@@ -3,6 +3,7 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+#include "node_debug.h"
#include "node_snapshotable.h"
#include "v8-fast-api-calls.h"
#include "v8.h"
@@ -72,23 +73,23 @@ class BindingData : public SnapshotableObject {
SET_SELF_SIZE(BindingData)
static BindingData* FromV8Value(v8::Local receiver);
- static void NumberImpl(BindingData* receiver);
+ static void HrtimeImpl(BindingData* receiver);
- static void FastNumber(v8::Local unused,
- v8::Local receiver) {
- NumberImpl(FromV8Value(receiver));
+ static void FastHrtime(v8::Local receiver) {
+ TRACK_V8_FAST_API_CALL("process.hrtime");
+ HrtimeImpl(FromV8Value(receiver));
}
- static void SlowNumber(const v8::FunctionCallbackInfo& args);
+ static void SlowHrtime(const v8::FunctionCallbackInfo& args);
- static void BigIntImpl(BindingData* receiver);
+ static void HrtimeBigIntImpl(BindingData* receiver);
- static void FastBigInt(v8::Local unused,
- v8::Local receiver) {
- BigIntImpl(FromV8Value(receiver));
+ static void FastHrtimeBigInt(v8::Local receiver) {
+ TRACK_V8_FAST_API_CALL("process.hrtimeBigInt");
+ HrtimeBigIntImpl(FromV8Value(receiver));
}
- static void SlowBigInt(const v8::FunctionCallbackInfo& args);
+ static void SlowHrtimeBigInt(const v8::FunctionCallbackInfo& args);
static void LoadEnvFile(const v8::FunctionCallbackInfo& args);
@@ -101,8 +102,8 @@ class BindingData : public SnapshotableObject {
// These need to be static so that we have their addresses available to
// register as external references in the snapshot at environment creation
// time.
- static v8::CFunction fast_number_;
- static v8::CFunction fast_bigint_;
+ static v8::CFunction fast_hrtime_;
+ static v8::CFunction fast_hrtime_bigint_;
};
} // namespace process
diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc
index 1cb08b715865f8..9dcca7509f37f2 100644
--- a/src/node_process_methods.cc
+++ b/src/node_process_methods.cc
@@ -651,22 +651,22 @@ BindingData::BindingData(Realm* realm,
hrtime_buffer_.MakeWeak();
}
-v8::CFunction BindingData::fast_number_(v8::CFunction::Make(FastNumber));
-v8::CFunction BindingData::fast_bigint_(v8::CFunction::Make(FastBigInt));
+CFunction BindingData::fast_hrtime_(CFunction::Make(FastHrtime));
+CFunction BindingData::fast_hrtime_bigint_(CFunction::Make(FastHrtimeBigInt));
void BindingData::AddMethods(Isolate* isolate, Local target) {
SetFastMethodNoSideEffect(
- isolate, target, "hrtime", SlowNumber, &fast_number_);
+ isolate, target, "hrtime", SlowHrtime, &fast_hrtime_);
SetFastMethodNoSideEffect(
- isolate, target, "hrtimeBigInt", SlowBigInt, &fast_bigint_);
+ isolate, target, "hrtimeBigInt", SlowHrtimeBigInt, &fast_hrtime_bigint_);
}
void BindingData::RegisterExternalReferences(
ExternalReferenceRegistry* registry) {
- registry->Register(SlowNumber);
- registry->Register(SlowBigInt);
- registry->Register(fast_number_);
- registry->Register(fast_bigint_);
+ registry->Register(SlowHrtime);
+ registry->Register(SlowHrtimeBigInt);
+ registry->Register(fast_hrtime_);
+ registry->Register(fast_hrtime_bigint_);
}
BindingData* BindingData::FromV8Value(Local value) {
@@ -688,14 +688,14 @@ void BindingData::MemoryInfo(MemoryTracker* tracker) const {
// broken into the upper/lower 32 bits to be converted back in JS,
// because there is no Uint64Array in JS.
// The third entry contains the remaining nanosecond part of the value.
-void BindingData::NumberImpl(BindingData* receiver) {
+void BindingData::HrtimeImpl(BindingData* receiver) {
uint64_t t = uv_hrtime();
receiver->hrtime_buffer_[0] = (t / NANOS_PER_SEC) >> 32;
receiver->hrtime_buffer_[1] = (t / NANOS_PER_SEC) & 0xffffffff;
receiver->hrtime_buffer_[2] = t % NANOS_PER_SEC;
}
-void BindingData::BigIntImpl(BindingData* receiver) {
+void BindingData::HrtimeBigIntImpl(BindingData* receiver) {
uint64_t t = uv_hrtime();
// The buffer is a Uint32Array, so we need to reinterpret it as a
// Uint64Array to write the value. The buffer is valid at this scope so we
@@ -705,12 +705,12 @@ void BindingData::BigIntImpl(BindingData* receiver) {
fields[0] = t;
}
-void BindingData::SlowBigInt(const FunctionCallbackInfo& args) {
- BigIntImpl(FromJSObject(args.This()));
+void BindingData::SlowHrtimeBigInt(const FunctionCallbackInfo& args) {
+ HrtimeBigIntImpl(FromJSObject(args.This()));
}
-void BindingData::SlowNumber(const v8::FunctionCallbackInfo& args) {
- NumberImpl(FromJSObject(args.This()));
+void BindingData::SlowHrtime(const FunctionCallbackInfo& args) {
+ HrtimeImpl(FromJSObject(args.This()));
}
bool BindingData::PrepareForSerialization(Local context,
diff --git a/src/node_report.cc b/src/node_report.cc
index df73a8204bc091..8ff711f12e19f7 100644
--- a/src/node_report.cc
+++ b/src/node_report.cc
@@ -752,7 +752,6 @@ static void PrintSystemInformation(JSONWriter* writer) {
writer->json_objectstart("userLimits");
struct rlimit limit;
- std::string soft, hard;
for (size_t i = 0; i < arraysize(rlimit_strings); i++) {
if (getrlimit(rlimit_strings[i].id, &limit) == 0) {
@@ -788,8 +787,6 @@ static void PrintLoadedLibraries(JSONWriter* writer) {
// Obtain and report the node and subcomponent version strings.
static void PrintComponentVersions(JSONWriter* writer) {
- std::stringstream buf;
-
writer->json_objectstart("componentVersions");
for (const auto& version : per_process::metadata.versions.pairs()) {
@@ -857,7 +854,7 @@ std::string TriggerNodeReport(Isolate* isolate,
THROW_IF_INSUFFICIENT_PERMISSIONS(
env,
permission::PermissionScope::kFileSystemWrite,
- std::string_view(Environment::GetCwd(env->exec_path())),
+ Environment::GetCwd(env->exec_path()),
filename);
}
}
diff --git a/src/node_version.h b/src/node_version.h
index 73e57c69861765..ba16ca3abcf821 100644
--- a/src/node_version.h
+++ b/src/node_version.h
@@ -29,7 +29,7 @@
#define NODE_VERSION_IS_LTS 1
#define NODE_VERSION_LTS_CODENAME "Jod"
-#define NODE_VERSION_IS_RELEASE 0
+#define NODE_VERSION_IS_RELEASE 1
#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
diff --git a/src/node_wasi.cc b/src/node_wasi.cc
index 090866960beb8f..85e549e4592e1d 100644
--- a/src/node_wasi.cc
+++ b/src/node_wasi.cc
@@ -265,7 +265,6 @@ inline void EinvalError() {}
template
R WASI::WasiFunction::FastCallback(
- Local |