From b5d5dd74a1f31ba9e15ca0978395aa069bae85ef Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 4 Aug 2026 10:17:50 +0200 Subject: [PATCH 001/152] Revert "http2: avoid copying the options in respond()" This reverts commit 33296477628f34857471d3cf8aa20eb5fbe0afbb. PR-URL: https://github.com/nodejs/node/pull/64663 Reviewed-By: Antoine du Hamel Reviewed-By: Tim Perry Reviewed-By: Ethan Arrowood --- lib/internal/http2/core.js | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 8042ae2b6782..9e4e228568d5 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -2654,35 +2654,31 @@ function prepareResponseHeaders(stream, headersParam, options) { function prepareResponseHeadersObject(oldHeaders, options) { assertIsObject(oldHeaders, 'headers', ['Object', 'Array']); const headers = { __proto__: null }; - let statusCode; - let hasDate = false; if (oldHeaders !== null && oldHeaders !== undefined) { // This loop is here for performance reason. Do not change. - // The :status and date fields are picked up while copying so they do - // not have to be looked up again on the null-prototype copy. for (const key in oldHeaders) { if (ObjectHasOwn(oldHeaders, key)) { - const value = oldHeaders[key]; - headers[key] = value; - if (key === HTTP2_HEADER_STATUS) - statusCode = value; - else if (key === HTTP2_HEADER_DATE) - hasDate = value != null; + headers[key] = oldHeaders[key]; } } headers[kSensitiveHeaders] = oldHeaders[kSensitiveHeaders]; } - statusCode = headers[HTTP2_HEADER_STATUS] = statusCode | 0 || HTTP_STATUS_OK; + const statusCode = + headers[HTTP2_HEADER_STATUS] = + headers[HTTP2_HEADER_STATUS] | 0 || HTTP_STATUS_OK; - if (!hasDate && (options.sendDate == null || options.sendDate)) { - headers[HTTP2_HEADER_DATE] = utcDate(); + if (options.sendDate == null || options.sendDate) { + headers[HTTP2_HEADER_DATE] ??= utcDate(); } validatePreparedResponseHeaders(headers, statusCode); - return { headers, statusCode }; + return { + headers, + statusCode: headers[HTTP2_HEADER_STATUS], + }; } function prepareResponseHeadersArray(headers, options) { @@ -3051,17 +3047,15 @@ class ServerHttp2Stream extends Http2Stream { const state = this[kState]; assertIsObject(options, 'options'); - // The options are only read, never mutated, so the user-provided object - // can be used directly instead of copying it. - options ??= kEmptyObject; + options = { ...options }; debugStreamObj(this, 'initiating response'); this[kUpdateTimer](); - const endStream = !!options.endStream; + options.endStream = !!options.endStream; let streamOptions = 0; - if (endStream) + if (options.endStream) streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD; if (options.waitForTrailers) { @@ -3079,11 +3073,12 @@ class ServerHttp2Stream extends Http2Stream { // Close the writable side if the endStream option is set or status // is one of known codes with no payload, or it's a head request - if (endStream || + if (!!options.endStream || statusCode === HTTP_STATUS_NO_CONTENT || statusCode === HTTP_STATUS_RESET_CONTENT || statusCode === HTTP_STATUS_NOT_MODIFIED || this.headRequest === true) { + options.endStream = true; this.end(); } From bce92debbaec55f84fab0bda5b87031c1853ba96 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 4 Aug 2026 10:17:50 +0200 Subject: [PATCH 002/152] Revert "http2: avoid per-write closures in kWriteGeneric" This reverts commit a44fca5e24d222f4c64364234c8417a2cabbc540. PR-URL: https://github.com/nodejs/node/pull/64663 Reviewed-By: Antoine du Hamel Reviewed-By: Tim Perry Reviewed-By: Ethan Arrowood --- lib/internal/http2/core.js | 106 ++++++++++++------------------------- 1 file changed, 35 insertions(+), 71 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 9e4e228568d5..72b5ceda87b0 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -1991,47 +1991,6 @@ function shutdownWritable(callback) { return afterShutdown.call(req, 0); } -// Completes one of the two halves of a dispatched write (the write callback -// itself and the end-of-stream check); the stream machinery callback runs -// once both have finished. The state lives on stream[kState] because only a -// single write may be in flight at any given time. -function finishWrite(stream) { - const state = stream[kState]; - if (--state.writePending !== 0) - return; - const cb = state.writeCb; - state.writeCb = null; - const err = aggregateTwoErrors(state.endErr, state.writeErr); - state.writeErr = null; - state.endErr = null; - // writeGeneric does not destroy on error and - // we cannot enable autoDestroy, - // so make sure to destroy on error. - if (err) { - stream.destroy(err); - } - cb(err); -} - -// Runs on the tick after a write was dispatched: if the write turned out to -// be the last chunk of an ending writable, shut the writable side down right -// away so the final DATA frame can include the END_STREAM flag. -function endCheckNT(stream) { - const state = stream[kState]; - if (state.writeErr || - !stream._writableState.ending || - stream._writableState.buffered.length || - (state.flags & STREAM_FLAGS_HAS_TRAILERS)) { - finishWrite(stream); - return; - } - debugStreamObj(stream, 'shutting down writable on last write'); - shutdownWritable.call(stream, (err) => { - state.endErr = err; - finishWrite(stream); - }); -} - function finishSendTrailers(stream, headersList) { // The stream might be destroyed and in that case // there is nothing to do. @@ -2139,12 +2098,6 @@ class Http2Stream extends Duplex { writeQueueSize: 0, trailersReady: false, endAfterHeaders: false, - writeCb: null, - writeErr: null, - endErr: null, - writePending: 0, - shutdownWritableCalled: false, - fd: -1, }; // Fields used by the compat API to avoid megamorphisms. @@ -2332,34 +2285,45 @@ class Http2Stream extends Duplex { if (!this.headersSent) this[kProceed](); - // The stream machinery dispatches at most one _write()/_writev() at a - // time, so the coordination state between the write callback and the - // end-of-stream check below can live on the stream state instead of - // being captured by per-write closures. - const state = this[kState]; - state.writeCb = cb; - state.writeErr = null; - state.endErr = null; - - if (state.flags & STREAM_FLAGS_HAS_TRAILERS) { - // Trailers are pending, so the writable side cannot be shut down - // early anyway; there is no point in scheduling the end check. - state.writePending = 1; - } else { - state.writePending = 2; - // Shutdown write stream right after last chunk is sent - // so final DATA frame can include END_STREAM flag - process.nextTick(endCheckNT, this); - } + let req; - // This is invoked both as a method on the write req and as a plain - // call, so the stream has to be captured here. + let waitingForWriteCallback = true; + let waitingForEndCheck = true; + let writeCallbackErr; + let endCheckCallbackErr; + const done = () => { + if (waitingForEndCheck || waitingForWriteCallback) return; + const err = aggregateTwoErrors(endCheckCallbackErr, writeCallbackErr); + // writeGeneric does not destroy on error and + // we cannot enable autoDestroy, + // so make sure to destroy on error. + if (err) { + this.destroy(err); + } + cb(err); + }; const writeCallback = (err) => { - state.writeErr = err; - finishWrite(this); + waitingForWriteCallback = false; + writeCallbackErr = err; + done(); + }; + const endCheckCallback = (err) => { + waitingForEndCheck = false; + endCheckCallbackErr = err; + done(); }; + // Shutdown write stream right after last chunk is sent + // so final DATA frame can include END_STREAM flag + process.nextTick(() => { + if (writeCallbackErr || + !this._writableState.ending || + this._writableState.buffered.length || + (this[kState].flags & STREAM_FLAGS_HAS_TRAILERS)) + return endCheckCallback(); + debugStreamObj(this, 'shutting down writable on last write'); + shutdownWritable.call(this, endCheckCallback); + }); - let req; if (writev) req = writevGeneric(this, data, writeCallback); else From 3ed37153f82c59c8d391e6c8eea73483b4e2ef76 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Thu, 2 Jul 2026 19:42:54 +0200 Subject: [PATCH 003/152] http2: reduce per-request allocations Cut several sources of per-stream/per-request overhead on the hot path: - Track 'priority'/'frameError' stream listeners by overriding the EventEmitter methods on Http2Stream instead of subscribing to 'newListener'/'removeListener', which made every listener add and remove on every stream emit an extra tracking event. - Replace the per-call SafeSet and sensitive-header mapping in buildNgHeaderString with a lazily allocated array and an empty-array fast path, and skip the HTTP token regex and connection-specific header checks for well-known single-value header names. - Replace per-call closures with shared named handlers in onStreamClose, afterShutdown and Http2Stream._destroy. - Skip the pendingStreams Set add/delete for streams that are created with their native handle already available (all server streams). - Hoist the per-request onStreamTimeout closure factories in the compat layer to module-level handlers, and avoid a once() wrapper allocation per server stream. h2load, 1 KiB response payload, -c 4 -m 100, mean of 6 alternating runs: core API 60.2k -> 69.3k req/s (+15%), compat API 43.6k -> 46.2k req/s (+5.9%). Signed-off-by: Matteo Collina PR-URL: https://github.com/nodejs/node/pull/64265 Backport-PR-URL: https://github.com/nodejs/node/pull/64663 Reviewed-By: Antoine du Hamel Reviewed-By: Tim Perry Reviewed-By: Ethan Arrowood --- lib/internal/http2/compat.js | 15 ++++++++------- lib/internal/http2/util.js | 32 +++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 338dc279f48e..1df6a6820af9 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -300,11 +300,12 @@ function onStreamCloseRequest() { req.emit('close'); } -function onStreamTimeout(kind) { - return function onStreamTimeout() { - const obj = this[kind]; - obj.emit('timeout'); - }; +function onStreamTimeoutRequest() { + this[kRequest].emit('timeout'); +} + +function onStreamTimeoutResponse() { + this[kResponse].emit('timeout'); } class Http2ServerRequest extends Readable { @@ -332,7 +333,7 @@ class Http2ServerRequest extends Readable { stream.on('error', onStreamError); stream.on('aborted', onStreamAbortedRequest); stream.on('close', onStreamCloseRequest); - stream.on('timeout', onStreamTimeout(kRequest)); + stream.on('timeout', onStreamTimeoutRequest); this.on('pause', onRequestPause); this.on('resume', onRequestResume); } @@ -486,7 +487,7 @@ class Http2ServerResponse extends Stream { stream.on('aborted', onStreamAbortedResponse); stream.on('close', onStreamCloseResponse); stream.on('wantTrailers', onStreamTrailersReady); - stream.on('timeout', onStreamTimeout(kResponse)); + stream.on('timeout', onStreamTimeoutResponse); } // User land modules such as finalhandler just check truthiness of this diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index 25adc8f9697d..9d35d5878425 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -770,14 +770,16 @@ function buildNgHeaderString(arrayOrMap, let pseudoHeaders = ''; let count = 0; - const singles = new SafeSet(); + let singles; const sensitiveHeaders = arrayOrMap[kSensitiveHeaders] || emptyArray; - const neverIndex = sensitiveHeaders.map((v) => v.toLowerCase()); + const neverIndex = sensitiveHeaders.length === 0 ? + emptyArray : sensitiveHeaders.map((v) => v.toLowerCase()); function processHeader(key, value) { key = key.toLowerCase(); + const isSingleValueField = kSingleValueFields.has(key); const isStrictSingleValueField = strictSingleValueFields && - kSingleValueFields.has(key); + isSingleValueField; let isArray = ArrayIsArray(value); if (isArray) { switch (value.length) { @@ -795,11 +797,15 @@ function buildNgHeaderString(arrayOrMap, value = String(value); } if (isStrictSingleValueField) { - if (singles.has(key)) + if (singles === undefined) { + singles = [key]; + } else if (singles.includes(key)) { throw new ERR_HTTP2_HEADER_SINGLE_VALUE(key); - singles.add(key); + } else { + singles.push(key); + } } - const flags = neverIndex.includes(key) ? + const flags = neverIndex.length !== 0 && neverIndex.includes(key) ? kNeverIndexFlag : kNoHeaderFlags; if (key[0] === ':') { @@ -810,11 +816,15 @@ function buildNgHeaderString(arrayOrMap, count++; return; } - if (!checkIsHttpToken(key)) { - throw new ERR_INVALID_HTTP_TOKEN('Header name', key); - } - if (isIllegalConnectionSpecificHeader(key, value)) { - throw new ERR_HTTP2_INVALID_CONNECTION_HEADERS(key); + // Well-known single-value fields are all valid HTTP tokens and none of + // them is a connection-specific header, so both checks can be skipped. + if (!isSingleValueField) { + if (!checkIsHttpToken(key)) { + throw new ERR_INVALID_HTTP_TOKEN('Header name', key); + } + if (isIllegalConnectionSpecificHeader(key, value)) { + throw new ERR_HTTP2_INVALID_CONNECTION_HEADERS(key); + } } if (isArray) { for (let j = 0; j < value.length; ++j) { From f6692da576e5e4be465d90a6649dde8f9a8b3743 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Thu, 2 Jul 2026 20:52:08 +0200 Subject: [PATCH 004/152] http2: avoid per-write closures in kWriteGeneric Every _write()/_writev() on an Http2Stream allocated four closures and an anonymous nextTick callback to coordinate the write callback with the end-of-stream check. Since the stream machinery dispatches at most one write at a time, that coordination state can live on the stream's kState object instead, with shared named functions for the end check and completion logic. When trailers are pending the writable side cannot be shut down early anyway, so the end-of-stream check tick is now skipped entirely for those writes. Also pre-initialize the kState fields that used to be added dynamically (shutdownWritableCalled, fd) so hot-path stores no longer transition the object shape. h2load, 1 KiB response payload, -c 4 -m 100, mean of 6 alternating runs vs main: core API 61.0k -> 70.7k req/s (+15.9% cumulative), compat API 43.7k -> 50.4k req/s (+15.3% cumulative). Signed-off-by: Matteo Collina PR-URL: https://github.com/nodejs/node/pull/64265 Backport-PR-URL: https://github.com/nodejs/node/pull/64663 Reviewed-By: Antoine du Hamel Reviewed-By: Tim Perry Reviewed-By: Ethan Arrowood --- lib/internal/http2/core.js | 106 +++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 72b5ceda87b0..9e4e228568d5 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -1991,6 +1991,47 @@ function shutdownWritable(callback) { return afterShutdown.call(req, 0); } +// Completes one of the two halves of a dispatched write (the write callback +// itself and the end-of-stream check); the stream machinery callback runs +// once both have finished. The state lives on stream[kState] because only a +// single write may be in flight at any given time. +function finishWrite(stream) { + const state = stream[kState]; + if (--state.writePending !== 0) + return; + const cb = state.writeCb; + state.writeCb = null; + const err = aggregateTwoErrors(state.endErr, state.writeErr); + state.writeErr = null; + state.endErr = null; + // writeGeneric does not destroy on error and + // we cannot enable autoDestroy, + // so make sure to destroy on error. + if (err) { + stream.destroy(err); + } + cb(err); +} + +// Runs on the tick after a write was dispatched: if the write turned out to +// be the last chunk of an ending writable, shut the writable side down right +// away so the final DATA frame can include the END_STREAM flag. +function endCheckNT(stream) { + const state = stream[kState]; + if (state.writeErr || + !stream._writableState.ending || + stream._writableState.buffered.length || + (state.flags & STREAM_FLAGS_HAS_TRAILERS)) { + finishWrite(stream); + return; + } + debugStreamObj(stream, 'shutting down writable on last write'); + shutdownWritable.call(stream, (err) => { + state.endErr = err; + finishWrite(stream); + }); +} + function finishSendTrailers(stream, headersList) { // The stream might be destroyed and in that case // there is nothing to do. @@ -2098,6 +2139,12 @@ class Http2Stream extends Duplex { writeQueueSize: 0, trailersReady: false, endAfterHeaders: false, + writeCb: null, + writeErr: null, + endErr: null, + writePending: 0, + shutdownWritableCalled: false, + fd: -1, }; // Fields used by the compat API to avoid megamorphisms. @@ -2285,45 +2332,34 @@ class Http2Stream extends Duplex { if (!this.headersSent) this[kProceed](); - let req; + // The stream machinery dispatches at most one _write()/_writev() at a + // time, so the coordination state between the write callback and the + // end-of-stream check below can live on the stream state instead of + // being captured by per-write closures. + const state = this[kState]; + state.writeCb = cb; + state.writeErr = null; + state.endErr = null; + + if (state.flags & STREAM_FLAGS_HAS_TRAILERS) { + // Trailers are pending, so the writable side cannot be shut down + // early anyway; there is no point in scheduling the end check. + state.writePending = 1; + } else { + state.writePending = 2; + // Shutdown write stream right after last chunk is sent + // so final DATA frame can include END_STREAM flag + process.nextTick(endCheckNT, this); + } - let waitingForWriteCallback = true; - let waitingForEndCheck = true; - let writeCallbackErr; - let endCheckCallbackErr; - const done = () => { - if (waitingForEndCheck || waitingForWriteCallback) return; - const err = aggregateTwoErrors(endCheckCallbackErr, writeCallbackErr); - // writeGeneric does not destroy on error and - // we cannot enable autoDestroy, - // so make sure to destroy on error. - if (err) { - this.destroy(err); - } - cb(err); - }; + // This is invoked both as a method on the write req and as a plain + // call, so the stream has to be captured here. const writeCallback = (err) => { - waitingForWriteCallback = false; - writeCallbackErr = err; - done(); - }; - const endCheckCallback = (err) => { - waitingForEndCheck = false; - endCheckCallbackErr = err; - done(); + state.writeErr = err; + finishWrite(this); }; - // Shutdown write stream right after last chunk is sent - // so final DATA frame can include END_STREAM flag - process.nextTick(() => { - if (writeCallbackErr || - !this._writableState.ending || - this._writableState.buffered.length || - (this[kState].flags & STREAM_FLAGS_HAS_TRAILERS)) - return endCheckCallback(); - debugStreamObj(this, 'shutting down writable on last write'); - shutdownWritable.call(this, endCheckCallback); - }); + let req; if (writev) req = writevGeneric(this, data, writeCallback); else From 72448a82f435331777af98aa3fa2c87a5db4b2c0 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Fri, 3 Jul 2026 10:47:40 +0200 Subject: [PATCH 005/152] http2: avoid copying the options in respond() respond() copied the user-provided options object on every call just so it could normalize and locally flip options.endStream, and prepareResponseHeadersObject() then looked the :status and date fields up again on the dictionary-mode null-prototype headers copy it had just built. Use a local variable for endStream and pick up :status/date while copying the headers instead. No measurable throughput change on its own; this removes an object clone and several dictionary-mode property lookups per response. Signed-off-by: Matteo Collina PR-URL: https://github.com/nodejs/node/pull/64265 Backport-PR-URL: https://github.com/nodejs/node/pull/64663 Reviewed-By: Antoine du Hamel Reviewed-By: Tim Perry Reviewed-By: Ethan Arrowood --- lib/internal/http2/core.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 9e4e228568d5..8042ae2b6782 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -2654,31 +2654,35 @@ function prepareResponseHeaders(stream, headersParam, options) { function prepareResponseHeadersObject(oldHeaders, options) { assertIsObject(oldHeaders, 'headers', ['Object', 'Array']); const headers = { __proto__: null }; + let statusCode; + let hasDate = false; if (oldHeaders !== null && oldHeaders !== undefined) { // This loop is here for performance reason. Do not change. + // The :status and date fields are picked up while copying so they do + // not have to be looked up again on the null-prototype copy. for (const key in oldHeaders) { if (ObjectHasOwn(oldHeaders, key)) { - headers[key] = oldHeaders[key]; + const value = oldHeaders[key]; + headers[key] = value; + if (key === HTTP2_HEADER_STATUS) + statusCode = value; + else if (key === HTTP2_HEADER_DATE) + hasDate = value != null; } } headers[kSensitiveHeaders] = oldHeaders[kSensitiveHeaders]; } - const statusCode = - headers[HTTP2_HEADER_STATUS] = - headers[HTTP2_HEADER_STATUS] | 0 || HTTP_STATUS_OK; + statusCode = headers[HTTP2_HEADER_STATUS] = statusCode | 0 || HTTP_STATUS_OK; - if (options.sendDate == null || options.sendDate) { - headers[HTTP2_HEADER_DATE] ??= utcDate(); + if (!hasDate && (options.sendDate == null || options.sendDate)) { + headers[HTTP2_HEADER_DATE] = utcDate(); } validatePreparedResponseHeaders(headers, statusCode); - return { - headers, - statusCode: headers[HTTP2_HEADER_STATUS], - }; + return { headers, statusCode }; } function prepareResponseHeadersArray(headers, options) { @@ -3047,15 +3051,17 @@ class ServerHttp2Stream extends Http2Stream { const state = this[kState]; assertIsObject(options, 'options'); - options = { ...options }; + // The options are only read, never mutated, so the user-provided object + // can be used directly instead of copying it. + options ??= kEmptyObject; debugStreamObj(this, 'initiating response'); this[kUpdateTimer](); - options.endStream = !!options.endStream; + const endStream = !!options.endStream; let streamOptions = 0; - if (options.endStream) + if (endStream) streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD; if (options.waitForTrailers) { @@ -3073,12 +3079,11 @@ class ServerHttp2Stream extends Http2Stream { // Close the writable side if the endStream option is set or status // is one of known codes with no payload, or it's a head request - if (!!options.endStream || + if (endStream || statusCode === HTTP_STATUS_NO_CONTENT || statusCode === HTTP_STATUS_RESET_CONTENT || statusCode === HTTP_STATUS_NOT_MODIFIED || this.headRequest === true) { - options.endStream = true; this.end(); } From 44042c20d490d613a858ca013316ae01fc4e347d Mon Sep 17 00:00:00 2001 From: Ryuhei Shima <65934663+islandryu@users.noreply.github.com> Date: Wed, 22 Jul 2026 00:03:54 +0900 Subject: [PATCH 006/152] zlib: accept ArrayBuffer dictionary in Zstd Fixes: https://github.com/nodejs/node/issues/64598 Signed-off-by: islandryu PR-URL: https://github.com/nodejs/node/pull/64599 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- doc/api/zlib.md | 8 ++++++-- lib/zlib.js | 12 +++++++++++- test/parallel/test-zlib-zstd-dictionary.js | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/doc/api/zlib.md b/doc/api/zlib.md index f32715ba6cc6..a06e57c9180c 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -1099,6 +1099,10 @@ added: - v23.8.0 - v22.15.0 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/64599 + description: The `dictionary` option can be a `TypedArray`, `DataView`, or + `ArrayBuffer`. - version: v26.5.0 pr-url: https://github.com/nodejs/node/pull/64023 description: The `rejectGarbageAfterEnd` option was added. @@ -1115,8 +1119,8 @@ Each Zstd-based class takes an `options` object. All options are optional. * `maxOutputLength` {integer} Limits output size when using [convenience methods][]. **Default:** [`buffer.kMaxLength`][] * `info` {boolean} If `true`, returns an object with `buffer` and `engine`. **Default:** `false` -* `dictionary` {Buffer} Optional dictionary used to - improve compression efficiency when compressing or decompressing data that +* `dictionary` {Buffer|TypedArray|DataView|ArrayBuffer} Optional dictionary used + to improve compression efficiency when compressing or decompressing data that shares common patterns with the dictionary. * `rejectGarbageAfterEnd` {boolean} If `true`, decompression fails when input remains after the first complete compressed stream. **Default:** `false` diff --git a/lib/zlib.js b/lib/zlib.js index 47726337cf72..5caaf797c484 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -33,6 +33,7 @@ const { ObjectSetPrototypeOf, Symbol, Uint32Array, + Uint8Array, } = primordials; const { @@ -926,6 +927,15 @@ class Zstd extends ZlibBase { }); } + let dictionary = opts?.dictionary; + if (dictionary !== undefined && !isArrayBufferView(dictionary)) { + if (isAnyArrayBuffer(dictionary)) { + dictionary = new Uint8Array(dictionary); + } else { + dictionary = undefined; + } + } + const handle = mode === ZSTD_COMPRESS ? new binding.ZstdCompress() : new binding.ZstdDecompress(); @@ -938,7 +948,7 @@ class Zstd extends ZlibBase { pledgedSrcSize, writeState, processCallback, - opts?.dictionary && isArrayBufferView(opts.dictionary) ? opts.dictionary : undefined, + dictionary, ); super(opts, mode, handle, zstdDefaultOpts); diff --git a/test/parallel/test-zlib-zstd-dictionary.js b/test/parallel/test-zlib-zstd-dictionary.js index 28dde28cb055..41b2ab90238c 100644 --- a/test/parallel/test-zlib-zstd-dictionary.js +++ b/test/parallel/test-zlib-zstd-dictionary.js @@ -24,3 +24,19 @@ zlib.zstdCompress(input, { dictionary }, common.mustSucceed((compressed) => { assert.strictEqual(decompressed.toString(), input.toString()); })); })); + +const baseline = zlib.zstdCompressSync(input, { dictionary }).length; + +const arrayBuffer = dictionary.buffer.slice( + dictionary.byteOffset, dictionary.byteOffset + dictionary.byteLength); +const uint8 = new Uint8Array(arrayBuffer); +const dataView = new DataView(arrayBuffer); + +for (const dict of [arrayBuffer, uint8, dataView]) { + assert.strictEqual(zlib.zstdCompressSync(input, { dictionary: dict }).length, + baseline); + + const compressed = zlib.zstdCompressSync(input, { dictionary: dict }); + const decompressed = zlib.zstdDecompressSync(compressed, { dictionary: dict }); + assert.strictEqual(decompressed.toString(), input.toString()); +} From bbd6fc58c46951276a5a9364f592a2f8530e8bfb Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Wed, 22 Jul 2026 11:40:02 +0800 Subject: [PATCH 007/152] diagnostics_channel: grow native channel storage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every string-named JavaScript channel consumed an entry in the fixed native subscriber array. Creating more than 1,024 channels triggered a CHECK and terminated the process. Allocate slots only for native publishers and grow the aliased buffer when it fills. Refresh the JavaScript view after resizing and preserve the capacity in snapshots. Signed-off-by: Stephen Belanger PR-URL: https://github.com/nodejs/node/pull/64497 Reviewed-By: Rafael Gonzaga Reviewed-By: Gerhard Stöbich Reviewed-By: James M Snell --- lib/diagnostics_channel.js | 25 +++++++---- lib/internal/process/pre_execution.js | 11 ++++- src/node_diagnostics_channel.cc | 43 +++++++++--------- src/node_diagnostics_channel.h | 5 +-- test/cctest/test_diagnostics_channel.cc | 44 +++++++++++++++++++ .../test-diagnostics-channel-many-channels.js | 19 ++++++++ .../test-diagnostics-channel-symbol-named.js | 1 + .../test-permission-diagnostics-channel.js | 6 +++ 8 files changed, 118 insertions(+), 36 deletions(-) create mode 100644 test/parallel/test-diagnostics-channel-many-channels.js diff --git a/lib/diagnostics_channel.js b/lib/diagnostics_channel.js index 7b78851208df..93a2e85857ab 100644 --- a/lib/diagnostics_channel.js +++ b/lib/diagnostics_channel.js @@ -30,8 +30,9 @@ const { const { triggerUncaughtException } = internalBinding('errors'); +// The subscriber buffer is replaced when native channel storage grows, so it +// must always be accessed through the binding instead of cached. const dc_binding = internalBinding('diagnostics_channel'); -const { subscribers: subscriberCounts } = dc_binding; const { WeakReference, kEmptyObject } = require('internal/util'); const { isPromise } = require('internal/util/types'); @@ -132,7 +133,7 @@ class ActiveChannel { this._subscribers = ArrayPrototypeSlice(this._subscribers); ArrayPrototypePush(this._subscribers, subscription); channels.incRef(this.name); - if (this._index !== undefined) subscriberCounts[this._index]++; + if (this._index !== undefined) dc_binding.subscribers[this._index]++; } unsubscribe(subscription) { @@ -145,7 +146,7 @@ class ActiveChannel { ArrayPrototypePushApply(this._subscribers, after); channels.decRef(this.name); - if (this._index !== undefined) subscriberCounts[this._index]--; + if (this._index !== undefined) dc_binding.subscribers[this._index]--; maybeMarkInactive(this); return true; @@ -155,7 +156,7 @@ class ActiveChannel { const replacing = this._stores.has(store); if (!replacing) { channels.incRef(this.name); - if (this._index !== undefined) subscriberCounts[this._index]++; + if (this._index !== undefined) dc_binding.subscribers[this._index]++; } this._stores.set(store, transform); } @@ -168,7 +169,7 @@ class ActiveChannel { this._stores.delete(store); channels.decRef(this.name); - if (this._index !== undefined) subscriberCounts[this._index]--; + if (this._index !== undefined) dc_binding.subscribers[this._index]--; maybeMarkInactive(this); return true; @@ -208,9 +209,7 @@ class Channel { this._subscribers = undefined; this._stores = undefined; this.name = name; - if (typeof name === 'string') { - this._index = dc_binding.getOrCreateChannelIndex(name); - } + this._index = undefined; channels.set(name, this); } @@ -640,7 +639,15 @@ function tracingChannel(nameOrChannels) { return new TracingChannel(nameOrChannels); } -dc_binding.linkNativeChannel((name) => channel(name)); +// Keep in sync with setupDiagnosticsChannel() in pre_execution.js. +dc_binding.linkNativeChannel((name, index) => { + const linkedChannel = channel(name); + linkedChannel._index = index; + dc_binding.subscribers[index] = + (linkedChannel._subscribers?.length || 0) + + (linkedChannel._stores?.size || 0); + return linkedChannel; +}); module.exports = { channel, diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index a9724f1e6bc5..1fceb26ea558 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -657,9 +657,18 @@ function initializeClusterIPC() { function setupDiagnosticsChannel() { // Re-link native channels after snapshot deserialization since // JS references are cleared during serialization. + // Keep this callback in sync with the initial registration in + // lib/diagnostics_channel.js. const dc = require('diagnostics_channel'); const dc_binding = internalBinding('diagnostics_channel'); - dc_binding.linkNativeChannel((name) => dc.channel(name)); + dc_binding.linkNativeChannel((name, index) => { + const channel = dc.channel(name); + channel._index = index; + dc_binding.subscribers[index] = + (channel._subscribers?.length || 0) + + (channel._stores?.size || 0); + return channel; + }); } function initializePermission() { diff --git a/src/node_diagnostics_channel.cc b/src/node_diagnostics_channel.cc index 450a124c8695..cfae019da62f 100644 --- a/src/node_diagnostics_channel.cc +++ b/src/node_diagnostics_channel.cc @@ -16,6 +16,7 @@ using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; +using v8::Integer; using v8::Isolate; using v8::Local; using v8::Object; @@ -28,8 +29,10 @@ BindingData::BindingData(Realm* realm, Local wrap, InternalFieldInfo* info) : SnapshotableObject(realm, wrap, type_int), - subscribers_( - realm->isolate(), kMaxChannels, MAYBE_FIELD_PTR(info, subscribers)) { + subscribers_(realm->isolate(), + info == nullptr ? kInitialChannelCapacity + : info->subscribers_capacity, + MAYBE_FIELD_PTR(info, subscribers)) { if (info == nullptr) { wrap->Set(realm->context(), FIXED_ONE_BYTE_STRING(realm->isolate(), "subscribers"), @@ -50,25 +53,20 @@ uint32_t BindingData::GetOrCreateChannelIndex(const std::string& name) { if (it != channel_indices_.end()) { return it->second; } - CHECK_LT(next_channel_index_, kMaxChannels); + if (next_channel_index_ == subscribers_.Length()) { + subscribers_.reserve(subscribers_.Length() * 2); + object() + ->Set(realm()->context(), + FIXED_ONE_BYTE_STRING(realm()->isolate(), "subscribers"), + subscribers_.GetJSArray()) + .Check(); + subscribers_.MakeWeak(); + } uint32_t index = next_channel_index_++; channel_indices_.emplace(name, index); return index; } -void BindingData::GetOrCreateChannelIndex( - const FunctionCallbackInfo& args) { - Realm* realm = Realm::GetCurrent(args); - BindingData* binding = realm->GetBindingData(); - CHECK_NOT_NULL(binding); - - CHECK(args[0]->IsString()); - Utf8Value name(realm->isolate(), args[0]); - - uint32_t index = binding->GetOrCreateChannelIndex(*name); - args.GetReturnValue().Set(index); -} - void BindingData::LinkNativeChannel(const FunctionCallbackInfo& args) { Realm* realm = Realm::GetCurrent(args); BindingData* binding = realm->GetBindingData(); @@ -85,10 +83,11 @@ void BindingData::LinkNativeChannel(const FunctionCallbackInfo& args) { Local name = String::NewFromUtf8(isolate, channel_ptr->name_.c_str()) .ToLocalChecked(); - Local argv[] = {name}; + Local argv[] = { + name, Integer::NewFromUnsigned(isolate, channel_ptr->index_)}; Local result; if (binding->link_callback_.Get(isolate) - ->Call(context, v8::Undefined(isolate), 1, argv) + ->Call(context, v8::Undefined(isolate), arraysize(argv), argv) .ToLocal(&result) && result->IsObject()) { channel_ptr->Link(isolate, result.As()); @@ -102,6 +101,7 @@ bool BindingData::PrepareForSerialization(Local context, DCHECK_NULL(internal_field_info_); internal_field_info_ = InternalFieldInfoBase::New(type()); internal_field_info_->subscribers = subscribers_.Serialize(context, creator); + internal_field_info_->subscribers_capacity = subscribers_.Length(); link_callback_.Reset(); channel_wrap_template_.Reset(); channels_.clear(); @@ -130,8 +130,6 @@ void BindingData::Deserialize(Local context, void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Isolate* isolate = isolate_data->isolate(); - SetMethod( - isolate, target, "getOrCreateChannelIndex", GetOrCreateChannelIndex); SetMethod(isolate, target, "linkNativeChannel", LinkNativeChannel); } @@ -146,7 +144,6 @@ void BindingData::CreatePerContextProperties(Local target, void BindingData::RegisterExternalReferences( ExternalReferenceRegistry* registry) { - registry->Register(GetOrCreateChannelIndex); registry->Register(LinkNativeChannel); } @@ -226,10 +223,10 @@ Channel* Channel::Get(Environment* env, const char* name) { HandleScope handle_scope(isolate); Local context = env->context(); Local js_name = String::NewFromUtf8(isolate, name).ToLocalChecked(); - Local argv[] = {js_name}; + Local argv[] = {js_name, Integer::NewFromUnsigned(isolate, index)}; Local result; if (binding->link_callback_.Get(isolate) - ->Call(context, v8::Undefined(isolate), 1, argv) + ->Call(context, v8::Undefined(isolate), arraysize(argv), argv) .ToLocal(&result) && result->IsObject()) { channel->Link(isolate, result.As()); diff --git a/src/node_diagnostics_channel.h b/src/node_diagnostics_channel.h index 1c1831a0f9e4..073e4e4f273b 100644 --- a/src/node_diagnostics_channel.h +++ b/src/node_diagnostics_channel.h @@ -20,10 +20,11 @@ class Channel; class BindingData : public SnapshotableObject { public: - static constexpr size_t kMaxChannels = 1024; + static constexpr size_t kInitialChannelCapacity = 1024; struct InternalFieldInfo : public node::InternalFieldInfoBase { AliasedBufferIndex subscribers; + size_t subscribers_capacity; }; BindingData(Realm* realm, @@ -48,8 +49,6 @@ class BindingData : public SnapshotableObject { v8::Global channel_wrap_template_; std::vector> channels_; - static void GetOrCreateChannelIndex( - const v8::FunctionCallbackInfo& args); static void LinkNativeChannel( const v8::FunctionCallbackInfo& args); diff --git a/test/cctest/test_diagnostics_channel.cc b/test/cctest/test_diagnostics_channel.cc index b456f6aaf9ea..30a004b59070 100644 --- a/test/cctest/test_diagnostics_channel.cc +++ b/test/cctest/test_diagnostics_channel.cc @@ -257,3 +257,47 @@ TEST_F(DiagnosticsChannelTest, JSChannelVisibleFromCpp) { EXPECT_TRUE(js_has_subs->IsTrue()); EXPECT_TRUE(ch->HasSubscribers()); } + +// Native channels grow the shared subscriber storage past its initial +// capacity without losing the state of channels that were already linked. +// Updating the first and last channels after growth also verifies that JS uses +// the replacement buffer instead of a stale cached reference. +TEST_F(DiagnosticsChannelTest, NativeChannelsGrowSubscriberStorage) { + const v8::HandleScope handle_scope(isolate_); + Argv argv; + Env env{handle_scope, argv}; + + SetProcessExitHandler(*env, [&](node::Environment* env_, int exit_code) { + EXPECT_EQ(exit_code, 0); + node::Stop(*env); + }); + + node::LoadEnvironment( + *env, + "globalThis.__dc = require('diagnostics_channel');" + "globalThis.__firstSubscriber = () => {};" + "globalThis.__dc.subscribe('test:cctest:grow:0', " + " globalThis.__firstSubscriber);"); + + Channel* first = Channel::Get(*env, "test:cctest:grow:0"); + ASSERT_NE(first, nullptr); + ASSERT_TRUE(first->HasSubscribers()); + + Channel* last = nullptr; + for (size_t i = 1; i <= 1024; i++) { + std::string name = "test:cctest:grow:" + std::to_string(i); + last = Channel::Get(*env, name.c_str()); + ASSERT_NE(last, nullptr); + } + + RunJS(isolate_, + "globalThis.__dc.unsubscribe('test:cctest:grow:0', " + " globalThis.__firstSubscriber);"); + EXPECT_FALSE(first->HasSubscribers()); + + RunJS(isolate_, + "globalThis.__lastSubscriber = () => {};" + "globalThis.__dc.subscribe('test:cctest:grow:1024', " + " globalThis.__lastSubscriber);"); + EXPECT_TRUE(last->HasSubscribers()); +} diff --git a/test/parallel/test-diagnostics-channel-many-channels.js b/test/parallel/test-diagnostics-channel-many-channels.js new file mode 100644 index 000000000000..3183bc10f485 --- /dev/null +++ b/test/parallel/test-diagnostics-channel-many-channels.js @@ -0,0 +1,19 @@ +'use strict'; + +const common = require('../common'); +const assert = require('node:assert'); +const dc = require('node:diagnostics_channel'); + +let last; +for (let i = 0; i < 1024 * 10 + 1; i++) { + last = dc.channel(`test:many-channels:${i}`); +} + +const onMessage = common.mustCall((message, name) => { + assert.strictEqual(message, 'message'); + assert.strictEqual(name, last.name); +}); + +last.subscribe(onMessage); +last.publish('message'); +assert.strictEqual(last.unsubscribe(onMessage), true); diff --git a/test/parallel/test-diagnostics-channel-symbol-named.js b/test/parallel/test-diagnostics-channel-symbol-named.js index 96fe0fa53596..84213d4e22e5 100644 --- a/test/parallel/test-diagnostics-channel-symbol-named.js +++ b/test/parallel/test-diagnostics-channel-symbol-named.js @@ -12,6 +12,7 @@ const symbol = Symbol('test'); // Individual channel objects can be created to avoid future lookups const channel = dc.channel(symbol); +assert.strictEqual(Object.hasOwn(channel, '_index'), true); // Expect two successful publishes later channel.subscribe(common.mustCall((message, name) => { diff --git a/test/parallel/test-permission-diagnostics-channel.js b/test/parallel/test-permission-diagnostics-channel.js index add99d5f8f09..3593305e552c 100644 --- a/test/parallel/test-permission-diagnostics-channel.js +++ b/test/parallel/test-permission-diagnostics-channel.js @@ -12,6 +12,12 @@ const assert = require('node:assert'); const dc = require('node:diagnostics_channel'); const fs = require('node:fs'); +// JS-only channels must not consume the native subscriber storage used by the +// permission audit publisher. +for (let i = 0; i < 1024 * 10 + 1; i++) { + dc.channel(`test:permission:unrelated:${i}`); +} + const messages = []; dc.subscribe('node:permission-model:fs', (msg) => { messages.push(msg); From 0fb1d2bd65ff7c77c8a7d659516a49562fb95c07 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Tue, 21 Jul 2026 23:49:15 -0700 Subject: [PATCH 008/152] ffi: validate fast integer argument ranges Validate narrow integer and 64-bit BigInt arguments before entering the Fast API trampoline. This prevents out-of-range values from being silently truncated or wrapped and matches the generic FFI path. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.6-sol PR-URL: https://github.com/nodejs/node/pull/64614 Fixes: https://github.com/nodejs/node/issues/64613 Reviewed-By: Paolo Insogna Reviewed-By: Matteo Collina --- lib/internal/ffi/fast-api.js | 68 +++++++++++++++++--- src/ffi/fast.cc | 14 ++++ src/ffi/fast.h | 1 + src/node_ffi.cc | 11 ++-- test/ffi/test-ffi-fast-integer-validation.js | 56 ++++++++++++++++ 5 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 test/ffi/test-ffi-fast-integer-validation.js diff --git a/lib/internal/ffi/fast-api.js b/lib/internal/ffi/fast-api.js index 15b13af23cea..11e8c005dee9 100644 --- a/lib/internal/ffi/fast-api.js +++ b/lib/internal/ffi/fast-api.js @@ -2,6 +2,7 @@ const { ArrayPrototypeIncludes, + NumberIsInteger, ObjectDefineProperty, ReflectApply, StringPrototypeIncludes, @@ -19,6 +20,7 @@ const { } = require('internal/util/types'); const { + charIsSigned, getRawPointer, kFastArguments, kFastBufferInvoke, @@ -28,6 +30,33 @@ const { const kFastBuffer = Symbol('kFastBuffer'); const kStringConversionBuffer = Symbol('kStringConversionBuffer'); +const U64_MAX = 0xFFFFFFFFFFFFFFFFn; +const I64_MAX = 0x7FFFFFFFFFFFFFFFn; +const I64_MIN = -0x8000000000000000n; + +// These ranges mirror ToFFIArgument in src/ffi/types.cc. V8's Fast API +// exposes narrow integers as 32-bit values and uses truncating BigInt +// conversions, so the public FFI ranges must be checked before the raw call. +const fastIntegerTypeInfo = { + __proto__: null, + i8: { kind: 'number', min: -128, max: 127, label: 'an int8' }, + int8: { kind: 'number', min: -128, max: 127, label: 'an int8' }, + char: charIsSigned ? + { kind: 'number', min: -128, max: 127, label: 'an int8' } : + { kind: 'number', min: 0, max: 255, label: 'a uint8' }, + u8: { kind: 'number', min: 0, max: 255, label: 'a uint8' }, + uint8: { kind: 'number', min: 0, max: 255, label: 'a uint8' }, + bool: { kind: 'number', min: 0, max: 255, label: 'a uint8' }, + i16: { kind: 'number', min: -32768, max: 32767, label: 'an int16' }, + int16: { kind: 'number', min: -32768, max: 32767, label: 'an int16' }, + u16: { kind: 'number', min: 0, max: 65535, label: 'a uint16' }, + uint16: { kind: 'number', min: 0, max: 65535, label: 'a uint16' }, + i64: { kind: 'bigint', min: I64_MIN, max: I64_MAX, label: 'an int64' }, + int64: { kind: 'bigint', min: I64_MIN, max: I64_MAX, label: 'an int64' }, + u64: { kind: 'bigint', min: 0n, max: U64_MAX, label: 'a uint64' }, + uint64: { kind: 'bigint', min: 0n, max: U64_MAX, label: 'a uint64' }, +}; + function throwFFIArgError(msg) { // eslint-disable-next-line no-restricted-syntax const err = new TypeError(msg); @@ -40,6 +69,17 @@ function throwFFIArgCountError(expected, actual) { `Invalid argument count: expected ${expected}, got ${actual}`); } +function validateFastIntegerArg(type, value, index) { + const info = fastIntegerTypeInfo[type]; + if (info === undefined) return; + const validType = info.kind === 'number' ? + typeof value === 'number' && NumberIsInteger(value) : + typeof value === 'bigint'; + if (!validType || value < info.min || value > info.max) { + throwFFIArgError(`Argument ${index} must be ${info.label}`); + } +} + function needsRawPointerConversion(type, rawFn) { if (rawFn !== undefined && rawFn[kFastBuffer] === true && (type === 'buffer' || type === 'arraybuffer')) { @@ -134,10 +174,11 @@ function convertPointerArg(type, value, owner, index) { return value; } -function getPointerConversionIndexes(argumentsTypes, rawFn) { +function getFastArgumentIndexes(argumentsTypes, rawFn) { let indexes = null; for (let i = 0; i < argumentsTypes.length; i++) { - if (!needsPointerConversion(argumentsTypes[i], rawFn)) { + if (fastIntegerTypeInfo[argumentsTypes[i]] === undefined && + !needsPointerConversion(argumentsTypes[i], rawFn)) { continue; } if (indexes === null) { @@ -148,6 +189,12 @@ function getPointerConversionIndexes(argumentsTypes, rawFn) { return indexes; } +function convertFastArg(type, value, rawFn, owner, index) { + validateFastIntegerArg(type, value, index); + return needsPointerConversion(type, rawFn) ? + convertPointerArg(type, value, owner, index) : value; +} + function initializeFastBufferMetadata(rawFn, argumentTypes) { if (rawFn === undefined || rawFn === null || argumentTypes === undefined) { return; @@ -192,7 +239,7 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { return rawFn; } - const indexes = getPointerConversionIndexes(argumentTypes, rawFn); + const indexes = getFastArgumentIndexes(argumentTypes, rawFn); if (indexes === null) { return rawFn; } @@ -209,6 +256,7 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { if (arguments.length !== 1) { throwFFIArgCountError(1, arguments.length); } + validateFastIntegerArg(t0, a0, 0); let arg = a0; if (needsNullPointerConversion(t0) && (arg === null || arg === undefined)) { @@ -232,8 +280,8 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { if (arguments.length !== 2) { throwFFIArgCountError(2, arguments.length); } - return rawFn(c0 ? convertPointerArg(t0, a0, owner, 0) : a0, - c1 ? convertPointerArg(t1, a1, owner, 1) : a1); + return rawFn(c0 ? convertFastArg(t0, a0, rawFn, owner, 0) : a0, + c1 ? convertFastArg(t1, a1, rawFn, owner, 1) : a1); }; } else if (nargs === 3) { const c0 = ArrayPrototypeIncludes(indexes, 0); @@ -246,9 +294,9 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { if (arguments.length !== 3) { throwFFIArgCountError(3, arguments.length); } - return rawFn(c0 ? convertPointerArg(t0, a0, owner, 0) : a0, - c1 ? convertPointerArg(t1, a1, owner, 1) : a1, - c2 ? convertPointerArg(t2, a2, owner, 2) : a2); + return rawFn(c0 ? convertFastArg(t0, a0, rawFn, owner, 0) : a0, + c1 ? convertFastArg(t1, a1, rawFn, owner, 1) : a1, + c2 ? convertFastArg(t2, a2, rawFn, owner, 2) : a2); }; } else { wrapper = function(...args) { @@ -257,8 +305,8 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { } for (let i = 0; i < indexes.length; i++) { const index = indexes[i]; - args[index] = convertPointerArg( - argumentTypes[index], args[index], owner, index); + args[index] = convertFastArg( + argumentTypes[index], args[index], rawFn, owner, index); } return ReflectApply(rawFn, undefined, args); }; diff --git a/src/ffi/fast.cc b/src/ffi/fast.cc index 13c039c0da3f..7d4b8b6c3a6b 100644 --- a/src/ffi/fast.cc +++ b/src/ffi/fast.cc @@ -160,6 +160,20 @@ bool SignatureNeedsRawPointerConversions(const FFIFunction& fn) { return false; } +bool SignatureNeedsFastIntegerValidation(const FFIFunction& fn) { + // V8 widens narrow integers to 32 bits and truncates BigInts to 64 bits for + // Fast API calls. These types need a JS range check before the trampoline. + for (const std::string& name : fn.arg_type_names) { + if (name == "bool" || name == "char" || name == "i8" || name == "int8" || + name == "u8" || name == "uint8" || name == "i16" || name == "int16" || + name == "u16" || name == "uint16" || name == "i64" || name == "int64" || + name == "u64" || name == "uint64") { + return true; + } + } + return false; +} + bool IsPointerTypeName(const std::string& name) { // `pointer`, `ptr`, and `function` all use the same uintptr ABI slot; only // the public type spelling differs. diff --git a/src/ffi/fast.h b/src/ffi/fast.h index bd0a2bac88f8..fc9f69970efe 100644 --- a/src/ffi/fast.h +++ b/src/ffi/fast.h @@ -59,6 +59,7 @@ struct FastFFIMetadata { bool IsFastCallSupported(); bool SignatureNeedsRawPointerConversions(const FFIFunction& fn); +bool SignatureNeedsFastIntegerValidation(const FFIFunction& fn); bool IsPointerTypeName(const std::string& name); bool SignatureNeedsFastBufferInvoke(const FFIFunction& fn); std::shared_ptr CloneWithFastBufferArgNames( diff --git a/src/node_ffi.cc b/src/node_ffi.cc index 8e5729cb4ee0..23c58e8ea128 100644 --- a/src/node_ffi.cc +++ b/src/node_ffi.cc @@ -253,10 +253,11 @@ MaybeLocal DynamicLibrary::CreateFunction( bool use_fast_api = info->fast_metadata != nullptr; bool use_sb = !use_fast_api && IsSBEligibleSignature(*fn); bool has_ptr_args = use_sb && SignatureHasPointerArgs(*fn); - // Fast API signatures that still accept JS pointer-like values need a JS - // wrapper with the native type names attached as hidden metadata. - bool needs_raw_pointer_conversions = - use_fast_api && SignatureNeedsRawPointerConversions(*fn); + // Fast API signatures that need JS-side argument conversion or range checks + // use a wrapper with the native type names attached as hidden metadata. + bool needs_fast_argument_wrapper = + use_fast_api && (SignatureNeedsRawPointerConversions(*fn) || + SignatureNeedsFastIntegerValidation(*fn)); // A single pointer-like parameter can get a separate Buffer-aware Fast API // entrypoint so Buffer calls avoid JS pointer extraction. bool needs_fast_buffer_invoke = @@ -381,7 +382,7 @@ MaybeLocal DynamicLibrary::CreateFunction( } } - if (needs_raw_pointer_conversions || needs_fast_buffer_invoke) { + if (needs_fast_argument_wrapper || needs_fast_buffer_invoke) { // Fast API wrappers need only the parameter type names. Result conversion // is still handled by V8's CFunction metadata, unlike the SharedBuffer path // which must also know how to read slot 0. diff --git a/test/ffi/test-ffi-fast-integer-validation.js b/test/ffi/test-ffi-fast-integer-validation.js new file mode 100644 index 000000000000..49fd1364948c --- /dev/null +++ b/test/ffi/test-ffi-fast-integer-validation.js @@ -0,0 +1,56 @@ +// Flags: --experimental-ffi --allow-natives-syntax +'use strict'; + +const common = require('../common'); +common.skipIfFFIMissing(); + +const assert = require('node:assert'); +const { test } = require('node:test'); +const ffi = require('node:ffi'); +const { fixtureSymbols, libraryPath } = require('./ffi-test-common'); + +function optimize(fn, value) { + eval('%PrepareFunctionForOptimization(fn)'); + fn(value); + fn(value); + eval('%OptimizeFunctionOnNextCall(fn)'); + fn(value); +} + +test('fast FFI validates integer argument ranges', () => { + const { lib, functions } = ffi.dlopen(libraryPath, fixtureSymbols); + try { + function callI8(value) { return functions.add_i8(value, 0); } + + function callU8(value) { return functions.add_u8(value, 0); } + + function callI16(value) { return functions.add_i16(value, 0); } + + function callU16(value) { return functions.add_u16(value, 0); } + + function callI64(value) { return functions.add_i64(value, 0n); } + + function callU64(value) { return functions.add_u64(value, 0n); } + + for (const [fn, value] of [ + [callI8, 0], + [callU8, 0], + [callI16, 0], + [callU16, 0], + [callI64, 0n], + [callU64, 0n], + ]) { + optimize(fn, value); + } + + const expect = { code: 'ERR_INVALID_ARG_VALUE' }; + assert.throws(() => callI8(128), expect); + assert.throws(() => callU8(256), expect); + assert.throws(() => callI16(32768), expect); + assert.throws(() => callU16(65536), expect); + assert.throws(() => callI64(2n ** 63n), expect); + assert.throws(() => callU64(2n ** 64n), expect); + } finally { + lib.close(); + } +}); From ca60942f384c4217ee42b3d3d67016aa6da538b8 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:33:17 -0700 Subject: [PATCH 009/152] ffi: preserve uint8 semantics for bool fast calls Normalize bool to kUint8 when creating Fast API metadata. This keeps optimized calls consistent with generic FFI behavior, including numeric return values and rejection of JavaScript Boolean values. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.6-sol PR-URL: https://github.com/nodejs/node/pull/64527 Fixes: https://github.com/nodejs/node/issues/64526 Reviewed-By: Paolo Insogna --- doc/contributing/ffi-fast-api-internals.md | 4 ++-- src/ffi/fast.cc | 4 +--- src/ffi/fast.h | 1 - src/ffi/platforms/arm64.cc | 1 - src/ffi/platforms/loong64.cc | 1 - src/ffi/platforms/ppc64.cc | 1 - src/ffi/platforms/riscv64.cc | 1 - src/ffi/platforms/s390x.cc | 1 - src/ffi/platforms/x64.cc | 4 ---- test/ffi/test-ffi-calls.js | 14 +++++++++++--- 10 files changed, 14 insertions(+), 18 deletions(-) diff --git a/doc/contributing/ffi-fast-api-internals.md b/doc/contributing/ffi-fast-api-internals.md index 280fa4425119..478d7c493f49 100644 --- a/doc/contributing/ffi-fast-api-internals.md +++ b/doc/contributing/ffi-fast-api-internals.md @@ -200,7 +200,6 @@ surface. It models the ABI categories that the generated trampoline knows how to marshal directly: * `kVoid` -* `kBool` * signed and unsigned 8-bit, 16-bit, 32-bit, and 64-bit integers * `kFloat32` * `kFloat64` @@ -208,7 +207,8 @@ marshal directly: * `kBuffer` Public aliases are normalized in `FastScalarTypeFromName()` and -`FastArgTypeFromName()`. +`FastArgTypeFromName()`. In particular, `bool` is normalized to `kUint8` to +match its documented 8-bit unsigned integer semantics. `pointer`, `ptr`, `string`, `str`, `buffer`, and `arraybuffer` all represent pointer-sized native values at the target ABI boundary. They differ in how diff --git a/src/ffi/fast.cc b/src/ffi/fast.cc index 7d4b8b6c3a6b..8c4420761fec 100644 --- a/src/ffi/fast.cc +++ b/src/ffi/fast.cc @@ -37,7 +37,7 @@ bool FastScalarTypeFromName(std::string_view type, FastFFIType* out) { if (type == "void") { *out = FastFFIType::kVoid; } else if (type == "bool") { - *out = FastFFIType::kBool; + *out = FastFFIType::kUint8; } else if (IsTypeName(type, {"i8", "int8"})) { *out = FastFFIType::kInt8; } else if (IsTypeName(type, {"u8", "uint8"})) { @@ -96,8 +96,6 @@ CTypeInfo::Type ToV8Type(FastFFIType type, bool is_return) { switch (type) { case FastFFIType::kVoid: return CTypeInfo::Type::kVoid; - case FastFFIType::kBool: - return CTypeInfo::Type::kBool; case FastFFIType::kUint8: return CTypeInfo::Type::kUint32; case FastFFIType::kInt8: diff --git a/src/ffi/fast.h b/src/ffi/fast.h index fc9f69970efe..539df26da79b 100644 --- a/src/ffi/fast.h +++ b/src/ffi/fast.h @@ -17,7 +17,6 @@ struct FFIFunction; enum class FastFFIType : uint8_t { kVoid, - kBool, kInt8, kUint8, kInt16, diff --git a/src/ffi/platforms/arm64.cc b/src/ffi/platforms/arm64.cc index ccb7a8cf3d04..a3132966b560 100644 --- a/src/ffi/platforms/arm64.cc +++ b/src/ffi/platforms/arm64.cc @@ -136,7 +136,6 @@ uint32_t UxthW(unsigned reg) { // to the ABI width expected by the native target before the final call. bool EmitNarrow(uint32_t** cursor, FastFFIType type, unsigned reg) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kUint8: *(*cursor)++ = UxtbW(reg); return true; diff --git a/src/ffi/platforms/loong64.cc b/src/ffi/platforms/loong64.cc index 9b3bd5dfd901..6bc90358cfca 100644 --- a/src/ffi/platforms/loong64.cc +++ b/src/ffi/platforms/loong64.cc @@ -19,7 +19,6 @@ bool IsFloatType(FastFFIType type) { bool IsNarrowType(FastFFIType type) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kInt8: case FastFFIType::kUint8: case FastFFIType::kInt16: diff --git a/src/ffi/platforms/ppc64.cc b/src/ffi/platforms/ppc64.cc index 4549b98186f5..a0a53cfdb443 100644 --- a/src/ffi/platforms/ppc64.cc +++ b/src/ffi/platforms/ppc64.cc @@ -23,7 +23,6 @@ bool IsFloatType(FastFFIType type) { bool IsNarrowType(FastFFIType type) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kInt8: case FastFFIType::kUint8: case FastFFIType::kInt16: diff --git a/src/ffi/platforms/riscv64.cc b/src/ffi/platforms/riscv64.cc index 306dd5bbce77..0497f38619a8 100644 --- a/src/ffi/platforms/riscv64.cc +++ b/src/ffi/platforms/riscv64.cc @@ -19,7 +19,6 @@ bool IsFloatType(FastFFIType type) { bool IsNarrowType(FastFFIType type) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kInt8: case FastFFIType::kUint8: case FastFFIType::kInt16: diff --git a/src/ffi/platforms/s390x.cc b/src/ffi/platforms/s390x.cc index 2d8a0c0bea7c..dd8606de82bc 100644 --- a/src/ffi/platforms/s390x.cc +++ b/src/ffi/platforms/s390x.cc @@ -19,7 +19,6 @@ bool IsFloatType(FastFFIType type) { bool IsNarrowType(FastFFIType type) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kInt8: case FastFFIType::kUint8: case FastFFIType::kInt16: diff --git a/src/ffi/platforms/x64.cc b/src/ffi/platforms/x64.cc index 1073508d365b..51de3c5452f3 100644 --- a/src/ffi/platforms/x64.cc +++ b/src/ffi/platforms/x64.cc @@ -232,7 +232,6 @@ void EmitNarrowInstruction(uint8_t** cursor, uint8_t opcode, unsigned reg) { // consumes the declared low 8/16 bits. bool EmitNarrowReturn(uint8_t** cursor, FastFFIType type, unsigned reg) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kUint8: EmitNarrowInstruction(cursor, 0xb6, reg); return true; @@ -252,7 +251,6 @@ bool EmitNarrowReturn(uint8_t** cursor, FastFFIType type, unsigned reg) { bool NeedsNarrow(FastFFIType type) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kUint8: case FastFFIType::kInt8: case FastFFIType::kUint16: @@ -655,7 +653,6 @@ void EmitNarrowInstruction(uint8_t** cursor, uint8_t opcode, unsigned reg) { bool EmitNarrowReturn(uint8_t** cursor, FastFFIType type, unsigned reg) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kUint8: EmitNarrowInstruction(cursor, 0xb6, reg); return true; @@ -675,7 +672,6 @@ bool EmitNarrowReturn(uint8_t** cursor, FastFFIType type, unsigned reg) { bool NeedsNarrow(FastFFIType type) { switch (type) { - case FastFFIType::kBool: case FastFFIType::kUint8: case FastFFIType::kInt8: case FastFFIType::kUint16: diff --git a/test/ffi/test-ffi-calls.js b/test/ffi/test-ffi-calls.js index 192fa74fa3fb..e966809de666 100644 --- a/test/ffi/test-ffi-calls.js +++ b/test/ffi/test-ffi-calls.js @@ -1,4 +1,4 @@ -// Flags: --experimental-ffi --expose-gc +// Flags: --experimental-ffi --expose-gc --allow-natives-syntax 'use strict'; const common = require('../common'); common.skipIfFFIMissing(); @@ -62,8 +62,16 @@ test('ffi bool signatures use uint8 values', () => { arguments: ['bool', 'bool'], return: 'bool', }); - assert.strictEqual(boolAdder(1, 0), 1); - assert.throws(() => boolAdder(true, false), /Argument 0 must be a uint8/); + function callBoolAdder(a, b) { + return boolAdder(a, b); + } + + eval('%PrepareFunctionForOptimization(callBoolAdder)'); + assert.strictEqual(callBoolAdder(1, 0), 1); + eval('%OptimizeFunctionOnNextCall(callBoolAdder)'); + assert.strictEqual(callBoolAdder(1, 0), 1); + assert.throws( + () => callBoolAdder(true, false), /Argument 0 must be a uint8/); } finally { lib.close(); } From 870f4997e7b1e815a2fec9557b6101a9a123a576 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Thu, 23 Jul 2026 16:37:21 +0200 Subject: [PATCH 010/152] sqlite: fix use-after-free in Exec() and ApplyChangeset() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When sqlite3_exec() or sqlite3changeset_apply() call JavaScript callbacks (user-defined functions, conflict handlers, or filter callbacks), the DatabaseSync object could be garbage-collected if the JavaScript code drops all references to it. Both methods only held a raw DatabaseSync* pointer on the C++ stack, which V8 GC does not track. Add a BaseObjectPtr guard that keeps the database alive for the duration of these SQLite API calls, preventing a use-after-free when the JavaScript callback triggers GC. Signed-off-by: Matteo Collina PR-URL: https://github.com/nodejs/node/pull/64535 Reviewed-By: Edy Silva Reviewed-By: Stephen Belanger Reviewed-By: James M Snell Reviewed-By: René --- src/node_sqlite.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index d830039b760e..b1e2ea14c445 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -1584,6 +1584,13 @@ void DatabaseSync::Exec(const FunctionCallbackInfo& args) { return; } + // Keep the database alive during sqlite3_exec(), which may call + // user-defined SQLite functions that trigger JavaScript callbacks. + // If the JavaScript callback drops all references to the database, + // the DatabaseSync could otherwise be garbage-collected while the + // SQLite callback is still executing, causing a use-after-free. + BaseObjectPtr guard(db); + Utf8Value sql(env->isolate(), args[0].As()); int r = sqlite3_exec(db->connection_, *sql, nullptr, nullptr, nullptr); CHECK_ERROR_OR_THROW(env->isolate(), db, r, SQLITE_OK, void()); @@ -2358,6 +2365,13 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo& args) { } } + // Keep the database alive during sqlite3changeset_apply(), which may + // call conflict or filter callbacks that trigger JavaScript execution. + // If the JavaScript callback drops all references to the database, + // the DatabaseSync could otherwise be garbage-collected while the + // callback is still executing, causing a use-after-free. + BaseObjectPtr guard(db); + ArrayBufferViewContents buf(args[0]); int r = sqlite3changeset_apply( db->connection_, From aa3f168b315a23a8d5448989d274261fa5996d72 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Thu, 23 Jul 2026 08:27:21 -0700 Subject: [PATCH 011/152] ffi: preserve strings during reentrant calls Cache temporary string conversion buffers by wrapper and active call depth. This prevents nested FFI calls from overwriting or replacing buffers still in use by an outer native call. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.6-sol PR-URL: https://github.com/nodejs/node/pull/64551 Fixes: https://github.com/nodejs/node/issues/64550 Reviewed-By: Paolo Insogna Reviewed-By: Matteo Collina --- lib/internal/ffi/fast-api.js | 95 +++++++++++++++------ test/ffi/fixture_library/ffi_test_library.c | 9 ++ test/ffi/test-ffi-fast-buffer.js | 26 ++++++ 3 files changed, 103 insertions(+), 27 deletions(-) diff --git a/lib/internal/ffi/fast-api.js b/lib/internal/ffi/fast-api.js index 11e8c005dee9..c897f1baf9e6 100644 --- a/lib/internal/ffi/fast-api.js +++ b/lib/internal/ffi/fast-api.js @@ -28,7 +28,6 @@ const { } = internalBinding('ffi'); const kFastBuffer = Symbol('kFastBuffer'); -const kStringConversionBuffer = Symbol('kStringConversionBuffer'); const U64_MAX = 0xFFFFFFFFFFFFFFFFn; const I64_MAX = 0x7FFFFFFFFFFFFFFFn; @@ -119,19 +118,20 @@ function hasPointerMemoryArg(type, value) { (isArrayBufferView(value) || isAnyArrayBuffer(value)); } -function getStringConversionPointer(owner, value, index) { - const size = value.length * 3 + 1; - let buffers = owner[kStringConversionBuffer]; - if (buffers === undefined) { - buffers = []; - ObjectDefineProperty(owner, kStringConversionBuffer, { - __proto__: null, - configurable: false, - enumerable: false, - writable: false, - value: buffers, - }); +function enterStringConversion(state) { + if (state.buffers[state.depth] === undefined) { + state.buffers[state.depth] = []; } + state.depth++; +} + +function exitStringConversion(state) { + state.depth--; +} + +function getStringConversionPointer(state, value, index) { + const size = value.length * 3 + 1; + const buffers = state.buffers[state.depth - 1]; let entry = buffers[index]; if (entry !== undefined && entry.string === value) { return entry.pointer; @@ -157,13 +157,13 @@ function getStringConversionPointer(owner, value, index) { return entry.pointer; } -function convertPointerArg(type, value, owner, index) { +function convertPointerArg(type, value, stringState, index) { if (needsNullPointerConversion(type) && (value === null || value === undefined)) { return 0n; } if (hasStringPointerArg(type, value)) { - return getStringConversionPointer(owner, value, index); + return getStringConversionPointer(stringState, value, index); } if (hasPointerMemoryArg(type, value)) { return getRawPointer(value); @@ -189,10 +189,10 @@ function getFastArgumentIndexes(argumentsTypes, rawFn) { return indexes; } -function convertFastArg(type, value, rawFn, owner, index) { +function convertFastArg(type, value, rawFn, stringState, index) { validateFastIntegerArg(type, value, index); return needsPointerConversion(type, rawFn) ? - convertPointerArg(type, value, owner, index) : value; + convertPointerArg(type, value, stringState, index) : value; } function initializeFastBufferMetadata(rawFn, argumentTypes) { @@ -228,7 +228,7 @@ function inheritMetadata(wrapper, rawFn, nargs) { return wrapper; } -function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { +function wrapWithRawPointerConversions(rawFn, argumentTypes, _owner) { if (rawFn === undefined || rawFn === null) { return rawFn; } @@ -244,6 +244,12 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { return rawFn; } + const stringState = { + __proto__: null, + buffers: [], + depth: 0, + }; + const nargs = argumentTypes.length; let wrapper; if (nargs === 1 && indexes.length === 1 && indexes[0] === 0) { @@ -262,7 +268,12 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { (arg === null || arg === undefined)) { arg = 0n; } else if (string0 && typeof arg === 'string') { - arg = getStringConversionPointer(owner, arg, 0); + enterStringConversion(stringState); + try { + return rawFn(getStringConversionPointer(stringState, arg, 0)); + } finally { + exitStringConversion(stringState); + } } else if (memory0 && (isArrayBufferView(arg) || isAnyArrayBuffer(arg))) { if (fastBufferInvoke !== undefined) { return fastBufferInvoke(arg); @@ -280,8 +291,16 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { if (arguments.length !== 2) { throwFFIArgCountError(2, arguments.length); } - return rawFn(c0 ? convertFastArg(t0, a0, rawFn, owner, 0) : a0, - c1 ? convertFastArg(t1, a1, rawFn, owner, 1) : a1); + const stringCall = (c0 && hasStringPointerArg(t0, a0)) || + (c1 && hasStringPointerArg(t1, a1)); + if (stringCall) enterStringConversion(stringState); + try { + return rawFn(c0 ? + convertFastArg(t0, a0, rawFn, stringState, 0) : a0, + c1 ? convertFastArg(t1, a1, rawFn, stringState, 1) : a1); + } finally { + if (stringCall) exitStringConversion(stringState); + } }; } else if (nargs === 3) { const c0 = ArrayPrototypeIncludes(indexes, 0); @@ -294,21 +313,43 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { if (arguments.length !== 3) { throwFFIArgCountError(3, arguments.length); } - return rawFn(c0 ? convertFastArg(t0, a0, rawFn, owner, 0) : a0, - c1 ? convertFastArg(t1, a1, rawFn, owner, 1) : a1, - c2 ? convertFastArg(t2, a2, rawFn, owner, 2) : a2); + const stringCall = (c0 && hasStringPointerArg(t0, a0)) || + (c1 && hasStringPointerArg(t1, a1)) || + (c2 && hasStringPointerArg(t2, a2)); + if (stringCall) enterStringConversion(stringState); + try { + return rawFn(c0 ? + convertFastArg(t0, a0, rawFn, stringState, 0) : a0, + c1 ? convertFastArg(t1, a1, rawFn, stringState, 1) : a1, + c2 ? convertFastArg(t2, a2, rawFn, stringState, 2) : a2); + } finally { + if (stringCall) exitStringConversion(stringState); + } }; } else { wrapper = function(...args) { if (args.length !== nargs) { throwFFIArgCountError(nargs, args.length); } + let stringCall = false; for (let i = 0; i < indexes.length; i++) { const index = indexes[i]; - args[index] = convertFastArg( - argumentTypes[index], args[index], rawFn, owner, index); + if (hasStringPointerArg(argumentTypes[index], args[index])) { + stringCall = true; + break; + } + } + if (stringCall) enterStringConversion(stringState); + try { + for (let i = 0; i < indexes.length; i++) { + const index = indexes[i]; + args[index] = convertFastArg( + argumentTypes[index], args[index], rawFn, stringState, index); + } + return ReflectApply(rawFn, undefined, args); + } finally { + if (stringCall) exitStringConversion(stringState); } - return ReflectApply(rawFn, undefined, args); }; } diff --git a/test/ffi/fixture_library/ffi_test_library.c b/test/ffi/fixture_library/ffi_test_library.c index c58a6536469d..10d2ed9f66c9 100644 --- a/test/ffi/fixture_library/ffi_test_library.c +++ b/test/ffi/fixture_library/ffi_test_library.c @@ -333,6 +333,15 @@ FFI_EXPORT void call_void_callback(VoidCallback callback) { } } +FFI_EXPORT int32_t string_survives_callback(const char* str, + VoidCallback callback) { + if (callback) { + callback(); + } + + return str && strcmp(str, "outer string") == 0; +} + FFI_EXPORT void call_string_callback(StringCallback callback, const char* str) { if (callback) { callback(str); diff --git a/test/ffi/test-ffi-fast-buffer.js b/test/ffi/test-ffi-fast-buffer.js index ceca89b7c432..596ae83d1888 100644 --- a/test/ffi/test-ffi-fast-buffer.js +++ b/test/ffi/test-ffi-fast-buffer.js @@ -69,3 +69,29 @@ test('fast FFI buffer arguments reject invalid values', () => { lib.close(); } }); + +test('fast FFI string buffers survive reentrant callbacks', { + // Bundled libffi callbacks crash on SmartOS. + skip: common.isSunOS, +}, () => { + const { lib, functions } = ffi.dlopen(libraryPath, { + safe_strlen: { arguments: ['string'], return: 'i32' }, + string_survives_callback: { + arguments: ['string', 'pointer'], + return: 'i32', + }, + }); + let nestedLength; + const callback = lib.registerCallback(() => { + nestedLength = functions.safe_strlen('inner string'); + }); + + try { + assert.strictEqual( + functions.string_survives_callback('outer string', callback), 1); + assert.strictEqual(nestedLength, 12); + } finally { + lib.unregisterCallback(callback); + lib.close(); + } +}); From 6879aa4aa893ec62be369d74e00de022b509f99f Mon Sep 17 00:00:00 2001 From: trivenay Date: Thu, 23 Jul 2026 22:17:25 +0530 Subject: [PATCH 012/152] http: propagate highWaterMark to ClientRequest OutgoingMessage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `http.request({ highWaterMark })` passes the value to the TCP socket via createConnection() but does not set it on the OutgoingMessage internal kHighWaterMark. OutgoingMessage._writeRaw() has two mutually exclusive write paths: Path A (socket connected): conn.write() — uses socket HWM ✓ Path B (no socket yet): outputSize < this[kHighWaterMark] — uses OutgoingMessage own default (64 KB) ✗ Because the OutgoingMessage constructor already accepts options.highWaterMark, the fix is to set kHighWaterMark from the user options after they are parsed in the ClientRequest constructor. This resolves two symptoms: 1. write() returning the wrong boolean for pre-socket writes (the user highWaterMark was silently ignored on all Node versions). 2. A deadlock on Node >= 24.16.0 where the incorrect false return sets kNeedDrain, but drain never fires because the socket was never backpressured (introduced by the stricter drain gate in #62936). Signed-off-by: Naman Trivedi Fixes: https://github.com/nodejs/node/issues/64645 Refs: https://github.com/nodejs/node/pull/62936 PR-URL: https://github.com/nodejs/node/pull/64653 Reviewed-By: Trivikram Kamat Reviewed-By: Tim Perry Reviewed-By: Matteo Collina Reviewed-By: Luigi Pinca --- lib/_http_client.js | 9 +++ .../test-http-client-highwatermark.js | 81 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 test/parallel/test-http-client-highwatermark.js diff --git a/lib/_http_client.js b/lib/_http_client.js index 9eb3c10547d5..2cdc840b60fe 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -51,6 +51,7 @@ const { kSkipPendingData, } = require('_http_common'); const { + kHighWaterMark, kUniqueHeaders, parseUniqueHeadersOption, OutgoingMessage, @@ -348,6 +349,14 @@ function ClientRequest(input, options, cb) { options = ObjectAssign({ __proto__: null }, input, options); } + // Propagate the user's highWaterMark to OutgoingMessage so that + // _writeRaw() uses the correct threshold for writes buffered before + // the socket connects (Path B). Without this, the OutgoingMessage + // defaults to 64 KB regardless of what the caller requested. + if (options.highWaterMark != null) { + this[kHighWaterMark] = options.highWaterMark; + } + let agent = options.agent; const defaultAgent = options._defaultAgent || Agent.globalAgent; if (agent === false) { diff --git a/test/parallel/test-http-client-highwatermark.js b/test/parallel/test-http-client-highwatermark.js new file mode 100644 index 000000000000..b36d6dc54bb0 --- /dev/null +++ b/test/parallel/test-http-client-highwatermark.js @@ -0,0 +1,81 @@ +// Flags: --expose-internals +'use strict'; + +// Regression test: http.request({ highWaterMark }) must propagate the value +// to OutgoingMessage's kHighWaterMark so that _writeRaw() Path B (buffering +// before socket connects) uses the correct threshold. +// +// Without the fix: +// - write() returns the wrong boolean (compares against default 64KB) +// - On Node >= 24.16.0 (post #62936), this causes a deadlock when +// the user awaits 'drain' after write() incorrectly returns false. +// +// Fixes: https://github.com/nodejs/node/issues/64645 + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const { kHighWaterMark } = require('_http_outgoing'); +const { getDefaultHighWaterMark } = require('internal/streams/state'); + +const server = http.createServer(common.mustCall((req, res) => { + req.resume(); + req.on('end', () => res.end('ok')); +}, 3)); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + let completed = 0; + + function done() { + if (++completed === 3) server.close(); + } + + // Test 1: kHighWaterMark is set to user value on the ClientRequest. + { + const hwm = getDefaultHighWaterMark() * 2; + const req = http.request({ port, method: 'POST', highWaterMark: hwm }); + assert.strictEqual(req[kHighWaterMark], hwm); + req.end(); + req.on('response', (res) => { res.resume(); res.on('end', done); }); + } + + // Test 2: Large HWM — write below threshold returns true before socket connects. + { + const req = http.request({ + port, + method: 'POST', + highWaterMark: 100_000, + }, common.mustCall((res) => { + res.resume(); + res.on('end', done); + })); + + // 64KB write in the same tick — socket not yet connected (Path B). + // With HWM=100KB, write() must return true. + const result = req.write(Buffer.alloc(64 * 1024)); + assert.strictEqual(result, true); + req.end(); + } + + // Test 3: Small HWM — write above threshold returns false, drain fires. + { + const req = http.request({ + port, + method: 'POST', + highWaterMark: 512, + }, common.mustCall((res) => { + res.resume(); + res.on('end', done); + })); + + // 2KB write in the same tick — exceeds HWM of 512 bytes. + const result = req.write(Buffer.alloc(2 * 1024)); + assert.strictEqual(result, false); + + // Drain must fire (no deadlock) so we can complete the request. + req.on('drain', common.mustCall(() => { + req.end(); + })); + } +})); From f322870bd1fc00ae1797d147cd23eec85c2d4bd7 Mon Sep 17 00:00:00 2001 From: Archkon <180910180+Archkon@users.noreply.github.com> Date: Fri, 24 Jul 2026 06:17:50 +0800 Subject: [PATCH 013/152] zlib: validate pledgedSrcSize as a safe integer pledgedSrcSize represents an exact byte count. Reject values that are not non-negative safe integers instead of silently ignoring or coercing them through IntegerValue(). Apply the same validation to zlib/iter and retain a native validation check for internal callers. Signed-off-by: Archkon <180910180+Archkon@users.noreply.github.com> PR-URL: https://github.com/nodejs/node/pull/64604 Fixes: https://github.com/nodejs/node/issues/64603 Reviewed-By: Ethan Arrowood Reviewed-By: Jan Martin --- doc/api/zlib.md | 8 +++-- lib/internal/streams/iter/transform.js | 10 ------ lib/zlib.js | 2 +- src/node_zlib.cc | 24 +++++++------ test/parallel/test-stream-iter-validation.js | 11 +++++- .../test-zlib-zstd-pledged-src-size.js | 36 +++++++++++++++++++ 6 files changed, 65 insertions(+), 26 deletions(-) diff --git a/doc/api/zlib.md b/doc/api/zlib.md index a06e57c9180c..46b8eef1ad9f 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -784,8 +784,9 @@ const stream = zlib.createZstdCompress({ #### Pledged Source Size It's possible to specify the expected total size of the uncompressed input via -`opts.pledgedSrcSize`. If the size doesn't match at the end of the input, -compression will fail with the code `ZSTD_error_srcSize_wrong`. +`opts.pledgedSrcSize`, which must be a non-negative safe integer. If the size +doesn't match at the end of the input, compression will fail with the code +`ZSTD_error_srcSize_wrong`. #### Decompressor options @@ -1927,7 +1928,8 @@ added: v25.9.0 `ZSTD_btultra2`. See the [Zstd compressor options][] in the zlib documentation for the full list. - * `pledgedSrcSize` {number} Expected uncompressed size (optional hint). + * `pledgedSrcSize` {number} Expected uncompressed size as a non-negative safe + integer (optional hint). * `dictionary` {Buffer|TypedArray|DataView} * Returns: {Object} A stateful transform. diff --git a/lib/internal/streams/iter/transform.js b/lib/internal/streams/iter/transform.js index 9782f5f50ebf..583c6e9b192d 100644 --- a/lib/internal/streams/iter/transform.js +++ b/lib/internal/streams/iter/transform.js @@ -257,16 +257,6 @@ function createZstdHandle(mode, options, processCallback, onError) { validateParams(options.params, maxParam, ERR_ZSTD_INVALID_PARAM); const pledgedSrcSize = options.pledgedSrcSize; - if (pledgedSrcSize !== undefined) { - if (typeof pledgedSrcSize !== 'number' || NumberIsNaN(pledgedSrcSize)) { - throw new ERR_INVALID_ARG_TYPE('options.pledgedSrcSize', 'number', - pledgedSrcSize); - } - if (pledgedSrcSize < 0) { - throw new ERR_OUT_OF_RANGE('options.pledgedSrcSize', '>= 0', - pledgedSrcSize); - } - } const handle = isCompress ? new binding.ZstdCompress() : new binding.ZstdDecompress(); diff --git a/lib/zlib.js b/lib/zlib.js index 5caaf797c484..e6c5c420d551 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -939,7 +939,7 @@ class Zstd extends ZlibBase { const handle = mode === ZSTD_COMPRESS ? new binding.ZstdCompress() : new binding.ZstdDecompress(); - const pledgedSrcSize = opts?.pledgedSrcSize ?? undefined; + const pledgedSrcSize = opts?.pledgedSrcSize; const writeState = new Uint32Array(2); diff --git a/src/node_zlib.cc b/src/node_zlib.cc index e656075585db..638982c7ede3 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -945,9 +945,6 @@ class ZstdStream final : public CompressionStream { } static void Init(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Local context = env->context(); - CHECK((args.Length() == 4 || args.Length() == 5) && "init(params, pledgedSrcSize, writeResult, writeCallback[, " "dictionary])"); @@ -964,19 +961,24 @@ class ZstdStream final : public CompressionStream { wrap->InitStream(write_result, write_js_callback); uint64_t pledged_src_size = ZSTD_CONTENTSIZE_UNKNOWN; - if (args[1]->IsNumber()) { - int64_t signed_pledged_src_size; - if (!args[1]->IntegerValue(context).To(&signed_pledged_src_size)) { - THROW_ERR_INVALID_ARG_VALUE(wrap->env(), - "pledgedSrcSize should be an integer"); + if (!args[1]->IsUndefined()) { + if (!args[1]->IsNumber()) { + THROW_ERR_INVALID_ARG_TYPE(wrap->env(), + "pledgedSrcSize must be a number"); + return; + } + if (!IsSafeJsInt(args[1])) { + THROW_ERR_OUT_OF_RANGE(wrap->env(), + "pledgedSrcSize must be a safe integer"); return; } + const int64_t signed_pledged_src_size = args[1].As()->Value(); if (signed_pledged_src_size < 0) { - THROW_ERR_INVALID_ARG_VALUE(wrap->env(), - "pledgedSrcSize may not be negative"); + THROW_ERR_OUT_OF_RANGE(wrap->env(), + "pledgedSrcSize must be non-negative"); return; } - pledged_src_size = signed_pledged_src_size; + pledged_src_size = static_cast(signed_pledged_src_size); } AllocScope alloc_scope(wrap); diff --git a/test/parallel/test-stream-iter-validation.js b/test/parallel/test-stream-iter-validation.js index 76bc7899e834..9f77fe330478 100644 --- a/test/parallel/test-stream-iter-validation.js +++ b/test/parallel/test-stream-iter-validation.js @@ -353,7 +353,16 @@ async function testAsyncValidation() { // Zstd pledgedSrcSize await assert.rejects(consume(compressZstd({ pledgedSrcSize: 'bad' })), TYPE); - await assert.rejects(consume(compressZstd({ pledgedSrcSize: -1 })), RANGE); + for (const pledgedSrcSize of [ + NaN, + Infinity, + -Infinity, + 1.9, + -1, + Number.MAX_SAFE_INTEGER + 1, + ]) { + await assert.rejects(consume(compressZstd({ pledgedSrcSize })), RANGE); + } } // ============================================================================= diff --git a/test/parallel/test-zlib-zstd-pledged-src-size.js b/test/parallel/test-zlib-zstd-pledged-src-size.js index b1e32e14ae73..4a5f27394c66 100644 --- a/test/parallel/test-zlib-zstd-pledged-src-size.js +++ b/test/parallel/test-zlib-zstd-pledged-src-size.js @@ -35,3 +35,39 @@ compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 0 }); compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 13 }); compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 42 }); + +function assertInvalidPledgedSrcSize(pledgedSrcSize, expected) { + assert.throws( + () => zlib.createZstdCompress({ pledgedSrcSize }), + expected, + ); + assert.throws( + () => zlib.zstdCompressSync('', { pledgedSrcSize }), + expected, + ); +} + +for (const pledgedSrcSize of ['1', null]) { + assertInvalidPledgedSrcSize(pledgedSrcSize, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + }); +} + +for (const pledgedSrcSize of [ + NaN, + Infinity, + -Infinity, + 1.9, + -1, + Number.MAX_SAFE_INTEGER + 1, +]) { + assertInvalidPledgedSrcSize(pledgedSrcSize, { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }); +} + +zlib.createZstdCompress({ + pledgedSrcSize: Number.MAX_SAFE_INTEGER, +}).destroy(); From c40aaa65391ca4cfefeecce9c0c023265a253f23 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Mon, 15 Jun 2026 00:37:11 +0000 Subject: [PATCH 014/152] doc: run license-builder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/63918 Reviewed-By: Antoine du Hamel Reviewed-By: Michaël Zasso Reviewed-By: Luigi Pinca Reviewed-By: Ulises Gascón --- LICENSE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 2842efa1288e..9cc3315dd388 100644 --- a/LICENSE +++ b/LICENSE @@ -1785,9 +1785,9 @@ The externally maintained libraries used by Node.js are: - zlib, located at deps/zlib, is licensed as follows: """ zlib.h -- interface of the 'zlib' general purpose compression library - version 1.3.1, January 22nd, 2024 + version 1.3.2.1, February xxth, 2026 - Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2026 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages From 53b7d48b47693f65d982bda27d21dfba46a28937 Mon Sep 17 00:00:00 2001 From: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:09:43 +0800 Subject: [PATCH 015/152] src: fix libuv assertion on windows Ignore `PostDelayedTask` after `Stop` to avoid assertions. Fixes: https://github.com/nodejs/node/issues/56645 Signed-off-by: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> PR-URL: https://github.com/nodejs/node/pull/61999 Reviewed-By: Santiago Gimeno Reviewed-By: Aviv Keller --- src/node_platform.cc | 4 +++ .../test-process-exit-after-fetch-throw.js | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 test/parallel/test-process-exit-after-fetch-throw.js diff --git a/src/node_platform.cc b/src/node_platform.cc index e68aa2a0d6f6..57a43eeb6459 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc @@ -117,6 +117,8 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler { double delay_in_seconds) { auto locked = tasks_.Lock(); + if (has_shut_down_) return; + auto entry = std::make_unique(std::move(task), priority); auto delayed = std::make_unique( this, std::move(entry), delay_in_seconds); @@ -132,6 +134,7 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler { void Stop() { auto locked = tasks_.Lock(); + has_shut_down_ = true; locked.Push(std::make_unique(this)); uv_async_send(&flush_tasks_); } @@ -241,6 +244,7 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler { uv_loop_t loop_; uv_async_t flush_tasks_; std::unordered_set timers_; + bool has_shut_down_ = false; }; WorkerThreadsTaskRunner::WorkerThreadsTaskRunner( diff --git a/test/parallel/test-process-exit-after-fetch-throw.js b/test/parallel/test-process-exit-after-fetch-throw.js new file mode 100644 index 000000000000..b6868c2c9e73 --- /dev/null +++ b/test/parallel/test-process-exit-after-fetch-throw.js @@ -0,0 +1,32 @@ +'use strict'; +// Ref: https://github.com/nodejs/node/issues/56645 +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const http = require('http'); + +if (process.argv[2] === 'child') { + http + .createServer((_, res) => { + res.writeHead(302, { Location: '/' }); + res.end(); + }) + .listen(0, '127.0.0.1', async function() { + try { + await fetch(`http://127.0.0.1:${this.address().port}/`); + } catch { + // ignore + } + process.exit(0); + }); +} else { + const child = cp.spawn(process.execPath, [__filename, 'child']); + + child.on( + 'close', + common.mustCall((exitCode, signal) => { + assert.strictEqual(exitCode, 0); + assert.strictEqual(signal, null); + }), + ); +} From d4d7172e10c0a03b5f6d1c123167c9c9464c7a4e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 23 Jul 2026 23:10:12 -0700 Subject: [PATCH 016/152] src: avoid using ToLocalChecked in crypto_hash And other minor cleanups Signed-off-by: James M Snell PR-URL: https://github.com/nodejs/node/pull/64668 Reviewed-By: Filip Skokan Reviewed-By: Joyee Cheung --- src/crypto/crypto_hash.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index 2c413b10b29c..4fe879c8e2c7 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -30,6 +30,7 @@ namespace node { using ncrypto::DataPointer; using ncrypto::EVPMDCtxPointer; using ncrypto::MarkPopErrorOnReturn; +using v8::ArrayBuffer; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -43,8 +44,11 @@ using v8::Maybe; using v8::MaybeLocal; using v8::Name; using v8::Nothing; +using v8::Null; using v8::Object; +using v8::String; using v8::Uint32; +using v8::Uint8Array; using v8::Value; namespace crypto { @@ -195,12 +199,12 @@ void Hash::GetCachedAliases(const FunctionCallbackInfo& args) { values.reserve(size); for (auto& [alias, id] : env->alias_to_md_id_map) { names.push_back(OneByteString(isolate, alias)); - values.push_back(v8::Uint32::New(isolate, id)); + values.push_back(Uint32::New(isolate, id)); } #else CHECK(env->alias_to_md_id_map.empty()); #endif - Local prototype = v8::Null(isolate); + Local prototype = Null(isolate); Local result = Object::New(isolate, prototype, names.data(), values.data(), size); args.GetReturnValue().Set(result); @@ -233,7 +237,7 @@ const EVP_MD* GetDigestImplementation(Environment* env, if (algorithm_cache.As() ->Set(isolate->GetCurrentContext(), algorithm, - v8::Int32::New(isolate, result.cache_id)) + Int32::New(isolate, result.cache_id)) .IsNothing()) { return nullptr; } @@ -311,11 +315,13 @@ void Hash::OneShotDigest(const FunctionCallbackInfo& args) { if (output_length == 0) { if (output_enc == BUFFER) { - Local ab = v8::ArrayBuffer::New(isolate, 0); - args.GetReturnValue().Set( - Buffer::New(isolate, ab, 0, 0).ToLocalChecked()); + Local u8; + if (Buffer::New(isolate, ArrayBuffer::New(isolate, 0), 0, 0) + .ToLocal(&u8)) { + args.GetReturnValue().Set(u8); + } } else { - args.GetReturnValue().Set(v8::String::Empty(isolate)); + args.GetReturnValue().Set(String::Empty(isolate)); } return; } From 8eeae28e88473a0666b20678c1287ec1f59fb004 Mon Sep 17 00:00:00 2001 From: liujiahui <75659415+Javonne-liu@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:57:00 +0800 Subject: [PATCH 017/152] deps: V8: backport c4d06ba586f3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: [loong64][compiler] Extend Word64Select instruction functionality Change-Id: Iba762777642d2d2d3aa904f9afc1e9005139992e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7801520 Reviewed-by: Zhao Jiazhong Commit-Queue: Liu Yu Reviewed-by: Darius Mercadier Auto-Submit: Liu Yu Cr-Commit-Position: refs/heads/main@{#107619} Refs: https://github.com/v8/v8/commit/c4d06ba586f33b8e5db81507b05e2cad9b7c07dc Co-authored-by: liujiahui PR-URL: https://github.com/nodejs/node/pull/63731 Fixes: https://github.com/nodejs/node/issues/63721 Reviewed-By: René Reviewed-By: Michaël Zasso --- common.gypi | 2 +- .../loong64/macro-assembler-loong64.cc | 15 ++ .../codegen/loong64/macro-assembler-loong64.h | 4 + .../backend/loong64/code-generator-loong64.cc | 181 ++++++++++--- .../loong64/instruction-selector-loong64.cc | 32 ++- deps/v8/test/cctest/BUILD.gn | 4 + .../test-run-machops-select-loong64.cc | 250 ++++++++++++++++++ 7 files changed, 443 insertions(+), 45 deletions(-) create mode 100644 deps/v8/test/cctest/compiler/test-run-machops-select-loong64.cc diff --git a/common.gypi b/common.gypi index 8cf118455ec8..0552460cb0b8 100644 --- a/common.gypi +++ b/common.gypi @@ -42,7 +42,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.26', + 'v8_embedder_string': '-node.27', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc b/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc index a8c8e961fc50..71f521e8a3ff 100644 --- a/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc +++ b/deps/v8/src/codegen/loong64/macro-assembler-loong64.cc @@ -3140,6 +3140,21 @@ void MacroAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone, bind(&done); } +void MacroAssembler::SelectWord(Register result, Register cond, Register v_true, + Register v_false) { + if (v_false == zero_reg) { + maskeqz(result, v_true, cond); + } else if (v_true == zero_reg) { + masknez(result, v_false, cond); + } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + maskeqz(scratch, v_true, cond); + masknez(result, v_false, cond); + or_(result, scratch, result); + } +} + void MacroAssembler::CompareWord(Condition cond, Register dst, Register lhs, const Operand& rhs) { switch (cond) { diff --git a/deps/v8/src/codegen/loong64/macro-assembler-loong64.h b/deps/v8/src/codegen/loong64/macro-assembler-loong64.h index be7502a5b5b8..b6be8e7781d5 100644 --- a/deps/v8/src/codegen/loong64/macro-assembler-loong64.h +++ b/deps/v8/src/codegen/loong64/macro-assembler-loong64.h @@ -140,6 +140,10 @@ class V8_EXPORT_PRIVATE MacroAssembler : public MacroAssemblerBase { // Print a message to stdout and abort execution. void Abort(AbortReason msg); + // Select v_true if cond is non-zero, otherwise select v_false. + void SelectWord(Register result, Register cond, Register v_true, + Register v_false); + void CompareWord(Condition cond, Register dst, Register lhs, const Operand& rhs); void Branch(Label* label, bool need_link = false); diff --git a/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc b/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc index 57262ede9d33..a0974e320ed3 100644 --- a/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc +++ b/deps/v8/src/compiler/backend/loong64/code-generator-loong64.cc @@ -1363,10 +1363,14 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( case kLoong64Sub_d: __ Sub_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); break; - case kLoong64SubOvf_d: + case kLoong64SubOvf_d: { + UseScratchRegisterScope temps(masm()); + DCHECK(temps.hasAvailable()); + temps.Exclude(t8); __ SubOverflow_d(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1), t8); break; + } case kLoong64Mul_w: __ Mul_w(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); break; @@ -4591,7 +4595,7 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr, } return; } else { - PrintF("AssembleArchBranch Unimplemented arch_opcode is : %d\n", + PrintF("AssembleArchBoolean Unimplemented arch_opcode is : %d\n", instr->arch_opcode()); TRACE("UNIMPLEMENTED code_generator_loong64: %s at line %d\n", __FUNCTION__, __LINE__); @@ -4646,56 +4650,49 @@ void CodeGenerator::AssembleArchSelect(Instruction* instr, size_t output_index = instr->OutputCount() - 1; // We don't know how many inputs were consumed by the condition, so we have to // calculate the indices of the last two inputs. - DCHECK_GE(instr->InputCount(), 4); size_t true_value_index = instr->InputCount() - 2; size_t false_value_index = instr->InputCount() - 1; + Register result = i.OutputRegister(output_index); + Register v_true = i.InputOrZeroRegister(true_value_index); + Register v_false = i.InputOrZeroRegister(false_value_index); + + DCHECK( + LocationOperand::cast(instr->OutputAt(output_index))->representation() == + MachineRepresentation::kWord64); if (instr->arch_opcode() == kLoong64Tst) { + DCHECK_GE(instr->InputCount(), 4); Condition cc = FlagsConditionToConditionTst(condition); - Register result = i.OutputRegister(output_index); - Register v_true = i.InputOrZeroRegister(true_value_index); - Register v_false = i.InputOrZeroRegister(false_value_index); - if (v_true == zero_reg || v_false == zero_reg) { - if (v_true == zero_reg) { - v_true = v_false; - cc = NegateCondition(cc); - } - if (cc == eq) - __ masknez(result, v_true, t8); - else - __ maskeqz(result, v_true, t8); - } else if (result == v_true || result == v_false) { - if (result == v_false) { - v_false = v_true; - cc = NegateCondition(cc); - } - Label done; - __ Branch(&done, cc, t8, Operand(0)); - __ Move(result, v_false); - __ bind(&done); - } else { - UseScratchRegisterScope temps(masm()); - Register scratch = temps.Acquire(); - if (cc == eq) { - Register temp = v_true; - v_true = v_false; - v_false = temp; - } - __ maskeqz(scratch, v_true, t8); - __ masknez(result, v_false, t8); - __ or_(result, scratch, result); + if (cc == eq) { + Register temp = v_true; + v_true = v_false; + v_false = temp; } + __ SelectWord(result, t8, v_true, v_false); UseScratchRegisterScope temps(masm()); temps.Include(t8); return; } else if (instr->arch_opcode() == kLoong64Cmp64 || - instr->arch_opcode() == kLoong64Cmp32) { + instr->arch_opcode() == kLoong64Cmp32 || + instr->arch_opcode() == kArchStackPointerGreaterThan) { Condition cc = FlagsConditionToConditionCmp(condition); - Register left = i.InputRegister(0); - Operand right = i.InputOperand(1); - Register result = i.OutputRegister(output_index); - Register v_true = i.InputOrZeroRegister(true_value_index); - Register v_false = i.InputOrZeroRegister(false_value_index); + Register left = no_reg; + Operand right = Operand(0); + if (instr->arch_opcode() == kArchStackPointerGreaterThan) { + DCHECK_GE(instr->InputCount(), 3); + DCHECK((cc == ls) || (cc == hi)); + left = sp; + right = i.InputOperand(0); + uint32_t offset; + if (ShouldApplyOffsetToStackCheck(instr, &offset)) { + left = i.TempRegister(1); + __ Sub_d(left, sp, offset); + } + } else { + DCHECK_GE(instr->InputCount(), 4); + left = i.InputRegister(0); + right = i.InputOperand(1); + } if (v_true == zero_reg || v_false == zero_reg) { if (v_true == zero_reg) { v_true = v_false; @@ -4725,6 +4722,108 @@ void CodeGenerator::AssembleArchSelect(Instruction* instr, __ bind(&done); } return; + } else if (instr->arch_opcode() == kLoong64Add_d || + instr->arch_opcode() == kLoong64Sub_d) { + DCHECK_GE(instr->InputCount(), 4); + Condition cc = FlagsConditionToConditionOvf(condition); + if (cc == eq) { + Register temp = v_true; + v_true = v_false; + v_false = temp; + } + UseScratchRegisterScope temps(masm()); + Register scratch1 = temps.Acquire(); + Register scratch2 = temps.Acquire(); + __ srai_d(scratch1, i.OutputRegister(), 32); + __ srai_w(scratch2, i.OutputRegister(), 31); + if (v_false == zero_reg) { + __ xor_(scratch1, scratch1, scratch2); + __ maskeqz(result, v_true, scratch1); + } else if (v_true == zero_reg) { + __ xor_(scratch1, scratch1, scratch2); + __ masknez(result, v_false, scratch1); + } else if (result == v_true || result == v_false) { + if (result == v_false) { + v_false = v_true; + cc = NegateCondition(cc); + } + Label done; + __ Branch(&done, cc, scratch2, Operand(scratch1)); + __ Move(result, v_false); + __ bind(&done); + } else { + Label true_label, done; + __ Branch(&true_label, cc, scratch2, Operand(scratch1)); + __ Move(result, v_false); + __ Branch(&done); + __ bind(&true_label); + __ Move(result, v_true); + __ bind(&done); + } + return; + } else if (instr->arch_opcode() == kLoong64AddOvf_d || + instr->arch_opcode() == kLoong64SubOvf_d) { + DCHECK_GE(instr->InputCount(), 4); + // Overflow occurs if overflow register is negative + Condition cc = lt; + if (condition == kNotOverflow) { + Register temp = v_true; + v_true = v_false; + v_false = temp; + } + if (v_false == zero_reg) { + __ slt(t8, t8, zero_reg); + __ maskeqz(result, v_true, t8); + } else if (v_true == zero_reg) { + __ slt(t8, t8, zero_reg); + __ masknez(result, v_false, t8); + } else if (result == v_true || result == v_false) { + if (result == v_false) { + v_false = v_true; + cc = NegateCondition(cc); + } + Label done; + __ Branch(&done, cc, t8, Operand(zero_reg)); + __ Move(result, v_false); + __ bind(&done); + } else { + Label true_label, done; + __ Branch(&true_label, cc, t8, Operand(zero_reg)); + __ Move(result, v_false); + __ Branch(&done); + __ bind(&true_label); + __ Move(result, v_true); + __ bind(&done); + } + UseScratchRegisterScope temps(masm()); + temps.Include(t8); + } else if (instr->arch_opcode() == kLoong64MulOvf_w || + instr->arch_opcode() == kLoong64MulOvf_d) { + DCHECK_GE(instr->InputCount(), 4); + Condition cc = FlagsConditionToConditionOvf(condition); + if (cc == eq) { + Register temp = v_true; + v_true = v_false; + v_false = temp; + } + __ SelectWord(result, t8, v_true, v_false); + UseScratchRegisterScope temps(masm()); + temps.Include(t8); + return; + } else if (instr->arch_opcode() == kLoong64Float32Cmp || + instr->arch_opcode() == kLoong64Float64Cmp) { + bool predicate; + FlagsConditionToConditionCmpFPU(&predicate, condition); + UseScratchRegisterScope temps(masm()); + Register scratch = temps.Acquire(); + if (!predicate) { + Register temp = v_true; + v_true = v_false; + v_false = temp; + } + __ movcf2gr(scratch, FCC0); + __ SelectWord(result, scratch, v_true, v_false); + return; } else { PrintF("AssembleArchSelect Unimplemented arch_opcode is : %d\n", instr->arch_opcode()); diff --git a/deps/v8/src/compiler/backend/loong64/instruction-selector-loong64.cc b/deps/v8/src/compiler/backend/loong64/instruction-selector-loong64.cc index 4126871d4527..05ed6dae4f0b 100644 --- a/deps/v8/src/compiler/backend/loong64/instruction-selector-loong64.cc +++ b/deps/v8/src/compiler/backend/loong64/instruction-selector-loong64.cc @@ -48,6 +48,20 @@ class Loong64OperandGenerator final : public OperandGenerator { return UseRegister(node); } + InstructionOperand UseRegisterAtEndOrImmediateZero(OpIndex node) { + if (const ConstantOp* constant = + selector()->Get(node).TryCast()) { + if ((constant->IsIntegral() && constant->integral() == 0) || + (constant->kind == ConstantOp::Kind::kFloat32 && + constant->float32().get_bits() == 0) || + (constant->kind == ConstantOp::Kind::kFloat64 && + constant->float64().get_bits() == 0)) { + return UseImmediate(node); + } + } + return UseRegisterAtEnd(node); + } + bool IsIntegerConstant(OpIndex node) { int64_t unused; return selector()->MatchSignedIntegralConstant(node, &unused); @@ -307,7 +321,7 @@ static void VisitBinop(InstructionSelector* selector, turboshaft::OpIndex node, InstructionCode reverse_opcode, FlagsContinuation* cont) { Loong64OperandGenerator g(selector); - InstructionOperand inputs[2]; + InstructionOperand inputs[4]; size_t input_count = 0; InstructionOperand outputs[1]; size_t output_count = 0; @@ -331,6 +345,13 @@ static void VisitBinop(InstructionSelector* selector, turboshaft::OpIndex node, inputs[input_count++] = g.UseOperand(right_node, opcode); } + if (cont->IsSelect()) { + inputs[input_count++] = + g.UseRegisterAtEndOrImmediateZero(cont->true_value()); + inputs[input_count++] = + g.UseRegisterAtEndOrImmediateZero(cont->false_value()); + } + outputs[output_count++] = g.DefineAsRegister(node); DCHECK_NE(0u, input_count); @@ -2169,9 +2190,14 @@ void InstructionSelector::VisitStackPointerGreaterThan( ? OperandGenerator::kUniqueRegister : OperandGenerator::kRegister; - InstructionOperand inputs[] = {g.UseRegisterWithMode(value, register_mode)}; - static constexpr int input_count = arraysize(inputs); + InstructionOperand inputs[3]; + int input_count = 0; + inputs[input_count++] = g.UseRegisterWithMode(value, register_mode); + if (cont->IsSelect()) { + inputs[input_count++] = g.UseRegisterOrImmediateZero(cont->true_value()); + inputs[input_count++] = g.UseRegisterOrImmediateZero(cont->false_value()); + } EmitWithContinuation(opcode, output_count, outputs, input_count, inputs, temp_count, temps, cont); } diff --git a/deps/v8/test/cctest/BUILD.gn b/deps/v8/test/cctest/BUILD.gn index dd759917f784..52faa5ccbcaf 100644 --- a/deps/v8/test/cctest/BUILD.gn +++ b/deps/v8/test/cctest/BUILD.gn @@ -233,6 +233,10 @@ v8_source_set("cctest_sources") { if (is_win) { sources += [ "test-stack-unwinding-win64.cc" ] } + } else if (v8_current_cpu == "loong64") { + if (v8_enable_turbofan) { + sources += [ "compiler/test-run-machops-select-loong64.cc" ] + } } if (v8_use_perfetto) { diff --git a/deps/v8/test/cctest/compiler/test-run-machops-select-loong64.cc b/deps/v8/test/cctest/compiler/test-run-machops-select-loong64.cc new file mode 100644 index 000000000000..946b2173507e --- /dev/null +++ b/deps/v8/test/cctest/compiler/test-run-machops-select-loong64.cc @@ -0,0 +1,250 @@ +// Copyright 2026 the V8 project authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include +#include +#include + +#include "src/base/bits.h" +#include "src/base/ieee754.h" +#include "src/base/numerics/safe_conversions.h" +#include "src/base/overflowing-math.h" +#include "src/base/utils/random-number-generator.h" +#include "src/builtins/builtins.h" +#include "src/common/ptr-compr-inl.h" +#include "src/objects/objects-inl.h" +#include "src/utils/boxed-float.h" +#include "src/utils/utils.h" +#include "test/cctest/cctest.h" +#include "test/cctest/compiler/codegen-tester.h" +#include "test/common/flag-utils.h" +#include "test/common/value-helper.h" + +namespace v8 { +namespace internal { +namespace compiler { + +#define WORD_COMPARE(Cond, Type, TYPE, SIGN, v_true, v_false) \ + { \ + BufferedRawMachineAssemblerTester m(MachineType::Type(), \ + MachineType::Type()); \ + if (!m.machine()->Word64Select().IsSupported()) { \ + return; \ + } \ + Node* cmp = m.Cond(m.Parameter(0), m.Parameter(1)); \ + m.Return(m.Word64Select(cmp, m.Int64Constant(v_true), \ + m.Int64Constant(v_false))); \ + FOR_##TYPE##_INPUTS(i) { \ + FOR_##TYPE##_INPUTS(j) { \ + CHECK_EQ(m.Call(i, j), (i SIGN j) ? v_true : v_false); \ + } \ + } \ + } + +TEST(RunSelectWord32Compare) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + WORD_COMPARE(Word32Equal, Int32, INT32, ==, v_true, v_false) + WORD_COMPARE(Int32LessThan, Int32, INT32, <, v_true, v_false) + WORD_COMPARE(Int32LessThanOrEqual, Int32, INT32, <=, v_true, v_false) + WORD_COMPARE(Uint32LessThan, Uint32, UINT32, <, v_true, v_false) + WORD_COMPARE(Uint32LessThanOrEqual, Uint32, UINT32, <=, v_true, v_false) + } +} + +TEST(RunSelectWord64Compare) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + WORD_COMPARE(Word64Equal, Int64, INT64, ==, v_true, v_false) + WORD_COMPARE(Int64LessThan, Int64, INT64, <, v_true, v_false) + WORD_COMPARE(Int64LessThanOrEqual, Int64, INT64, <=, v_true, v_false) + WORD_COMPARE(Uint64LessThan, Uint64, UINT64, <, v_true, v_false) + WORD_COMPARE(Uint64LessThanOrEqual, Uint64, UINT64, <=, v_true, v_false) + } +} + +TEST(RunSelectFloat32Compare) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + WORD_COMPARE(Float32Equal, Float32, FLOAT32, ==, v_true, v_false) + WORD_COMPARE(Float32LessThan, Float32, FLOAT32, <, v_true, v_false) + WORD_COMPARE(Float32LessThanOrEqual, Float32, FLOAT32, <=, v_true, v_false) + } +} + +TEST(RunSelectFloat64Compare) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + WORD_COMPARE(Float64Equal, Float64, FLOAT64, ==, v_true, v_false) + WORD_COMPARE(Float64LessThan, Float64, FLOAT64, <, v_true, v_false) + WORD_COMPARE(Float64LessThanOrEqual, Float64, FLOAT64, <=, v_true, v_false) + } +} + +#undef WORD_COMPARE + +TEST(RunSelectStackPointerGreaterThan) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Uint64()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + v8::RegisterState state; +#if defined(USE_SIMULATOR) + SimulatorHelper simulator_helper; + if (!simulator_helper.Init(CcTest::isolate())) return; + simulator_helper.FillRegisters(&state); + uint64_t sp = reinterpret_cast(state.sp); +#else + uint64_t sp = reinterpret_cast(&state); +#endif + Node* cmp = m.StackPointerGreaterThan(m.Parameter(0)); + m.Return( + m.Word64Select(cmp, m.Int64Constant(v_true), m.Int64Constant(v_false))); + FOR_UINT64_INPUTS(i) { CHECK_EQ(m.Call(i), sp > i ? v_true : v_false); } + } +} + +TEST(RunSelectWord32AddOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int32(), + MachineType::Int32()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int32AddWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int result; + FOR_INT32_INPUTS(i) { + FOR_INT32_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedAddOverflow32(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord32SubOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int32(), + MachineType::Int32()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int32SubWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int result; + FOR_INT32_INPUTS(i) { + FOR_INT32_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedSubOverflow32(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord32MulOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int32(), + MachineType::Int32()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int32MulWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int result; + FOR_INT32_INPUTS(i) { + FOR_INT32_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedMulOverflow32(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord64AddOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int64(), + MachineType::Int64()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int64AddWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int64_t result; + FOR_INT64_INPUTS(i) { + FOR_INT64_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedAddOverflow64(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord64SubOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int64(), + MachineType::Int64()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int64SubWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int64_t result; + FOR_INT64_INPUTS(i) { + FOR_INT64_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedSubOverflow64(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +TEST(RunSelectWord64MulOvf) { + for (int64_t k = -50; k < 50; k++) { + int64_t v_true = k % 5, v_false = k % 11; + BufferedRawMachineAssemblerTester m(MachineType::Int64(), + MachineType::Int64()); + if (!m.machine()->Word64Select().IsSupported()) { + return; + } + Node* cal = m.Int64MulWithOverflow(m.Parameter(0), m.Parameter(1)); + Node* ovf = m.Projection(1, cal); + m.Return( + m.Word64Select(ovf, m.Int64Constant(v_true), m.Int64Constant(v_false))); + int64_t result; + FOR_INT64_INPUTS(i) { + FOR_INT64_INPUTS(j) { + CHECK_EQ(m.Call(i, j), base::bits::SignedMulOverflow64(i, j, &result) + ? v_true + : v_false); + } + } + } +} + +} // namespace compiler +} // namespace internal +} // namespace v8 From df608e061f762c213aa1a8b289741a0b0b73ad1e Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Sun, 2 Nov 2025 21:43:58 +0000 Subject: [PATCH 018/152] build: perfetto-sdk Signed-off-by: Chengzhong Wu PR-URL: https://github.com/nodejs/node/pull/64565 Refs: https://github.com/nodejs/diagnostics/issues/654 Reviewed-By: James M Snell Reviewed-By: Ryuhei Shima --- common.gypi | 2 +- configure.py | 7 +++++ node.gyp | 16 ++++++++++ tools/v8_gypfiles/features.gypi | 8 +++-- tools/v8_gypfiles/v8.gyp | 54 +++++++++++++++++++++++++++++++-- 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/common.gypi b/common.gypi index 0552460cb0b8..e065e6e925f5 100644 --- a/common.gypi +++ b/common.gypi @@ -87,7 +87,7 @@ 'v8_enable_external_code_space%': 0, 'v8_enable_sandbox%': 0, 'v8_enable_v8_checks%': 0, - 'v8_use_perfetto': 0, + 'v8_use_perfetto%': 0, 'tsan%': 0, ##### end V8 defaults ##### diff --git a/configure.py b/configure.py index f7fa65db775f..20bf5b7d101f 100755 --- a/configure.py +++ b/configure.py @@ -1117,6 +1117,12 @@ default=None, help='disable the V8 inspector protocol') +parser.add_argument('--with-perfetto', + action='store_true', + dest='with_perfetto', + default=None, + help='enable perfetto support') + parser.add_argument('--shared', action='store_true', dest='shared', @@ -2217,6 +2223,7 @@ def configure_v8(o, configs): options.v8_disable_temporal_support = True o['variables']['v8_enable_temporal_support'] = 0 if options.v8_disable_temporal_support else 1 o['variables']['v8_trace_maps'] = 1 if options.trace_maps else 0 + o['variables']['v8_use_perfetto'] = 1 if options.with_perfetto else 0 o['variables']['node_use_v8_platform'] = b(not options.without_v8_platform) o['variables']['node_use_bundled_v8'] = b(not options.without_bundled_v8) o['variables']['force_dynamic_crt'] = 1 if options.shared else 0 diff --git a/node.gyp b/node.gyp index 8a6ab2cbe5b9..78e70394254f 100644 --- a/node.gyp +++ b/node.gyp @@ -1443,6 +1443,14 @@ }, { 'sources!': [ '<@(node_cctest_quic_sources)' ], }], + [ 'v8_use_perfetto==1', { + 'defines': [ + 'PERFETTO_ENABLE_LEGACY_TRACE_EVENTS=1' + ], + 'dependencies': [ + 'deps/perfetto/perfetto.gyp:perfetto_sdk', + ], + }], ['v8_enable_inspector==1', { 'defines': [ 'HAVE_INSPECTOR=1', @@ -1764,6 +1772,14 @@ 'NODE_USE_NODE_CODE_CACHE=1', ], }], + [ 'v8_use_perfetto==1', { + 'defines': [ + 'PERFETTO_ENABLE_LEGACY_TRACE_EVENTS=1' + ], + 'dependencies': [ + 'deps/perfetto/perfetto.gyp:perfetto_sdk', + ], + }], ['v8_enable_inspector==1', { 'defines': [ 'HAVE_INSPECTOR=1', diff --git a/tools/v8_gypfiles/features.gypi b/tools/v8_gypfiles/features.gypi index 0208ce11983a..a5227687d22d 100644 --- a/tools/v8_gypfiles/features.gypi +++ b/tools/v8_gypfiles/features.gypi @@ -200,8 +200,7 @@ # Enable seeded array index hash. 'v8_enable_seeded_array_index_hash%': 1, - # Use Perfetto (https://perfetto.dev) as the default TracingController. Not - # currently implemented. + # Use Perfetto (https://perfetto.dev) as the default TracingController. 'v8_use_perfetto%': 0, # Enable map packing & unpacking (sets -dV8_MAP_PACKING). @@ -463,7 +462,10 @@ 'defines': ['ENABLE_VERIFY_CSA',], }], ['v8_use_perfetto==1', { - 'defines': ['V8_USE_PERFETTO',], + 'defines': [ + 'V8_USE_PERFETTO', + 'V8_USE_PERFETTO_SDK', + ], }], ['v8_enable_map_packing==1', { 'defines': ['V8_MAP_PACKING',], diff --git a/tools/v8_gypfiles/v8.gyp b/tools/v8_gypfiles/v8.gyp index 7953fa44b432..94a5435fca65 100644 --- a/tools/v8_gypfiles/v8.gyp +++ b/tools/v8_gypfiles/v8.gyp @@ -38,6 +38,7 @@ ], }], ], + 'perfetto_gyp_file': '../../deps/perfetto/perfetto.gyp', }, 'includes': ['toolchain.gypi', 'features.gypi'], 'target_defaults': { @@ -292,6 +293,13 @@ 'sources': [ '<(V8_ROOT)/src/init/setup-isolate-full.cc', ], + 'conditions': [ + ['v8_use_perfetto==1', { + 'dependencies': [ + '<(perfetto_gyp_file):perfetto_sdk', + ], + }], + ], }, # v8_init { 'target_name': 'v8_initializers', @@ -312,6 +320,11 @@ ' Date: Sun, 2 Nov 2025 21:52:06 +0000 Subject: [PATCH 019/152] lib: add perfetto support Signed-off-by: Chengzhong Wu PR-URL: https://github.com/nodejs/node/pull/64565 Refs: https://github.com/nodejs/diagnostics/issues/654 Reviewed-By: James M Snell Reviewed-By: Ryuhei Shima --- lib/internal/console/constructor.js | 7 +-- lib/internal/constants.js | 2 + lib/internal/http.js | 16 ++++--- lib/internal/trace_events.js | 56 ++++++++++++++++++++++++ lib/internal/trace_events_async_hooks.js | 28 ++++++------ lib/internal/util/debuglog.js | 20 +++++---- src/node_constants.cc | 3 -- src/node_trace_events.cc | 8 ++++ test/parallel/test-bootstrap-modules.js | 1 + 9 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 lib/internal/trace_events.js diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 0b71abc98f70..9d653793f133 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -35,7 +35,7 @@ const { SymbolToStringTag, } = primordials; -const { trace } = internalBinding('trace_events'); +const { trace, nodeTraceEventCategory, kTraceCount } = require('internal/trace_events'); const { codes: { ERR_CONSOLE_WRITABLE_STREAM, @@ -59,9 +59,6 @@ const { const { isTypedArray, isSet, isMap, isSetIterator, isMapIterator, } = require('internal/util/types'); -const { - CHAR_UPPERCASE_C: kTraceCount, -} = require('internal/constants'); const kCounts = Symbol('counts'); const { time, timeLog, timeEnd, kNone } = require('internal/util/debuglog'); const { channel } = require('diagnostics_channel'); @@ -72,7 +69,7 @@ const onError = channel('console.error'); const onInfo = channel('console.info'); const onDebug = channel('console.debug'); -const kTraceConsoleCategory = 'node,node.console'; +const kTraceConsoleCategory = nodeTraceEventCategory('node.console'); const kMaxGroupIndentation = 1000; diff --git a/lib/internal/constants.js b/lib/internal/constants.js index 8d7204f6cb48..c635272dc6cf 100644 --- a/lib/internal/constants.js +++ b/lib/internal/constants.js @@ -9,7 +9,9 @@ module.exports = { CHAR_UPPERCASE_Z: 90, /* Z */ CHAR_LOWERCASE_Z: 122, /* z */ CHAR_UPPERCASE_C: 67, /* C */ + CHAR_UPPERCASE_B: 66, /* B */ CHAR_LOWERCASE_B: 98, /* b */ + CHAR_UPPERCASE_E: 69, /* E */ CHAR_LOWERCASE_E: 101, /* e */ CHAR_LOWERCASE_N: 110, /* n */ diff --git a/lib/internal/http.js b/lib/internal/http.js index 116d699f505a..304ef04d6638 100644 --- a/lib/internal/http.js +++ b/lib/internal/http.js @@ -9,11 +9,13 @@ const { } = primordials; const { setUnrefTimeout } = require('internal/timers'); -const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events'); const { - CHAR_LOWERCASE_B, - CHAR_LOWERCASE_E, -} = require('internal/constants'); + getCategoryEnabledBuffer, + trace, + nodeTraceEventCategory, + kAsyncBegin, + kAsyncEnd, +} = require('internal/trace_events'); const { URL } = require('internal/url'); const { Buffer } = require('buffer'); @@ -48,14 +50,14 @@ function isTraceHTTPEnabled() { return httpEnabled[0] > 0; } -const traceEventCategory = 'node,node.http'; +const traceEventCategory = nodeTraceEventCategory('node.http'); function traceBegin(...args) { - trace(CHAR_LOWERCASE_B, traceEventCategory, ...args); + trace(kAsyncBegin, traceEventCategory, ...args); } function traceEnd(...args) { - trace(CHAR_LOWERCASE_E, traceEventCategory, ...args); + trace(kAsyncEnd, traceEventCategory, ...args); } function ipToInt(ip) { diff --git a/lib/internal/trace_events.js b/lib/internal/trace_events.js new file mode 100644 index 000000000000..d240f4ad589e --- /dev/null +++ b/lib/internal/trace_events.js @@ -0,0 +1,56 @@ +'use strict'; + +const { getCategoryEnabledBuffer, trace, usePerfetto } = internalBinding('trace_events'); +const { + CHAR_UPPERCASE_B, + CHAR_LOWERCASE_B, + CHAR_UPPERCASE_C, + CHAR_LOWERCASE_E, + CHAR_UPPERCASE_E, + CHAR_LOWERCASE_N, +} = require('internal/constants'); + +let nodeTraceEventCategory; +if (usePerfetto) { + nodeTraceEventCategory = (category) => `${category}`; +} else { + nodeTraceEventCategory = (category) => `node,${category}`; +} + +// The async events describe the execution of a single asynchronous operation, and are +// used to measure the time spent in a single asynchronous operation. +// Async events may overlap with each other. Different events do not have +// to be nested, or FILO (first in last out). +// TODO(legendecas): V8 `trace` API does not support async marks in perfetto yet. +const kAsyncBegin = usePerfetto ? CHAR_UPPERCASE_B : CHAR_LOWERCASE_B; +const kAsyncEnd = usePerfetto ? CHAR_UPPERCASE_E : CHAR_LOWERCASE_E; + +// The sync events describe the execution of a single thread, and are +// used to measure the time spent in a function. +// Sync events must be nested, and are FILO (first in last out), in a stack +// manner. +const kSyncBegin = CHAR_UPPERCASE_B; +const kSyncEnd = CHAR_UPPERCASE_E; + +// Counter events track a named numeric value as it changes over time. Each +// event records the value at a point in time, and the trace viewer renders the +// series as a graph. +// TODO(legendecas): V8 `trace` API does not support count marks in perfetto yet. +const kTraceCount = usePerfetto ? CHAR_LOWERCASE_N : CHAR_UPPERCASE_C; + +// Instant events mark a single moment in time. They have no duration and do +// not need to be paired or nested. +const kTraceInstant = CHAR_LOWERCASE_N; + +module.exports = { + usePerfetto, + getCategoryEnabledBuffer, + trace, + nodeTraceEventCategory, + kAsyncBegin, + kAsyncEnd, + kSyncBegin, + kSyncEnd, + kTraceCount, + kTraceInstant, +}; diff --git a/lib/internal/trace_events_async_hooks.js b/lib/internal/trace_events_async_hooks.js index a9f517ffc9e4..86de9e796474 100644 --- a/lib/internal/trace_events_async_hooks.js +++ b/lib/internal/trace_events_async_hooks.js @@ -7,20 +7,18 @@ const { Symbol, } = primordials; -const { trace } = internalBinding('trace_events'); +const { + trace, + nodeTraceEventCategory, + kAsyncBegin, + kAsyncEnd, + kSyncBegin, + kSyncEnd, +} = require('internal/trace_events'); const async_wrap = internalBinding('async_wrap'); const async_hooks = require('async_hooks'); -const { - CHAR_LOWERCASE_B, - CHAR_LOWERCASE_E, -} = require('internal/constants'); -// Use small letters such that chrome://tracing groups by the name. -// The behavior is not only useful but the same as the events emitted using -// the specific C++ macros. -const kBeforeEvent = CHAR_LOWERCASE_B; -const kEndEvent = CHAR_LOWERCASE_E; -const kTraceEventCategory = 'node,node.async_hooks'; +const kTraceEventCategory = nodeTraceEventCategory('node.async_hooks'); const kEnabled = Symbol('enabled'); @@ -45,7 +43,7 @@ function createHook() { if (nativeProviders.has(type)) return; typeMemory.set(asyncId, type); - trace(kBeforeEvent, kTraceEventCategory, + trace(kAsyncBegin, kTraceEventCategory, type, asyncId, { triggerAsyncId, @@ -57,21 +55,21 @@ function createHook() { const type = typeMemory.get(asyncId); if (type === undefined) return; - trace(kBeforeEvent, kTraceEventCategory, `${type}_CALLBACK`, asyncId); + trace(kSyncBegin, kTraceEventCategory, `${type}_CALLBACK`, asyncId); }, after(asyncId) { const type = typeMemory.get(asyncId); if (type === undefined) return; - trace(kEndEvent, kTraceEventCategory, `${type}_CALLBACK`, asyncId); + trace(kSyncEnd, kTraceEventCategory, `${type}_CALLBACK`, asyncId); }, destroy(asyncId) { const type = typeMemory.get(asyncId); if (type === undefined) return; - trace(kEndEvent, kTraceEventCategory, type, asyncId); + trace(kAsyncEnd, kTraceEventCategory, type, asyncId); // Cleanup asyncId to type map typeMemory.delete(asyncId); diff --git a/lib/internal/util/debuglog.js b/lib/internal/util/debuglog.js index 06a4f8a23985..1cb3ba4df520 100644 --- a/lib/internal/util/debuglog.js +++ b/lib/internal/util/debuglog.js @@ -14,13 +14,15 @@ const { StringPrototypeToLowerCase, StringPrototypeToUpperCase, } = primordials; -const { - CHAR_LOWERCASE_B: kTraceBegin, - CHAR_LOWERCASE_E: kTraceEnd, - CHAR_LOWERCASE_N: kTraceInstant, -} = require('internal/constants'); const { inspect, format, formatWithOptions } = require('internal/util/inspect'); -const { getCategoryEnabledBuffer, trace } = internalBinding('trace_events'); +const { + getCategoryEnabledBuffer, + trace, + nodeTraceEventCategory, + kAsyncBegin, + kAsyncEnd, + kTraceInstant, +} = require('internal/trace_events'); // `debugImpls` and `testEnabled` are deliberately not initialized so any call // to `debuglog()` before `initializeDebugEnv()` is called will throw. @@ -246,7 +248,7 @@ function time(timesStore, traceCategory, implementation, timerFlags, logLabel = if ((timerFlags & kSkipTrace) === 0) { traceLabel = safeTraceLabel(traceLabel); - trace(kTraceBegin, traceCategory, traceLabel, 0); + trace(kAsyncBegin, traceCategory, traceLabel, 0); } timesStore.set(logLabel, process.hrtime()); @@ -286,7 +288,7 @@ function timeEnd( if ((timerFlags & kSkipTrace) === 0) { traceLabel = safeTraceLabel(traceLabel); - trace(kTraceEnd, traceCategory, traceLabel, 0); + trace(kAsyncEnd, traceCategory, traceLabel, 0); } timesStore.delete(logLabel); @@ -385,7 +387,7 @@ function debugWithTimer(set, cb) { ); } - const traceCategory = `node,node.${StringPrototypeToLowerCase(set)}`; + const traceCategory = nodeTraceEventCategory(`node.${StringPrototypeToLowerCase(set)}`); let traceCategoryBuffer; let debugLogCategoryEnabled = false; let timerFlags = kNone; diff --git a/src/node_constants.cc b/src/node_constants.cc index 8abf3fd8afe4..db670789dc06 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1322,9 +1322,6 @@ void DefineTraceConstants(Local target) { NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_MEMORY_DUMP); NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_MARK); NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_CLOCK_SYNC); - NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_ENTER_CONTEXT); - NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_LEAVE_CONTEXT); - NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_LINK_IDS); } void CreatePerContextProperties(Local target, diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc index 030ca9b315b9..3bd9bd2dff5f 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -188,6 +188,14 @@ void NodeCategorySet::Initialize(Local target, .Check(); target->Set(context, trace, binding->Get(context, trace).ToLocalChecked()).Check(); + + Local use_perfetto = + FIXED_ONE_BYTE_STRING(env->isolate(), "usePerfetto"); +#if defined(V8_USE_PERFETTO) + target->Set(context, use_perfetto, v8::True(isolate)).Check(); +#else + target->Set(context, use_perfetto, v8::False(isolate)).Check(); +#endif } void NodeCategorySet::RegisterExternalReferences( diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 01b7ba07cf61..099f8d2eaf05 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -118,6 +118,7 @@ expected.beforePreExec = new Set([ 'NativeModule internal/net', 'NativeModule internal/dns/utils', 'NativeModule internal/modules/esm/get_format', + 'NativeModule internal/trace_events', ]); expected.atRunTime = new Set([ From 2897cc1d936bdf7b7cb560f50b6e10e3a1059804 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Thu, 18 Jun 2026 21:43:19 -0400 Subject: [PATCH 020/152] src: fix trace macro compatibility Signed-off-by: Chengzhong Wu PR-URL: https://github.com/nodejs/node/pull/64565 Refs: https://github.com/nodejs/diagnostics/issues/654 Reviewed-By: James M Snell Reviewed-By: Ryuhei Shima --- src/node_dir.cc | 53 ++++++++++++++++++++++++------------------------ src/node_file.cc | 6 ++++++ 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/node_dir.cc b/src/node_dir.cc index c9173d404c79..952161d9e2cf 100644 --- a/src/node_dir.cc +++ b/src/node_dir.cc @@ -63,16 +63,10 @@ static const char* get_dir_func_name_by_type(uv_fs_type req_type) { #define GET_TRACE_ENABLED \ (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( \ TRACING_CATEGORY_NODE2(fs_dir, sync)) != 0) -#define FS_DIR_SYNC_TRACE_BEGIN(syscall, ...) \ - if (GET_TRACE_ENABLED) \ - TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs_dir, sync), \ - TRACE_NAME(syscall), \ - ##__VA_ARGS__); -#define FS_DIR_SYNC_TRACE_END(syscall, ...) \ - if (GET_TRACE_ENABLED) \ - TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), \ - TRACE_NAME(syscall), \ - ##__VA_ARGS__); +#define FS_DIR_SYNC_TRACE(syscall) \ + if (GET_TRACE_ENABLED) { \ + TRACE_EVENT0(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall)); \ + } #define FS_DIR_ASYNC_TRACE_BEGIN0(fs_type, id) \ TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(TRACING_CATEGORY_NODE2(fs_dir, async), \ @@ -138,9 +132,11 @@ void DirHandle::MemoryInfo(MemoryTracker* tracker) const { inline void DirHandle::GCClose() { if (closed_) return; uv_fs_t req; - FS_DIR_SYNC_TRACE_BEGIN(closedir); - int ret = uv_fs_closedir(nullptr, &req, dir_, nullptr); - FS_DIR_SYNC_TRACE_END(closedir); + int ret; + { + FS_DIR_SYNC_TRACE(closedir); + ret = uv_fs_closedir(nullptr, &req, dir_, nullptr); + } uv_fs_req_cleanup(&req); closing_ = false; closed_ = true; @@ -201,9 +197,8 @@ void DirHandle::Close(const FunctionCallbackInfo& args) { uv_fs_closedir, dir->dir()); } else { // close() FSReqWrapSync req_wrap_sync("closedir"); - FS_DIR_SYNC_TRACE_BEGIN(closedir); + FS_DIR_SYNC_TRACE(closedir); SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_closedir, dir->dir()); - FS_DIR_SYNC_TRACE_END(closedir); } } @@ -299,10 +294,12 @@ void DirHandle::Read(const FunctionCallbackInfo& args) { AfterDirRead, uv_fs_readdir, dir->dir()); } else { // dir.read(encoding, bufferSize) FSReqWrapSync req_wrap_sync("readdir"); - FS_DIR_SYNC_TRACE_BEGIN(readdir); - int err = - SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_readdir, dir->dir()); - FS_DIR_SYNC_TRACE_END(readdir); + int err; + { + FS_DIR_SYNC_TRACE(readdir); + err = SyncCallAndThrowOnError( + env, &req_wrap_sync, uv_fs_readdir, dir->dir()); + } if (err < 0) { return; // syscall failed, no need to continue, error is already thrown } @@ -377,10 +374,12 @@ static void OpenDir(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemRead, path.ToStringView()); FSReqWrapSync req_wrap_sync("opendir", *path); - FS_DIR_SYNC_TRACE_BEGIN(opendir); - int result = - SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_opendir, *path); - FS_DIR_SYNC_TRACE_END(opendir); + int result; + { + FS_DIR_SYNC_TRACE(opendir); + result = + SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_opendir, *path); + } if (result < 0) { return; // syscall failed, no need to continue, error is already thrown } @@ -407,9 +406,11 @@ static void OpenDirSync(const FunctionCallbackInfo& args) { uv_fs_t req; auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); - FS_DIR_SYNC_TRACE_BEGIN(opendir); - int err = uv_fs_opendir(nullptr, &req, *path, nullptr); - FS_DIR_SYNC_TRACE_END(opendir); + int err; + { + FS_DIR_SYNC_TRACE(opendir); + err = uv_fs_opendir(nullptr, &req, *path, nullptr); + } if (err < 0) { return env->ThrowUVException(err, "opendir"); } diff --git a/src/node_file.cc b/src/node_file.cc index b8b8be835980..78bb84b9f06c 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -153,10 +153,16 @@ static const char* get_fs_func_name_by_type(uv_fs_type req_type) { if (GET_TRACE_ENABLED) \ TRACE_EVENT_BEGIN( \ TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), ##__VA_ARGS__); +#ifdef V8_USE_PERFETTO +#define FS_SYNC_TRACE_END(syscall, ...) \ + if (GET_TRACE_ENABLED) \ + TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs, sync), ##__VA_ARGS__); +#else #define FS_SYNC_TRACE_END(syscall, ...) \ if (GET_TRACE_ENABLED) \ TRACE_EVENT_END( \ TRACING_CATEGORY_NODE2(fs, sync), TRACE_NAME(syscall), ##__VA_ARGS__); +#endif #define FS_ASYNC_TRACE_BEGIN0(fs_type, id) \ TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(TRACING_CATEGORY_NODE2(fs, async), \ From 0611d443abde7741390bc267d8e6d0bbee557957 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Fri, 19 Jun 2026 17:46:55 -0400 Subject: [PATCH 021/152] src: rename legacy trace event headers Signed-off-by: Chengzhong Wu PR-URL: https://github.com/nodejs/node/pull/64565 Refs: https://github.com/nodejs/diagnostics/issues/654 Reviewed-By: James M Snell Reviewed-By: Ryuhei Shima --- Makefile | 2 +- node.gyp | 40 +- src/inspector_agent.cc | 8 + src/node_internals.h | 9 - src/tracing/agent_legacy.h | 4 + src/tracing/trace_event.h | 721 +----------------- .../{trace_event.cc => trace_event_helper.cc} | 2 +- src/tracing/trace_event_helper.h | 33 + ...ce_event_common.h => trace_event_legacy.h} | 10 +- src/tracing/trace_event_legacy_inl.h | 710 +++++++++++++++++ 10 files changed, 801 insertions(+), 738 deletions(-) rename src/tracing/{trace_event.cc => trace_event_helper.cc} (93%) create mode 100644 src/tracing/trace_event_helper.h rename src/tracing/{trace_event_common.h => trace_event_legacy.h} (99%) create mode 100644 src/tracing/trace_event_legacy_inl.h diff --git a/Makefile b/Makefile index 985ed3dd8483..bc9dd7a144db 100644 --- a/Makefile +++ b/Makefile @@ -1572,7 +1572,7 @@ LINT_CPP_EXCLUDE ?= LINT_CPP_EXCLUDE += src/node_root_certs.h LINT_CPP_EXCLUDE += $(LINT_CPP_ADDON_DOC_FILES) # These files were copied more or less verbatim from V8. -LINT_CPP_EXCLUDE += src/tracing/trace_event.h src/tracing/trace_event_common.h +LINT_CPP_EXCLUDE += src/tracing/trace_event_legacy.h src/tracing/trace_event_legacy_inl.h # deps/ncrypto is included in this list, as it is maintained in # this repository, and should be linted. Eventually it should move diff --git a/node.gyp b/node.gyp index 78e70394254f..021a77b61f8b 100644 --- a/node.gyp +++ b/node.gyp @@ -199,10 +199,7 @@ 'src/timers.cc', 'src/timer_wrap.cc', 'src/tracing/agent.cc', - 'src/tracing/agent_legacy.cc', - 'src/tracing/node_trace_buffer.cc', - 'src/tracing/node_trace_writer.cc', - 'src/tracing/trace_event.cc', + 'src/tracing/trace_event_helper.cc', 'src/tracing/traced_value.cc', 'src/tty_wrap.cc', 'src/udp_wrap.cc', @@ -338,11 +335,8 @@ 'src/tcp_wrap.h', 'src/timers.h', 'src/tracing/agent.h', - 'src/tracing/agent_legacy.h', - 'src/tracing/node_trace_buffer.h', - 'src/tracing/node_trace_writer.h', + 'src/tracing/trace_event_helper.h', 'src/tracing/trace_event.h', - 'src/tracing/trace_event_common.h', 'src/tracing/traced_value.h', 'src/timer_wrap.h', 'src/timer_wrap-inl.h', @@ -449,6 +443,18 @@ 'src/node_crypto.cc', 'src/node_crypto.h', ], + 'node_tracing_perfetto_sources': [ + ], + 'node_tracing_legacy_sources': [ + 'src/tracing/agent_legacy.cc', + 'src/tracing/agent_legacy.h', + 'src/tracing/node_trace_buffer.cc', + 'src/tracing/node_trace_buffer.h', + 'src/tracing/node_trace_writer.cc', + 'src/tracing/node_trace_writer.h', + 'src/tracing/trace_event_legacy_inl.h', + 'src/tracing/trace_event_legacy.h', + ], 'node_cctest_openssl_sources': [ 'test/cctest/test_crypto_clienthello.cc', 'test/cctest/test_node_crypto.cc', @@ -952,6 +958,18 @@ }], ], }], + [ 'v8_use_perfetto==1', { + 'sources': [ + '<@(node_tracing_perfetto_sources)', + ], + 'dependencies': [ + 'deps/perfetto/perfetto.gyp:perfetto_sdk', + ], + }, { + 'sources': [ + '<@(node_tracing_legacy_sources)', + ], + }], [ 'v8_enable_inspector==1', { 'includes' : [ 'src/inspector/node_inspector.gypi' ], }, { @@ -1444,9 +1462,6 @@ 'sources!': [ '<@(node_cctest_quic_sources)' ], }], [ 'v8_use_perfetto==1', { - 'defines': [ - 'PERFETTO_ENABLE_LEGACY_TRACE_EVENTS=1' - ], 'dependencies': [ 'deps/perfetto/perfetto.gyp:perfetto_sdk', ], @@ -1773,9 +1788,6 @@ ], }], [ 'v8_use_perfetto==1', { - 'defines': [ - 'PERFETTO_ENABLE_LEGACY_TRACE_EVENTS=1' - ], 'dependencies': [ 'deps/perfetto/perfetto.gyp:perfetto_sdk', ], diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 5e1d9149dc75..00b4982d9b83 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -14,7 +14,9 @@ #include "inspector/runtime_agent.h" #include "inspector/storage_agent.h" #include "inspector/target_agent.h" +#ifndef V8_USE_PERFETTO #include "inspector/tracing_agent.h" +#endif // V8_USE_PERFETTO #include "inspector/worker_agent.h" #include "inspector/worker_inspector.h" #include "inspector_io.h" @@ -238,9 +240,11 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, StringView(), V8Inspector::ClientTrustLevel::kFullyTrusted); node_dispatcher_ = std::make_unique(this); +#ifndef V8_USE_PERFETTO tracing_agent_ = std::make_unique(env, main_thread_); tracing_agent_->Wire(node_dispatcher_.get()); +#endif // V8_USE_PERFETTO if (worker_manager) { worker_agent_ = std::make_unique(worker_manager); worker_agent_->Wire(node_dispatcher_.get()); @@ -274,8 +278,10 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, } ~ChannelImpl() override { +#ifndef V8_USE_PERFETTO tracing_agent_->disable(); tracing_agent_.reset(); // Dispose before the dispatchers +#endif // V8_USE_PERFETTO if (worker_agent_) { worker_agent_->disable(); worker_agent_.reset(); // Dispose before the dispatchers @@ -436,7 +442,9 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, } std::unique_ptr runtime_agent_; +#ifndef V8_USE_PERFETTO std::unique_ptr tracing_agent_; +#endif std::unique_ptr worker_agent_; std::shared_ptr target_agent_; std::unique_ptr network_inspector_; diff --git a/src/node_internals.h b/src/node_internals.h index dbe368686006..631a8d7ccdd9 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -317,15 +317,6 @@ class ThreadPoolWork { const char* type_; }; -#define TRACING_CATEGORY_NODE "node" -#define TRACING_CATEGORY_NODE1(one) \ - TRACING_CATEGORY_NODE "," \ - TRACING_CATEGORY_NODE "." #one -#define TRACING_CATEGORY_NODE2(one, two) \ - TRACING_CATEGORY_NODE "," \ - TRACING_CATEGORY_NODE "." #one "," \ - TRACING_CATEGORY_NODE "." #one "." #two - // Functions defined in node.cc that are exposed via the bootstrapper object #if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) diff --git a/src/tracing/agent_legacy.h b/src/tracing/agent_legacy.h index 4b2a728dc28a..cd992d1ee394 100644 --- a/src/tracing/agent_legacy.h +++ b/src/tracing/agent_legacy.h @@ -6,6 +6,10 @@ // This is an implementation of the legacy V8 tracing agent // defined in `libplatform/v8-tracing.h`. +#ifdef V8_USE_PERFETTO +#error Perfetto is enabled. +#endif + #include "libplatform/v8-tracing.h" #include "node_mutex.h" #include "tracing/agent.h" diff --git a/src/tracing/trace_event.h b/src/tracing/trace_event.h index a0f8c695ea01..21d158fb16fb 100644 --- a/src/tracing/trace_event.h +++ b/src/tracing/trace_event.h @@ -1,720 +1,21 @@ -// Copyright 2015 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - #ifndef SRC_TRACING_TRACE_EVENT_H_ #define SRC_TRACING_TRACE_EVENT_H_ -#include "v8-platform.h" -#include "tracing/agent_legacy.h" -#include "trace_event_common.h" -#include - -// This header file defines implementation details of how the trace macros in -// trace_event_common.h collect and store trace events. Anything not -// implementation-specific should go in trace_macros_common.h instead of here. - - -// The pointer returned from GetCategoryGroupEnabled() points to a -// value with zero or more of the following bits. Used in this class only. -// The TRACE_EVENT macros should only use the value as a bool. -// These values must be in sync with macro values in trace_log.h in -// chromium. -enum CategoryGroupEnabledFlags { - // Category group enabled for the recording mode. - kEnabledForRecording_CategoryGroupEnabledFlags = 1 << 0, - // Category group enabled by SetEventCallbackEnabled(). - kEnabledForEventCallback_CategoryGroupEnabledFlags = 1 << 2, - // Category group enabled to export events to ETW. - kEnabledForETWExport_CategoryGroupEnabledFlags = 1 << 3, -}; - -// By default, const char* argument values are assumed to have long-lived scope -// and will not be copied. Use this macro to force a const char* to be copied. -#define TRACE_STR_COPY(str) node::tracing::TraceStringWithCopy(str) - -// By default, uint64 ID argument values are not mangled with the Process ID in -// TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling. -#define TRACE_ID_MANGLE(id) node::tracing::TraceID::ForceMangle(id) - -// By default, pointers are mangled with the Process ID in TRACE_EVENT_ASYNC -// macros. Use this macro to prevent Process ID mangling. -#define TRACE_ID_DONT_MANGLE(id) node::tracing::TraceID::DontMangle(id) - -// By default, trace IDs are eventually converted to a single 64-bit number. Use -// this macro to add a scope string. -#define TRACE_ID_WITH_SCOPE(scope, id) \ - trace_event_internal::TraceID::WithScope(scope, id) - -#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \ - *INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \ - (kEnabledForRecording_CategoryGroupEnabledFlags | \ - kEnabledForEventCallback_CategoryGroupEnabledFlags) - -// The following macro has no implementation, but it needs to exist since -// it gets called from scoped trace events. It cannot call UNIMPLEMENTED() -// since an empty implementation is a valid one. -#define INTERNAL_TRACE_MEMORY(category, name) - -//////////////////////////////////////////////////////////////////////////////// -// Implementation specific tracing API definitions. - -// Get a pointer to the enabled state of the given trace category. Only -// long-lived literal strings should be given as the category group. The -// returned pointer can be held permanently in a local static for example. If -// the unsigned char is non-zero, tracing is enabled. If tracing is enabled, -// TRACE_EVENT_API_ADD_TRACE_EVENT can be called. It's OK if tracing is disabled -// between the load of the tracing state and the call to -// TRACE_EVENT_API_ADD_TRACE_EVENT, because this flag only provides an early out -// for best performance when tracing is disabled. -// const uint8_t* -// TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(const char* category_group) -#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \ - node::tracing::TraceEventHelper::GetCategoryGroupEnabled - -// Get the number of times traces have been recorded. This is used to implement -// the TRACE_EVENT_IS_NEW_TRACE facility. -// unsigned int TRACE_EVENT_API_GET_NUM_TRACES_RECORDED() -#define TRACE_EVENT_API_GET_NUM_TRACES_RECORDED UNIMPLEMENTED() - -// Add a trace event to the platform tracing system. -// uint64_t TRACE_EVENT_API_ADD_TRACE_EVENT( -// char phase, -// const uint8_t* category_group_enabled, -// const char* name, -// const char* scope, -// uint64_t id, -// uint64_t bind_id, -// int num_args, -// const char** arg_names, -// const uint8_t* arg_types, -// const uint64_t* arg_values, -// unsigned int flags) -#define TRACE_EVENT_API_ADD_TRACE_EVENT node::tracing::AddTraceEventImpl - -// Add a trace event to the platform tracing system. -// uint64_t TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( -// char phase, -// const uint8_t* category_group_enabled, -// const char* name, -// const char* scope, -// uint64_t id, -// uint64_t bind_id, -// int num_args, -// const char** arg_names, -// const uint8_t* arg_types, -// const uint64_t* arg_values, -// unsigned int flags, -// int64_t timestamp) -#define TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP \ - node::tracing::AddTraceEventWithTimestampImpl - -// Set the duration field of a COMPLETE trace event. -// void TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION( -// const uint8_t* category_group_enabled, -// const char* name, -// uint64_t id) -#define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION \ - if (auto controller = \ - node::tracing::TraceEventHelper::GetTracingController()) \ - controller->UpdateTraceEventDuration - -// Adds a metadata event to the trace log. The |AppendValueAsTraceFormat| method -// on the convertable value will be called at flush time. -// TRACE_EVENT_API_ADD_METADATA_EVENT( -// const unsigned char* category_group_enabled, -// const char* event_name, -// const char* arg_name, -// std::unique_ptr arg_value) -#define TRACE_EVENT_API_ADD_METADATA_EVENT node::tracing::AddMetadataEvent - -// Defines atomic operations used internally by the tracing system. -#define TRACE_EVENT_API_ATOMIC_WORD std::atomic -#define TRACE_EVENT_API_ATOMIC_WORD_VALUE intptr_t -#define TRACE_EVENT_API_ATOMIC_LOAD(var) (var).load() -#define TRACE_EVENT_API_ATOMIC_STORE(var, value) (var).store(value) - -//////////////////////////////////////////////////////////////////////////////// - -// Implementation detail: trace event macros create temporary variables -// to keep instrumentation overhead low. These macros give each temporary -// variable a unique name based on the line number to prevent name collisions. -#define INTERNAL_TRACE_EVENT_UID3(a, b) trace_event_unique_##a##b -#define INTERNAL_TRACE_EVENT_UID2(a, b) INTERNAL_TRACE_EVENT_UID3(a, b) -#define INTERNAL_TRACE_EVENT_UID(name_prefix) \ - INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__) - -// Implementation detail: internal macro to create static category. -// No barriers are needed, because this code is designed to operate safely -// even when the unsigned char* points to garbage data (which may be the case -// on processors without cache coherency). -// TODO(fmeawad): This implementation contradicts that we can have a different -// configuration for each isolate, -// https://code.google.com/p/v8/issues/detail?id=4563 -#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \ - category_group, atomic, category_group_enabled) \ - category_group_enabled = \ - reinterpret_cast(TRACE_EVENT_API_ATOMIC_LOAD(atomic)); \ - if (!category_group_enabled) { \ - category_group_enabled = \ - TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \ - TRACE_EVENT_API_ATOMIC_STORE( \ - atomic, reinterpret_cast( \ - category_group_enabled)); \ - } - -#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) \ - static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) {0}; \ - const uint8_t* INTERNAL_TRACE_EVENT_UID(category_group_enabled); \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \ - category_group, INTERNAL_TRACE_EVENT_UID(atomic), \ - INTERNAL_TRACE_EVENT_UID(category_group_enabled)); - -// Implementation detail: internal macro to create static category and add -// event if the category is enabled. -#define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \ - do { \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ - node::tracing::AddTraceEvent( \ - phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - node::tracing::kGlobalScope, node::tracing::kNoId, \ - node::tracing::kNoId, flags, ##__VA_ARGS__); \ - } \ - } while (0) - -// Implementation detail: internal macro to create static category and add begin -// event if the category is enabled. Also adds the end event when the scope -// ends. -#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, ...) \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - node::tracing::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \ - if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ - uint64_t h = node::tracing::AddTraceEvent( \ - TRACE_EVENT_PHASE_COMPLETE, \ - INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - node::tracing::kGlobalScope, node::tracing::kNoId, \ - node::tracing::kNoId, TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \ - INTERNAL_TRACE_EVENT_UID(tracer) \ - .Initialize(INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - h); \ - } - -#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, \ - bind_id, flow_flags, ...) \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - node::tracing::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \ - if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ - unsigned int trace_event_flags = flow_flags; \ - node::tracing::TraceID trace_event_bind_id(bind_id, \ - &trace_event_flags); \ - uint64_t h = node::tracing::AddTraceEvent( \ - TRACE_EVENT_PHASE_COMPLETE, \ - INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - node::tracing::kGlobalScope, node::tracing::kNoId, \ - trace_event_bind_id.raw_id(), trace_event_flags, ##__VA_ARGS__); \ - INTERNAL_TRACE_EVENT_UID(tracer) \ - .Initialize(INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - h); \ - } - -// Implementation detail: internal macro to create static category and add -// event if the category is enabled. -#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category_group, name, id, \ - flags, ...) \ - do { \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ - unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ - node::tracing::TraceID trace_event_trace_id(id, \ - &trace_event_flags); \ - node::tracing::AddTraceEvent( \ - phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \ - node::tracing::kNoId, trace_event_flags, ##__VA_ARGS__); \ - } \ - } while (0) - -// Adds a trace event with a given timestamp. -#define INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(phase, category_group, name, \ - timestamp, flags, ...) \ - do { \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ - node::tracing::AddTraceEventWithTimestamp( \ - phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - node::tracing::kGlobalScope, node::tracing::kNoId, \ - node::tracing::kNoId, flags, timestamp, ##__VA_ARGS__); \ - } \ - } while (0) - -// Adds a trace event with a given id and timestamp. Not Implemented. -#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_AND_TIMESTAMP( \ - phase, category_group, name, id, timestamp, flags, ...) \ - UNIMPLEMENTED() - -// Adds a trace event with a given id, thread_id, and timestamp. Not -// Implemented. -#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ - phase, category_group, name, id, thread_id, timestamp, flags, ...) \ - do { \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ - unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ - node::tracing::TraceID trace_event_trace_id(id, \ - &trace_event_flags); \ - node::tracing::AddTraceEventWithTimestamp( \ - phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \ - node::tracing::kNoId, trace_event_flags, timestamp, ##__VA_ARGS__);\ - } \ - } while (0) - -#define INTERNAL_TRACE_EVENT_METADATA_ADD(category_group, name, ...) \ - do { \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ - TRACE_EVENT_API_ADD_METADATA_EVENT( \ - INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ - ##__VA_ARGS__); \ - } \ - } while(0) - -// Enter and leave a context based on the current scope. -#define INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context) \ - struct INTERNAL_TRACE_EVENT_UID(ScopedContext) { \ - public: \ - INTERNAL_TRACE_EVENT_UID(ScopedContext)(uint64_t cid) : cid_(cid) { \ - TRACE_EVENT_ENTER_CONTEXT(category_group, name, cid_); \ - } \ - ~INTERNAL_TRACE_EVENT_UID(ScopedContext)() { \ - TRACE_EVENT_LEAVE_CONTEXT(category_group, name, cid_); \ - } \ - \ - private: \ - /* Local class friendly DISALLOW_COPY_AND_ASSIGN */ \ - INTERNAL_TRACE_EVENT_UID(ScopedContext) \ - (const INTERNAL_TRACE_EVENT_UID(ScopedContext)&) {} \ - void operator=(const INTERNAL_TRACE_EVENT_UID(ScopedContext)&) {} \ - uint64_t cid_; \ - }; \ - INTERNAL_TRACE_EVENT_UID(ScopedContext) \ - INTERNAL_TRACE_EVENT_UID(scoped_context)(context); - -namespace node { -namespace tracing { - -// Specify these values when the corresponding argument of AddTraceEvent is not -// used. -const int kZeroNumArgs = 0; -const decltype(nullptr) kGlobalScope = nullptr; -const uint64_t kNoId = 0; - -class TraceEventHelper { - public: - static v8::TracingController* GetTracingController(); - static void SetTracingController(v8::TracingController* controller); - - static inline const uint8_t* GetCategoryGroupEnabled(const char* group) { - v8::TracingController* controller = GetTracingController(); - static const uint8_t disabled = 0; - if (controller == nullptr) [[unlikely]] { - return &disabled; - } - return controller->GetCategoryGroupEnabled(group); - } -}; - -// TraceID encapsulates an ID that can either be an integer or pointer. Pointers -// are by default mangled with the Process ID so that they are unlikely to -// collide when the same pointer is used on different processes. -class TraceID { - public: - class WithScope { - public: - WithScope(const char* scope, uint64_t raw_id) - : scope_(scope), raw_id_(raw_id) {} - uint64_t raw_id() const { return raw_id_; } - const char* scope() const { return scope_; } - - private: - const char* scope_ = nullptr; - uint64_t raw_id_; - }; - - class DontMangle { - public: - explicit DontMangle(const void* raw_id) - : raw_id_(static_cast(reinterpret_cast(raw_id))) {} - explicit DontMangle(uint64_t raw_id) : raw_id_(raw_id) {} - explicit DontMangle(unsigned int raw_id) : raw_id_(raw_id) {} - explicit DontMangle(uint16_t raw_id) : raw_id_(raw_id) {} - explicit DontMangle(unsigned char raw_id) : raw_id_(raw_id) {} - explicit DontMangle(int64_t raw_id) - : raw_id_(static_cast(raw_id)) {} - explicit DontMangle(int raw_id) : raw_id_(static_cast(raw_id)) {} - explicit DontMangle(int16_t raw_id) - : raw_id_(static_cast(raw_id)) {} - explicit DontMangle(signed char raw_id) - : raw_id_(static_cast(raw_id)) {} - explicit DontMangle(WithScope scoped_id) - : scope_(scoped_id.scope()), raw_id_(scoped_id.raw_id()) {} - const char* scope() const { return scope_; } - uint64_t raw_id() const { return raw_id_; } - - private: - const char* scope_ = nullptr; - uint64_t raw_id_; - }; - - class ForceMangle { - public: - explicit ForceMangle(uint64_t raw_id) : raw_id_(raw_id) {} - explicit ForceMangle(unsigned int raw_id) : raw_id_(raw_id) {} - explicit ForceMangle(uint16_t raw_id) : raw_id_(raw_id) {} - explicit ForceMangle(unsigned char raw_id) : raw_id_(raw_id) {} - explicit ForceMangle(int64_t raw_id) - : raw_id_(static_cast(raw_id)) {} - explicit ForceMangle(int raw_id) : raw_id_(static_cast(raw_id)) {} - explicit ForceMangle(int16_t raw_id) - : raw_id_(static_cast(raw_id)) {} - explicit ForceMangle(signed char raw_id) - : raw_id_(static_cast(raw_id)) {} - uint64_t raw_id() const { return raw_id_; } - - private: - uint64_t raw_id_; - }; - - TraceID(const void* raw_id, unsigned int* flags) - : raw_id_(static_cast(reinterpret_cast(raw_id))) { - *flags |= TRACE_EVENT_FLAG_MANGLE_ID; - } - TraceID(ForceMangle raw_id, unsigned int* flags) : raw_id_(raw_id.raw_id()) { - *flags |= TRACE_EVENT_FLAG_MANGLE_ID; - } - TraceID(DontMangle maybe_scoped_id, unsigned int* flags) - : scope_(maybe_scoped_id.scope()), raw_id_(maybe_scoped_id.raw_id()) {} - TraceID(uint64_t raw_id, unsigned int* flags) : raw_id_(raw_id) { - (void)flags; - } - TraceID(unsigned int raw_id, unsigned int* flags) : raw_id_(raw_id) { - (void)flags; - } - TraceID(uint16_t raw_id, unsigned int* flags) : raw_id_(raw_id) { - (void)flags; - } - TraceID(unsigned char raw_id, unsigned int* flags) : raw_id_(raw_id) { - (void)flags; - } - TraceID(int64_t raw_id, unsigned int* flags) - : raw_id_(static_cast(raw_id)) { - (void)flags; - } - TraceID(int raw_id, unsigned int* flags) - : raw_id_(static_cast(raw_id)) { - (void)flags; - } - TraceID(int16_t raw_id, unsigned int* flags) - : raw_id_(static_cast(raw_id)) { - (void)flags; - } - TraceID(signed char raw_id, unsigned int* flags) - : raw_id_(static_cast(raw_id)) { - (void)flags; - } - TraceID(WithScope scoped_id, unsigned int* flags) - : scope_(scoped_id.scope()), raw_id_(scoped_id.raw_id()) {} - - uint64_t raw_id() const { return raw_id_; } - const char* scope() const { return scope_; } - - private: - const char* scope_ = nullptr; - uint64_t raw_id_; -}; - -// Simple union to store various types as uint64_t. -union TraceValueUnion { - bool as_bool; - uint64_t as_uint; - int64_t as_int; - double as_double; - const void* as_pointer; - const char* as_string; -}; - -// Simple container for const char* that should be copied instead of retained. -class TraceStringWithCopy { - public: - explicit TraceStringWithCopy(const char* str) : str_(str) {} - operator const char*() const { return str_; } - - private: - const char* str_; -}; - -static inline uint64_t AddTraceEventImpl( - char phase, const uint8_t* category_group_enabled, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, - const char** arg_names, const uint8_t* arg_types, - const uint64_t* arg_values, unsigned int flags) { - std::unique_ptr arg_convertibles[2]; - if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) { - arg_convertibles[0].reset(reinterpret_cast( - static_cast(arg_values[0]))); - } - if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) { - arg_convertibles[1].reset(reinterpret_cast( - static_cast(arg_values[1]))); - } - // DCHECK(num_args, 2); - v8::TracingController* controller = - node::tracing::TraceEventHelper::GetTracingController(); - if (controller == nullptr) return 0; - return controller->AddTraceEvent(phase, category_group_enabled, name, scope, id, - bind_id, num_args, arg_names, arg_types, - arg_values, arg_convertibles, flags); -} - -static V8_INLINE uint64_t AddTraceEventWithTimestampImpl( - char phase, const uint8_t* category_group_enabled, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, - const char** arg_names, const uint8_t* arg_types, - const uint64_t* arg_values, unsigned int flags, int64_t timestamp) { - std::unique_ptr arg_convertibles[2]; - if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) { - arg_convertibles[0].reset(reinterpret_cast( - static_cast(arg_values[0]))); - } - if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) { - arg_convertibles[1].reset(reinterpret_cast( - static_cast(arg_values[1]))); - } - // DCHECK_LE(num_args, 2); - v8::TracingController* controller = - node::tracing::TraceEventHelper::GetTracingController(); - if (controller == nullptr) return 0; - return controller->AddTraceEventWithTimestamp( - phase, category_group_enabled, name, scope, id, bind_id, num_args, - arg_names, arg_types, arg_values, arg_convertibles, flags, timestamp); -} - -static V8_INLINE void AddMetadataEventImpl( - const uint8_t* category_group_enabled, const char* name, int32_t num_args, - const char** arg_names, const uint8_t* arg_types, - const uint64_t* arg_values, unsigned int flags) { - std::unique_ptr arg_convertibles[2]; - if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) { - arg_convertibles[0].reset(reinterpret_cast( - static_cast(arg_values[0]))); - } - if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) { - arg_convertibles[1].reset(reinterpret_cast( - static_cast(arg_values[1]))); - } - node::tracing::Agent* agent = - node::tracing::Agent::GetInstance(); - if (agent == nullptr) return; - static_cast( - agent->GetTracingController()) - ->AddMetadataEvent(category_group_enabled, name, num_args, arg_names, - arg_types, arg_values, arg_convertibles, flags); -} - -// Define SetTraceValue for each allowed type. It stores the type and -// value in the return arguments. This allows this API to avoid declaring any -// structures so that it is portable to third_party libraries. -#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, union_member, \ - value_type_id) \ - static inline void SetTraceValue(actual_type arg, unsigned char* type, \ - uint64_t* value) { \ - TraceValueUnion type_value; \ - type_value.union_member = arg; \ - *type = value_type_id; \ - *value = type_value.as_uint; \ - } -// Simpler form for int types that can be safely casted. -#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, value_type_id) \ - static inline void SetTraceValue(actual_type arg, unsigned char* type, \ - uint64_t* value) { \ - *type = value_type_id; \ - *value = static_cast(arg); \ - } - -INTERNAL_DECLARE_SET_TRACE_VALUE_INT(uint64_t, TRACE_VALUE_TYPE_UINT) -INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned int, TRACE_VALUE_TYPE_UINT) -INTERNAL_DECLARE_SET_TRACE_VALUE_INT(uint16_t, TRACE_VALUE_TYPE_UINT) -INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned char, TRACE_VALUE_TYPE_UINT) -INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int64_t, TRACE_VALUE_TYPE_INT) -INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int, TRACE_VALUE_TYPE_INT) -INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int16_t, TRACE_VALUE_TYPE_INT) -INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT) -INTERNAL_DECLARE_SET_TRACE_VALUE(bool, as_bool, TRACE_VALUE_TYPE_BOOL) -INTERNAL_DECLARE_SET_TRACE_VALUE(double, as_double, TRACE_VALUE_TYPE_DOUBLE) -INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, as_pointer, - TRACE_VALUE_TYPE_POINTER) -INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string, - TRACE_VALUE_TYPE_STRING) -INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string, - TRACE_VALUE_TYPE_COPY_STRING) - -#undef INTERNAL_DECLARE_SET_TRACE_VALUE -#undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT - -static inline void SetTraceValue(v8::ConvertableToTraceFormat* convertable_value, - unsigned char* type, uint64_t* value) { - *type = TRACE_VALUE_TYPE_CONVERTABLE; - *value = static_cast(reinterpret_cast(convertable_value)); -} - -template -static inline typename std::enable_if< - std::is_convertible::value>::type -SetTraceValue(std::unique_ptr ptr, unsigned char* type, uint64_t* value) { - SetTraceValue(ptr.release(), type, value); -} - -// These AddTraceEvent template -// function is defined here instead of in the macro, because the arg_values -// could be temporary objects, such as std::string. In order to store -// pointers to the internal c_str and pass through to the tracing API, -// the arg_values must live throughout these procedures. - -static inline uint64_t AddTraceEvent(char phase, - const uint8_t* category_group_enabled, - const char* name, const char* scope, - uint64_t id, uint64_t bind_id, - unsigned int flags) { - return TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_group_enabled, name, - scope, id, bind_id, kZeroNumArgs, - nullptr, nullptr, nullptr, flags); -} - -template -static inline uint64_t AddTraceEvent( - char phase, const uint8_t* category_group_enabled, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, - const char* arg1_name, ARG1_TYPE&& arg1_val) { - const int num_args = 1; - uint8_t arg_type; - uint64_t arg_value; - SetTraceValue(std::forward(arg1_val), &arg_type, &arg_value); - return TRACE_EVENT_API_ADD_TRACE_EVENT( - phase, category_group_enabled, name, scope, id, bind_id, num_args, - &arg1_name, &arg_type, &arg_value, flags); -} - -template -static inline uint64_t AddTraceEvent( - char phase, const uint8_t* category_group_enabled, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, - const char* arg1_name, ARG1_TYPE&& arg1_val, const char* arg2_name, - ARG2_TYPE&& arg2_val) { - const int num_args = 2; - const char* arg_names[2] = {arg1_name, arg2_name}; - unsigned char arg_types[2]; - uint64_t arg_values[2]; - SetTraceValue(std::forward(arg1_val), &arg_types[0], - &arg_values[0]); - SetTraceValue(std::forward(arg2_val), &arg_types[1], - &arg_values[1]); - return TRACE_EVENT_API_ADD_TRACE_EVENT( - phase, category_group_enabled, name, scope, id, bind_id, num_args, - arg_names, arg_types, arg_values, flags); -} - -static V8_INLINE uint64_t AddTraceEventWithTimestamp( - char phase, const uint8_t* category_group_enabled, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, - int64_t timestamp) { - return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( - phase, category_group_enabled, name, scope, id, bind_id, kZeroNumArgs, - nullptr, nullptr, nullptr, flags, timestamp); -} - -template -static V8_INLINE uint64_t AddTraceEventWithTimestamp( - char phase, const uint8_t* category_group_enabled, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, - int64_t timestamp, const char* arg1_name, ARG1_TYPE&& arg1_val) { - const int num_args = 1; - uint8_t arg_type; - uint64_t arg_value; - SetTraceValue(std::forward(arg1_val), &arg_type, &arg_value); - return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( - phase, category_group_enabled, name, scope, id, bind_id, num_args, - &arg1_name, &arg_type, &arg_value, flags, timestamp); -} - -template -static V8_INLINE uint64_t AddTraceEventWithTimestamp( - char phase, const uint8_t* category_group_enabled, const char* name, - const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, - int64_t timestamp, const char* arg1_name, ARG1_TYPE&& arg1_val, - const char* arg2_name, ARG2_TYPE&& arg2_val) { - const int num_args = 2; - const char* arg_names[2] = {arg1_name, arg2_name}; - unsigned char arg_types[2]; - uint64_t arg_values[2]; - SetTraceValue(std::forward(arg1_val), &arg_types[0], - &arg_values[0]); - SetTraceValue(std::forward(arg2_val), &arg_types[1], - &arg_values[1]); - return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( - phase, category_group_enabled, name, scope, id, bind_id, num_args, - arg_names, arg_types, arg_values, flags, timestamp); -} - -template -static V8_INLINE void AddMetadataEvent( - const uint8_t* category_group_enabled, const char* name, - const char* arg1_name, ARG1_TYPE&& arg1_val) { - const int num_args = 1; - uint8_t arg_type; - uint64_t arg_value; - SetTraceValue(std::forward(arg1_val), &arg_type, &arg_value); - AddMetadataEventImpl( - category_group_enabled, name, num_args, &arg1_name, &arg_type, &arg_value, - TRACE_EVENT_FLAG_NONE); -} +#if defined(V8_USE_PERFETTO) -// Used by TRACE_EVENTx macros. Do not use directly. -class ScopedTracer { - public: - // Note: members of data_ intentionally left uninitialized. See Initialize. - ScopedTracer() : p_data_(nullptr) {} +#error "Perfetto is not supported" - ~ScopedTracer() { - if (p_data_ && *data_.category_group_enabled) - TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION( - data_.category_group_enabled, data_.name, data_.event_handle); - } +#else // defined(V8_USE_PERFETTO) - void Initialize(const uint8_t* category_group_enabled, const char* name, - uint64_t event_handle) { - data_.category_group_enabled = category_group_enabled; - data_.name = name; - data_.event_handle = event_handle; - p_data_ = &data_; - } +#define TRACING_CATEGORY_NODE "node" +#define TRACING_CATEGORY_NODE1(one) \ + TRACING_CATEGORY_NODE "," TRACING_CATEGORY_NODE "." #one +#define TRACING_CATEGORY_NODE2(one, two) \ + TRACING_CATEGORY_NODE "," TRACING_CATEGORY_NODE "." #one \ + "," TRACING_CATEGORY_NODE "." #one "." #two - private: - // This Data struct workaround is to avoid initializing all the members - // in Data during construction of this object, since this object is always - // constructed, even when tracing is disabled. If the members of Data were - // members of this class instead, compiler warnings occur about potential - // uninitialized accesses. - struct Data { - const uint8_t* category_group_enabled; - const char* name; - uint64_t event_handle; - }; - Data* p_data_; - Data data_; -}; +#include "tracing/trace_event_legacy_inl.h" -} // namespace tracing -} // namespace node +#endif #endif // SRC_TRACING_TRACE_EVENT_H_ diff --git a/src/tracing/trace_event.cc b/src/tracing/trace_event_helper.cc similarity index 93% rename from src/tracing/trace_event.cc rename to src/tracing/trace_event_helper.cc index 59306fafb3a1..9a23b25c1782 100644 --- a/src/tracing/trace_event.cc +++ b/src/tracing/trace_event_helper.cc @@ -1,4 +1,4 @@ -#include "tracing/trace_event.h" +#include "tracing/trace_event_helper.h" #include "node.h" namespace node { diff --git a/src/tracing/trace_event_helper.h b/src/tracing/trace_event_helper.h new file mode 100644 index 000000000000..a4c01a66e70f --- /dev/null +++ b/src/tracing/trace_event_helper.h @@ -0,0 +1,33 @@ +#ifndef SRC_TRACING_TRACE_EVENT_HELPER_H_ +#define SRC_TRACING_TRACE_EVENT_HELPER_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "v8-platform.h" + +namespace node::tracing { + +class TraceEventHelper { + public: + static v8::TracingController* GetTracingController(); + static void SetTracingController(v8::TracingController* controller); + + static inline const uint8_t* GetCategoryGroupEnabled(const char* group) { +#if !defined(V8_USE_PERFETTO) + v8::TracingController* controller = GetTracingController(); + static const uint8_t disabled = 0; + if (controller == nullptr) [[unlikely]] { + return &disabled; + } + return controller->GetCategoryGroupEnabled(group); +#else + return nullptr; +#endif + } +}; + +} // namespace node::tracing + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_TRACING_TRACE_EVENT_HELPER_H_ diff --git a/src/tracing/trace_event_common.h b/src/tracing/trace_event_legacy.h similarity index 99% rename from src/tracing/trace_event_common.h rename to src/tracing/trace_event_legacy.h index be1c68cfae92..92b2e88d4823 100644 --- a/src/tracing/trace_event_common.h +++ b/src/tracing/trace_event_legacy.h @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SRC_TRACE_EVENT_COMMON_H -#define SRC_TRACE_EVENT_COMMON_H +#ifndef SRC_TRACING_TRACE_EVENT_LEGACY_H_ +#define SRC_TRACING_TRACE_EVENT_LEGACY_H_ + +#ifdef V8_USE_PERFETTO +#error Perfetto is enabled. +#endif // This header file defines the set of trace_event macros without specifying // how the events actually get collected and stored. If you need to expose trace @@ -1106,4 +1110,4 @@ #define TRACE_EVENT_SCOPE_NAME_PROCESS ('p') #define TRACE_EVENT_SCOPE_NAME_THREAD ('t') -#endif // SRC_TRACE_EVENT_COMMON_H +#endif // SRC_TRACING_TRACE_EVENT_LEGACY_H_ diff --git a/src/tracing/trace_event_legacy_inl.h b/src/tracing/trace_event_legacy_inl.h new file mode 100644 index 000000000000..4fadb6d8e38f --- /dev/null +++ b/src/tracing/trace_event_legacy_inl.h @@ -0,0 +1,710 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SRC_TRACING_TRACE_EVENT_LEGACY_INL_H_ +#define SRC_TRACING_TRACE_EVENT_LEGACY_INL_H_ + +#ifdef V8_USE_PERFETTO +#error Perfetto is enabled. +#endif + +#include "v8-platform.h" +#include "tracing/agent_legacy.h" +#include "tracing/trace_event_helper.h" +#include "tracing/trace_event_legacy.h" +#include + +// This header file defines implementation details of how the trace macros in +// trace_event_common.h collect and store trace events. Anything not +// implementation-specific should go in trace_macros_common.h instead of here. + + +// The pointer returned from GetCategoryGroupEnabled() points to a +// value with zero or more of the following bits. Used in this class only. +// The TRACE_EVENT macros should only use the value as a bool. +// These values must be in sync with macro values in trace_log.h in +// chromium. +enum CategoryGroupEnabledFlags { + // Category group enabled for the recording mode. + kEnabledForRecording_CategoryGroupEnabledFlags = 1 << 0, + // Category group enabled by SetEventCallbackEnabled(). + kEnabledForEventCallback_CategoryGroupEnabledFlags = 1 << 2, + // Category group enabled to export events to ETW. + kEnabledForETWExport_CategoryGroupEnabledFlags = 1 << 3, +}; + +// By default, const char* argument values are assumed to have long-lived scope +// and will not be copied. Use this macro to force a const char* to be copied. +#define TRACE_STR_COPY(str) node::tracing::TraceStringWithCopy(str) + +// By default, uint64 ID argument values are not mangled with the Process ID in +// TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling. +#define TRACE_ID_MANGLE(id) node::tracing::TraceID::ForceMangle(id) + +// By default, pointers are mangled with the Process ID in TRACE_EVENT_ASYNC +// macros. Use this macro to prevent Process ID mangling. +#define TRACE_ID_DONT_MANGLE(id) node::tracing::TraceID::DontMangle(id) + +// By default, trace IDs are eventually converted to a single 64-bit number. Use +// this macro to add a scope string. +#define TRACE_ID_WITH_SCOPE(scope, id) \ + trace_event_internal::TraceID::WithScope(scope, id) + +#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \ + *INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \ + (kEnabledForRecording_CategoryGroupEnabledFlags | \ + kEnabledForEventCallback_CategoryGroupEnabledFlags) + +// The following macro has no implementation, but it needs to exist since +// it gets called from scoped trace events. It cannot call UNIMPLEMENTED() +// since an empty implementation is a valid one. +#define INTERNAL_TRACE_MEMORY(category, name) + +//////////////////////////////////////////////////////////////////////////////// +// Implementation specific tracing API definitions. + +// Get a pointer to the enabled state of the given trace category. Only +// long-lived literal strings should be given as the category group. The +// returned pointer can be held permanently in a local static for example. If +// the unsigned char is non-zero, tracing is enabled. If tracing is enabled, +// TRACE_EVENT_API_ADD_TRACE_EVENT can be called. It's OK if tracing is disabled +// between the load of the tracing state and the call to +// TRACE_EVENT_API_ADD_TRACE_EVENT, because this flag only provides an early out +// for best performance when tracing is disabled. +// const uint8_t* +// TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(const char* category_group) +#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \ + node::tracing::TraceEventHelper::GetCategoryGroupEnabled + +// Get the number of times traces have been recorded. This is used to implement +// the TRACE_EVENT_IS_NEW_TRACE facility. +// unsigned int TRACE_EVENT_API_GET_NUM_TRACES_RECORDED() +#define TRACE_EVENT_API_GET_NUM_TRACES_RECORDED UNIMPLEMENTED() + +// Add a trace event to the platform tracing system. +// uint64_t TRACE_EVENT_API_ADD_TRACE_EVENT( +// char phase, +// const uint8_t* category_group_enabled, +// const char* name, +// const char* scope, +// uint64_t id, +// uint64_t bind_id, +// int num_args, +// const char** arg_names, +// const uint8_t* arg_types, +// const uint64_t* arg_values, +// unsigned int flags) +#define TRACE_EVENT_API_ADD_TRACE_EVENT node::tracing::AddTraceEventImpl + +// Add a trace event to the platform tracing system. +// uint64_t TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( +// char phase, +// const uint8_t* category_group_enabled, +// const char* name, +// const char* scope, +// uint64_t id, +// uint64_t bind_id, +// int num_args, +// const char** arg_names, +// const uint8_t* arg_types, +// const uint64_t* arg_values, +// unsigned int flags, +// int64_t timestamp) +#define TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP \ + node::tracing::AddTraceEventWithTimestampImpl + +// Set the duration field of a COMPLETE trace event. +// void TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION( +// const uint8_t* category_group_enabled, +// const char* name, +// uint64_t id) +#define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION \ + if (auto controller = \ + node::tracing::TraceEventHelper::GetTracingController()) \ + controller->UpdateTraceEventDuration + +// Adds a metadata event to the trace log. The |AppendValueAsTraceFormat| method +// on the convertable value will be called at flush time. +// TRACE_EVENT_API_ADD_METADATA_EVENT( +// const unsigned char* category_group_enabled, +// const char* event_name, +// const char* arg_name, +// std::unique_ptr arg_value) +#define TRACE_EVENT_API_ADD_METADATA_EVENT node::tracing::AddMetadataEvent + +// Defines atomic operations used internally by the tracing system. +#define TRACE_EVENT_API_ATOMIC_WORD std::atomic +#define TRACE_EVENT_API_ATOMIC_WORD_VALUE intptr_t +#define TRACE_EVENT_API_ATOMIC_LOAD(var) (var).load() +#define TRACE_EVENT_API_ATOMIC_STORE(var, value) (var).store(value) + +//////////////////////////////////////////////////////////////////////////////// + +// Implementation detail: trace event macros create temporary variables +// to keep instrumentation overhead low. These macros give each temporary +// variable a unique name based on the line number to prevent name collisions. +#define INTERNAL_TRACE_EVENT_UID3(a, b) trace_event_unique_##a##b +#define INTERNAL_TRACE_EVENT_UID2(a, b) INTERNAL_TRACE_EVENT_UID3(a, b) +#define INTERNAL_TRACE_EVENT_UID(name_prefix) \ + INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__) + +// Implementation detail: internal macro to create static category. +// No barriers are needed, because this code is designed to operate safely +// even when the unsigned char* points to garbage data (which may be the case +// on processors without cache coherency). +// TODO(fmeawad): This implementation contradicts that we can have a different +// configuration for each isolate, +// https://code.google.com/p/v8/issues/detail?id=4563 +#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \ + category_group, atomic, category_group_enabled) \ + category_group_enabled = \ + reinterpret_cast(TRACE_EVENT_API_ATOMIC_LOAD(atomic)); \ + if (!category_group_enabled) { \ + category_group_enabled = \ + TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \ + TRACE_EVENT_API_ATOMIC_STORE( \ + atomic, reinterpret_cast( \ + category_group_enabled)); \ + } + +#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) \ + static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) {0}; \ + const uint8_t* INTERNAL_TRACE_EVENT_UID(category_group_enabled); \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \ + category_group, INTERNAL_TRACE_EVENT_UID(atomic), \ + INTERNAL_TRACE_EVENT_UID(category_group_enabled)); + +// Implementation detail: internal macro to create static category and add +// event if the category is enabled. +#define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + node::tracing::AddTraceEvent( \ + phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + node::tracing::kGlobalScope, node::tracing::kNoId, \ + node::tracing::kNoId, flags, ##__VA_ARGS__); \ + } \ + } while (0) + +// Implementation detail: internal macro to create static category and add begin +// event if the category is enabled. Also adds the end event when the scope +// ends. +#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, ...) \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + node::tracing::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + uint64_t h = node::tracing::AddTraceEvent( \ + TRACE_EVENT_PHASE_COMPLETE, \ + INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + node::tracing::kGlobalScope, node::tracing::kNoId, \ + node::tracing::kNoId, TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \ + INTERNAL_TRACE_EVENT_UID(tracer) \ + .Initialize(INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + h); \ + } + +#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, \ + bind_id, flow_flags, ...) \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + node::tracing::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + unsigned int trace_event_flags = flow_flags; \ + node::tracing::TraceID trace_event_bind_id(bind_id, \ + &trace_event_flags); \ + uint64_t h = node::tracing::AddTraceEvent( \ + TRACE_EVENT_PHASE_COMPLETE, \ + INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + node::tracing::kGlobalScope, node::tracing::kNoId, \ + trace_event_bind_id.raw_id(), trace_event_flags, ##__VA_ARGS__); \ + INTERNAL_TRACE_EVENT_UID(tracer) \ + .Initialize(INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + h); \ + } + +// Implementation detail: internal macro to create static category and add +// event if the category is enabled. +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category_group, name, id, \ + flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ + node::tracing::TraceID trace_event_trace_id(id, \ + &trace_event_flags); \ + node::tracing::AddTraceEvent( \ + phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \ + node::tracing::kNoId, trace_event_flags, ##__VA_ARGS__); \ + } \ + } while (0) + +// Adds a trace event with a given timestamp. +#define INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(phase, category_group, name, \ + timestamp, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + node::tracing::AddTraceEventWithTimestamp( \ + phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + node::tracing::kGlobalScope, node::tracing::kNoId, \ + node::tracing::kNoId, flags, timestamp, ##__VA_ARGS__); \ + } \ + } while (0) + +// Adds a trace event with a given id and timestamp. Not Implemented. +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_AND_TIMESTAMP( \ + phase, category_group, name, id, timestamp, flags, ...) \ + UNIMPLEMENTED() + +// Adds a trace event with a given id, thread_id, and timestamp. Not +// Implemented. +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + phase, category_group, name, id, thread_id, timestamp, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ + node::tracing::TraceID trace_event_trace_id(id, \ + &trace_event_flags); \ + node::tracing::AddTraceEventWithTimestamp( \ + phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \ + node::tracing::kNoId, trace_event_flags, timestamp, ##__VA_ARGS__);\ + } \ + } while (0) + +#define INTERNAL_TRACE_EVENT_METADATA_ADD(category_group, name, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + TRACE_EVENT_API_ADD_METADATA_EVENT( \ + INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + ##__VA_ARGS__); \ + } \ + } while(0) + +// Enter and leave a context based on the current scope. +#define INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context) \ + struct INTERNAL_TRACE_EVENT_UID(ScopedContext) { \ + public: \ + INTERNAL_TRACE_EVENT_UID(ScopedContext)(uint64_t cid) : cid_(cid) { \ + TRACE_EVENT_ENTER_CONTEXT(category_group, name, cid_); \ + } \ + ~INTERNAL_TRACE_EVENT_UID(ScopedContext)() { \ + TRACE_EVENT_LEAVE_CONTEXT(category_group, name, cid_); \ + } \ + \ + private: \ + /* Local class friendly DISALLOW_COPY_AND_ASSIGN */ \ + INTERNAL_TRACE_EVENT_UID(ScopedContext) \ + (const INTERNAL_TRACE_EVENT_UID(ScopedContext)&) {} \ + void operator=(const INTERNAL_TRACE_EVENT_UID(ScopedContext)&) {} \ + uint64_t cid_; \ + }; \ + INTERNAL_TRACE_EVENT_UID(ScopedContext) \ + INTERNAL_TRACE_EVENT_UID(scoped_context)(context); + +namespace node { +namespace tracing { + +// Specify these values when the corresponding argument of AddTraceEvent is not +// used. +const int kZeroNumArgs = 0; +const decltype(nullptr) kGlobalScope = nullptr; +const uint64_t kNoId = 0; + +// TraceID encapsulates an ID that can either be an integer or pointer. Pointers +// are by default mangled with the Process ID so that they are unlikely to +// collide when the same pointer is used on different processes. +class TraceID { + public: + class WithScope { + public: + WithScope(const char* scope, uint64_t raw_id) + : scope_(scope), raw_id_(raw_id) {} + uint64_t raw_id() const { return raw_id_; } + const char* scope() const { return scope_; } + + private: + const char* scope_ = nullptr; + uint64_t raw_id_; + }; + + class DontMangle { + public: + explicit DontMangle(const void* raw_id) + : raw_id_(static_cast(reinterpret_cast(raw_id))) {} + explicit DontMangle(uint64_t raw_id) : raw_id_(raw_id) {} + explicit DontMangle(unsigned int raw_id) : raw_id_(raw_id) {} + explicit DontMangle(uint16_t raw_id) : raw_id_(raw_id) {} + explicit DontMangle(unsigned char raw_id) : raw_id_(raw_id) {} + explicit DontMangle(int64_t raw_id) + : raw_id_(static_cast(raw_id)) {} + explicit DontMangle(int raw_id) : raw_id_(static_cast(raw_id)) {} + explicit DontMangle(int16_t raw_id) + : raw_id_(static_cast(raw_id)) {} + explicit DontMangle(signed char raw_id) + : raw_id_(static_cast(raw_id)) {} + explicit DontMangle(WithScope scoped_id) + : scope_(scoped_id.scope()), raw_id_(scoped_id.raw_id()) {} + const char* scope() const { return scope_; } + uint64_t raw_id() const { return raw_id_; } + + private: + const char* scope_ = nullptr; + uint64_t raw_id_; + }; + + class ForceMangle { + public: + explicit ForceMangle(uint64_t raw_id) : raw_id_(raw_id) {} + explicit ForceMangle(unsigned int raw_id) : raw_id_(raw_id) {} + explicit ForceMangle(uint16_t raw_id) : raw_id_(raw_id) {} + explicit ForceMangle(unsigned char raw_id) : raw_id_(raw_id) {} + explicit ForceMangle(int64_t raw_id) + : raw_id_(static_cast(raw_id)) {} + explicit ForceMangle(int raw_id) : raw_id_(static_cast(raw_id)) {} + explicit ForceMangle(int16_t raw_id) + : raw_id_(static_cast(raw_id)) {} + explicit ForceMangle(signed char raw_id) + : raw_id_(static_cast(raw_id)) {} + uint64_t raw_id() const { return raw_id_; } + + private: + uint64_t raw_id_; + }; + + TraceID(const void* raw_id, unsigned int* flags) + : raw_id_(static_cast(reinterpret_cast(raw_id))) { + *flags |= TRACE_EVENT_FLAG_MANGLE_ID; + } + TraceID(ForceMangle raw_id, unsigned int* flags) : raw_id_(raw_id.raw_id()) { + *flags |= TRACE_EVENT_FLAG_MANGLE_ID; + } + TraceID(DontMangle maybe_scoped_id, unsigned int* flags) + : scope_(maybe_scoped_id.scope()), raw_id_(maybe_scoped_id.raw_id()) {} + TraceID(uint64_t raw_id, unsigned int* flags) : raw_id_(raw_id) { + (void)flags; + } + TraceID(unsigned int raw_id, unsigned int* flags) : raw_id_(raw_id) { + (void)flags; + } + TraceID(uint16_t raw_id, unsigned int* flags) : raw_id_(raw_id) { + (void)flags; + } + TraceID(unsigned char raw_id, unsigned int* flags) : raw_id_(raw_id) { + (void)flags; + } + TraceID(int64_t raw_id, unsigned int* flags) + : raw_id_(static_cast(raw_id)) { + (void)flags; + } + TraceID(int raw_id, unsigned int* flags) + : raw_id_(static_cast(raw_id)) { + (void)flags; + } + TraceID(int16_t raw_id, unsigned int* flags) + : raw_id_(static_cast(raw_id)) { + (void)flags; + } + TraceID(signed char raw_id, unsigned int* flags) + : raw_id_(static_cast(raw_id)) { + (void)flags; + } + TraceID(WithScope scoped_id, unsigned int* flags) + : scope_(scoped_id.scope()), raw_id_(scoped_id.raw_id()) {} + + uint64_t raw_id() const { return raw_id_; } + const char* scope() const { return scope_; } + + private: + const char* scope_ = nullptr; + uint64_t raw_id_; +}; + +// Simple union to store various types as uint64_t. +union TraceValueUnion { + bool as_bool; + uint64_t as_uint; + int64_t as_int; + double as_double; + const void* as_pointer; + const char* as_string; +}; + +// Simple container for const char* that should be copied instead of retained. +class TraceStringWithCopy { + public: + explicit TraceStringWithCopy(const char* str) : str_(str) {} + operator const char*() const { return str_; } + + private: + const char* str_; +}; + +static inline uint64_t AddTraceEventImpl( + char phase, const uint8_t* category_group_enabled, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, unsigned int flags) { + std::unique_ptr arg_convertibles[2]; + if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) { + arg_convertibles[0].reset(reinterpret_cast( + static_cast(arg_values[0]))); + } + if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) { + arg_convertibles[1].reset(reinterpret_cast( + static_cast(arg_values[1]))); + } + // DCHECK(num_args, 2); + v8::TracingController* controller = + node::tracing::TraceEventHelper::GetTracingController(); + if (controller == nullptr) return 0; + return controller->AddTraceEvent(phase, category_group_enabled, name, scope, id, + bind_id, num_args, arg_names, arg_types, + arg_values, arg_convertibles, flags); +} + +static V8_INLINE uint64_t AddTraceEventWithTimestampImpl( + char phase, const uint8_t* category_group_enabled, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, unsigned int flags, int64_t timestamp) { + std::unique_ptr arg_convertibles[2]; + if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) { + arg_convertibles[0].reset(reinterpret_cast( + static_cast(arg_values[0]))); + } + if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) { + arg_convertibles[1].reset(reinterpret_cast( + static_cast(arg_values[1]))); + } + // DCHECK_LE(num_args, 2); + v8::TracingController* controller = + node::tracing::TraceEventHelper::GetTracingController(); + if (controller == nullptr) return 0; + return controller->AddTraceEventWithTimestamp( + phase, category_group_enabled, name, scope, id, bind_id, num_args, + arg_names, arg_types, arg_values, arg_convertibles, flags, timestamp); +} + +static V8_INLINE void AddMetadataEventImpl( + const uint8_t* category_group_enabled, const char* name, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, unsigned int flags) { + std::unique_ptr arg_convertibles[2]; + if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) { + arg_convertibles[0].reset(reinterpret_cast( + static_cast(arg_values[0]))); + } + if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) { + arg_convertibles[1].reset(reinterpret_cast( + static_cast(arg_values[1]))); + } + node::tracing::Agent* agent = + node::tracing::Agent::GetInstance(); + if (agent == nullptr) return; + static_cast( + agent->GetTracingController()) + ->AddMetadataEvent(category_group_enabled, name, num_args, arg_names, + arg_types, arg_values, arg_convertibles, flags); +} + +// Define SetTraceValue for each allowed type. It stores the type and +// value in the return arguments. This allows this API to avoid declaring any +// structures so that it is portable to third_party libraries. +#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, union_member, \ + value_type_id) \ + static inline void SetTraceValue(actual_type arg, unsigned char* type, \ + uint64_t* value) { \ + TraceValueUnion type_value; \ + type_value.union_member = arg; \ + *type = value_type_id; \ + *value = type_value.as_uint; \ + } +// Simpler form for int types that can be safely casted. +#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, value_type_id) \ + static inline void SetTraceValue(actual_type arg, unsigned char* type, \ + uint64_t* value) { \ + *type = value_type_id; \ + *value = static_cast(arg); \ + } + +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(uint64_t, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned int, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(uint16_t, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned char, TRACE_VALUE_TYPE_UINT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int64_t, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int16_t, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT) +INTERNAL_DECLARE_SET_TRACE_VALUE(bool, as_bool, TRACE_VALUE_TYPE_BOOL) +INTERNAL_DECLARE_SET_TRACE_VALUE(double, as_double, TRACE_VALUE_TYPE_DOUBLE) +INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, as_pointer, + TRACE_VALUE_TYPE_POINTER) +INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string, + TRACE_VALUE_TYPE_STRING) +INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string, + TRACE_VALUE_TYPE_COPY_STRING) + +#undef INTERNAL_DECLARE_SET_TRACE_VALUE +#undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT + +static inline void SetTraceValue(v8::ConvertableToTraceFormat* convertable_value, + unsigned char* type, uint64_t* value) { + *type = TRACE_VALUE_TYPE_CONVERTABLE; + *value = static_cast(reinterpret_cast(convertable_value)); +} + +template +static inline typename std::enable_if< + std::is_convertible::value>::type +SetTraceValue(std::unique_ptr ptr, unsigned char* type, uint64_t* value) { + SetTraceValue(ptr.release(), type, value); +} + +// These AddTraceEvent template +// function is defined here instead of in the macro, because the arg_values +// could be temporary objects, such as std::string. In order to store +// pointers to the internal c_str and pass through to the tracing API, +// the arg_values must live throughout these procedures. + +static inline uint64_t AddTraceEvent(char phase, + const uint8_t* category_group_enabled, + const char* name, const char* scope, + uint64_t id, uint64_t bind_id, + unsigned int flags) { + return TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_group_enabled, name, + scope, id, bind_id, kZeroNumArgs, + nullptr, nullptr, nullptr, flags); +} + +template +static inline uint64_t AddTraceEvent( + char phase, const uint8_t* category_group_enabled, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, + const char* arg1_name, ARG1_TYPE&& arg1_val) { + const int num_args = 1; + uint8_t arg_type; + uint64_t arg_value; + SetTraceValue(std::forward(arg1_val), &arg_type, &arg_value); + return TRACE_EVENT_API_ADD_TRACE_EVENT( + phase, category_group_enabled, name, scope, id, bind_id, num_args, + &arg1_name, &arg_type, &arg_value, flags); +} + +template +static inline uint64_t AddTraceEvent( + char phase, const uint8_t* category_group_enabled, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, + const char* arg1_name, ARG1_TYPE&& arg1_val, const char* arg2_name, + ARG2_TYPE&& arg2_val) { + const int num_args = 2; + const char* arg_names[2] = {arg1_name, arg2_name}; + unsigned char arg_types[2]; + uint64_t arg_values[2]; + SetTraceValue(std::forward(arg1_val), &arg_types[0], + &arg_values[0]); + SetTraceValue(std::forward(arg2_val), &arg_types[1], + &arg_values[1]); + return TRACE_EVENT_API_ADD_TRACE_EVENT( + phase, category_group_enabled, name, scope, id, bind_id, num_args, + arg_names, arg_types, arg_values, flags); +} + +static V8_INLINE uint64_t AddTraceEventWithTimestamp( + char phase, const uint8_t* category_group_enabled, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, + int64_t timestamp) { + return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( + phase, category_group_enabled, name, scope, id, bind_id, kZeroNumArgs, + nullptr, nullptr, nullptr, flags, timestamp); +} + +template +static V8_INLINE uint64_t AddTraceEventWithTimestamp( + char phase, const uint8_t* category_group_enabled, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, + int64_t timestamp, const char* arg1_name, ARG1_TYPE&& arg1_val) { + const int num_args = 1; + uint8_t arg_type; + uint64_t arg_value; + SetTraceValue(std::forward(arg1_val), &arg_type, &arg_value); + return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( + phase, category_group_enabled, name, scope, id, bind_id, num_args, + &arg1_name, &arg_type, &arg_value, flags, timestamp); +} + +template +static V8_INLINE uint64_t AddTraceEventWithTimestamp( + char phase, const uint8_t* category_group_enabled, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, + int64_t timestamp, const char* arg1_name, ARG1_TYPE&& arg1_val, + const char* arg2_name, ARG2_TYPE&& arg2_val) { + const int num_args = 2; + const char* arg_names[2] = {arg1_name, arg2_name}; + unsigned char arg_types[2]; + uint64_t arg_values[2]; + SetTraceValue(std::forward(arg1_val), &arg_types[0], + &arg_values[0]); + SetTraceValue(std::forward(arg2_val), &arg_types[1], + &arg_values[1]); + return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( + phase, category_group_enabled, name, scope, id, bind_id, num_args, + arg_names, arg_types, arg_values, flags, timestamp); +} + +template +static V8_INLINE void AddMetadataEvent( + const uint8_t* category_group_enabled, const char* name, + const char* arg1_name, ARG1_TYPE&& arg1_val) { + const int num_args = 1; + uint8_t arg_type; + uint64_t arg_value; + SetTraceValue(std::forward(arg1_val), &arg_type, &arg_value); + AddMetadataEventImpl( + category_group_enabled, name, num_args, &arg1_name, &arg_type, &arg_value, + TRACE_EVENT_FLAG_NONE); +} + +// Used by TRACE_EVENTx macros. Do not use directly. +class ScopedTracer { + public: + // Note: members of data_ intentionally left uninitialized. See Initialize. + ScopedTracer() : p_data_(nullptr) {} + + ~ScopedTracer() { + if (p_data_ && *data_.category_group_enabled) + TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION( + data_.category_group_enabled, data_.name, data_.event_handle); + } + + void Initialize(const uint8_t* category_group_enabled, const char* name, + uint64_t event_handle) { + data_.category_group_enabled = category_group_enabled; + data_.name = name; + data_.event_handle = event_handle; + p_data_ = &data_; + } + + private: + // This Data struct workaround is to avoid initializing all the members + // in Data during construction of this object, since this object is always + // constructed, even when tracing is disabled. If the members of Data were + // members of this class instead, compiler warnings occur about potential + // uninitialized accesses. + struct Data { + const uint8_t* category_group_enabled; + const char* name; + uint64_t event_handle; + }; + Data* p_data_; + Data data_; +}; + +} // namespace tracing +} // namespace node + +#endif // SRC_TRACING_TRACE_EVENT_LEGACY_INL_H_ From e018f9a4a19f303e8d3c42efa54a57da2b6eeeeb Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Fri, 19 Jun 2026 17:50:27 -0400 Subject: [PATCH 022/152] src: add perfetto trace agent Signed-off-by: Chengzhong Wu PR-URL: https://github.com/nodejs/node/pull/64565 Refs: https://github.com/nodejs/diagnostics/issues/654 Reviewed-By: James M Snell Reviewed-By: Ryuhei Shima --- node.gyp | 4 + src/inspector/node_inspector.gypi | 13 +- src/node_options.h | 4 + src/tracing/agent.cc | 12 +- src/tracing/agent.h | 5 +- src/tracing/agent_perfetto.cc | 440 ++++++++++++++++++ src/tracing/agent_perfetto.h | 145 ++++++ src/tracing/trace_event.h | 8 +- src/tracing/trace_event_perfetto.cc | 5 + src/tracing/trace_event_perfetto.h | 47 ++ src/tracing/traced_value.cc | 16 + src/tracing/traced_value.h | 38 ++ .../test-trace-events-perfetto-pftrace.js | 28 ++ 13 files changed, 759 insertions(+), 6 deletions(-) create mode 100644 src/tracing/agent_perfetto.cc create mode 100644 src/tracing/agent_perfetto.h create mode 100644 src/tracing/trace_event_perfetto.cc create mode 100644 src/tracing/trace_event_perfetto.h create mode 100644 test/parallel/test-trace-events-perfetto-pftrace.js diff --git a/node.gyp b/node.gyp index 021a77b61f8b..162b4c0654d9 100644 --- a/node.gyp +++ b/node.gyp @@ -444,6 +444,10 @@ 'src/node_crypto.h', ], 'node_tracing_perfetto_sources': [ + 'src/tracing/agent_perfetto.cc', + 'src/tracing/agent_perfetto.h', + 'src/tracing/trace_event_perfetto.cc', + 'src/tracing/trace_event_perfetto.h', ], 'node_tracing_legacy_sources': [ 'src/tracing/agent_legacy.cc', diff --git a/src/inspector/node_inspector.gypi b/src/inspector/node_inspector.gypi index a493f59465d2..88e8a2f401ca 100644 --- a/src/inspector/node_inspector.gypi +++ b/src/inspector/node_inspector.gypi @@ -24,8 +24,6 @@ 'src/inspector/protocol_helper.h', 'src/inspector/runtime_agent.cc', 'src/inspector/runtime_agent.h', - 'src/inspector/tracing_agent.cc', - 'src/inspector/tracing_agent.h', 'src/inspector/worker_agent.cc', 'src/inspector/worker_agent.h', 'src/inspector/network_inspector.cc', @@ -51,6 +49,10 @@ 'src/inspector/notification_emitter.h', 'src/inspector/notification_emitter.cc', ], + 'node_inspector_without_perfetto_sources': [ + 'src/inspector/tracing_agent.cc', + 'src/inspector/tracing_agent.h', + ], 'node_inspector_generated_sources': [ '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Forward.h', '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Protocol.cpp', @@ -185,4 +187,11 @@ ], }, ], + 'conditions': [ + ['v8_use_perfetto!=1', { + 'sources': [ + '<@(node_inspector_without_perfetto_sources)', + ], + }], + ], } diff --git a/src/node_options.h b/src/node_options.h index 5b740b5ae6b8..aebdaeb88648 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -333,7 +333,11 @@ class PerProcessOptions : public Options { std::string title; std::string trace_event_categories; +#if defined(V8_USE_PERFETTO) + std::string trace_event_file_pattern = "node_trace.${rotation}.pftrace"; +#else std::string trace_event_file_pattern = "node_trace.${rotation}.log"; +#endif int64_t v8_thread_pool_size = 4; bool zero_fill_all_buffers = false; bool debug_arraybuffer_allocations = false; diff --git a/src/tracing/agent.cc b/src/tracing/agent.cc index 69b956e9bc9d..2ffc49f31e6a 100644 --- a/src/tracing/agent.cc +++ b/src/tracing/agent.cc @@ -1,6 +1,12 @@ #include "tracing/agent.h" + +#ifdef V8_USE_PERFETTO +#include "tracing/agent_perfetto.h" +#else #include "tracing/agent_legacy.h" -#include "tracing/trace_event.h" +#endif + +#include "tracing/trace_event_helper.h" namespace node { namespace tracing { @@ -23,7 +29,11 @@ void Agent::Deleter::operator()(Agent* agent) { std::unique_ptr Agent::CreateDefault() { CHECK_NULL(g_agent); +#ifdef V8_USE_PERFETTO + auto agent = new PerfettoTracingAgent(); +#else auto agent = new LegacyTracingAgent(); +#endif g_agent = agent; TraceEventHelper::SetTracingController(agent->GetTracingController()); diff --git a/src/tracing/agent.h b/src/tracing/agent.h index 78203154c099..60ed6ebf5a8d 100644 --- a/src/tracing/agent.h +++ b/src/tracing/agent.h @@ -43,9 +43,9 @@ class Agent { virtual AgentWriterHandle* GetDefaultWriterHandle() = 0; virtual void AddTraceStateObserver( - v8::TracingController::TraceStateObserver* observer) = 0; + v8::TracingController::TraceStateObserver* observer) {} virtual void RemoveTraceStateObserver( - v8::TracingController::TraceStateObserver* observer) = 0; + v8::TracingController::TraceStateObserver* observer) {} struct Deleter { void operator()(Agent* agent); @@ -88,6 +88,7 @@ class AgentWriterHandle { friend class Agent; friend class LegacyTracingAgent; + friend class PerfettoTracingAgent; }; void AgentWriterHandle::reset() { diff --git a/src/tracing/agent_perfetto.cc b/src/tracing/agent_perfetto.cc new file mode 100644 index 000000000000..1b1f3852a634 --- /dev/null +++ b/src/tracing/agent_perfetto.cc @@ -0,0 +1,440 @@ +#include "tracing/agent_perfetto.h" + +#include +#include +#include "debug_utils-inl.h" +#include "env-inl.h" +#include "node_options.h" +#include "trace_event.h" + +#include "trace_event_perfetto.h" + +namespace node { +namespace tracing { +namespace { + +// Perfetto's file writer drains buffers on a fixed period (default 5s) instead +// of continuously; mirror that cadence here. +constexpr uint64_t kReadPeriodMs = 5000; + +// Rotate to a new file once the current one reaches this size, so no single +// trace file grows without bound. +constexpr uint64_t kMaxFileSizeBytes = 64 * 1024 * 1024; // 64 MiB + +void replace_substring(std::string* target, + std::string_view search, + std::string_view insert) { + size_t pos = target->find(search); + for (; pos != std::string::npos; pos = target->find(search, pos)) { + target->replace(pos, search.size(), insert); + pos += insert.size(); + } +} + +std::set flatten( + const std::unordered_map>& map) { + std::set result; + for (const auto& id_value : map) + result.insert(id_value.second.begin(), id_value.second.end()); + return result; +} + +} // namespace + +// Writes trace chunks to a file, rotating by size. It deliberately uses the +// synchronous uv_fs_* APIs, mirroring Perfetto's internal file writer which +// does blocking writev() on its own service thread. +// +// Synchronous writes are the right fit here, not just simpler: +// - These writes run on the dedicated tracing loop thread, so blocking on +// disk stalls neither the application's main thread nor Perfetto's internal +// thread. +// - The buffer is drained periodically (see kReadPeriodMs) and each chunk is +// already contiguous, so there is little I/O to overlap; the thread-pool +// offload of async uv_fs_* would buy little. +// - It keeps buffer lifetime, write ordering, flushing, rotation, and +// shutdown trivial. Async writes would require keeping each buffer alive +// until its callback, serializing to one in-flight write per fd, and a +// condition-variable wait for blocking Flush() (see NodeTraceWriter). +class SimpleWriter : public TraceWriter { + public: + explicit SimpleWriter(std::string log_file_pattern) + : log_file_pattern_(std::move(log_file_pattern)) {} + + ~SimpleWriter() override { + if (fd_ < 0) return; + uv_fs_t req; + uv_fs_close(loop_, &req, fd_, nullptr); + uv_fs_req_cleanup(&req); + } + + void InitializeOnThread(uv_loop_t* loop) override { + loop_ = loop; + OpenNewFileForStreaming(); + } + + // Synchronously writes a chunk, rotating to a new file once it grows too + // large. See the class comment for why the write is synchronous. + void AppendTraceChunk(std::vector chunk) override { + if (fd_ < 0) return; + uv_buf_t buf = + uv_buf_init(chunk.data(), static_cast(chunk.size())); + uv_fs_t req; + int written = uv_fs_write(loop_, &req, fd_, &buf, 1, -1, nullptr); + uv_fs_req_cleanup(&req); + if (written < 0) return; + + bytes_written_ += written; + // Each chunk holds only whole trace packets, so rotating on a chunk + // boundary leaves every file a self-contained, valid trace. + if (bytes_written_ >= kMaxFileSizeBytes) OpenNewFileForStreaming(); + } + + void Flush(bool blocking) override { + if (fd_ < 0) return; + uv_fs_t req; + uv_fs_fsync(loop_, &req, fd_, nullptr); + uv_fs_req_cleanup(&req); + } + + private: + // Opens the next rotation file, evaluating a JS-style template that accepts + // ${pid} and ${rotation}, mirroring NodeTraceWriter::OpenNewFileForStreaming. + void OpenNewFileForStreaming() { + ++file_num_; + uv_fs_t req; + + std::string filepath(log_file_pattern_); + replace_substring(&filepath, "${pid}", std::to_string(uv_os_getpid())); + replace_substring(&filepath, "${rotation}", std::to_string(file_num_)); + + if (fd_ >= 0) { + uv_fs_close(loop_, &req, fd_, nullptr); + uv_fs_req_cleanup(&req); + } + + fd_ = uv_fs_open(loop_, + &req, + filepath.c_str(), + UV_FS_O_CREAT | UV_FS_O_WRONLY | UV_FS_O_TRUNC, + 0644, + nullptr); + uv_fs_req_cleanup(&req); + if (fd_ < 0) { + fprintf(stderr, + "Could not open trace file %s: %s\n", + filepath.c_str(), + uv_strerror(fd_)); + fd_ = -1; + } + bytes_written_ = 0; + } + + std::string log_file_pattern_; + uv_loop_t* loop_ = nullptr; + uv_file fd_ = -1; + int file_num_ = 0; + uint64_t bytes_written_ = 0; +}; + +void PerfettoSessionReader::Deleter::operator()( + PerfettoSessionReader* ptr) const noexcept { + ptr->tracing_session_->FlushBlocking(); + ptr->Read(); + ptr->tracing_session_->Stop(); +} + +PerfettoSessionReader::PerfettoSessionReader( + perfetto::TraceConfig config, std::unique_ptr writer) + : writer_(std::move(writer)) { + tracing_session_ = + perfetto::Tracing::NewTrace(perfetto::BackendType::kUnspecifiedBackend); + tracing_session_->Setup(config); + tracing_session_->SetOnStopCallback( + std::bind(&PerfettoSessionReader::SessionStopCallback, this)); + tracing_session_->StartBlocking(); +} + +PerfettoSessionReader::~PerfettoSessionReader() {} + +void PerfettoSessionReader::ChangeTraceConfig(perfetto::TraceConfig config) { + tracing_session_->ChangeTraceConfig(config); +} + +void PerfettoSessionReader::InitializeOnThread(uv_loop_t* loop) { + tracing_loop_ = loop; + CHECK_EQ(uv_async_init(tracing_loop_, &read_async_, OnReadAsync), 0); + read_async_.data = this; + CHECK_EQ(uv_timer_init(tracing_loop_, &read_timer_), 0); + read_timer_.data = this; + writer_->InitializeOnThread(loop); + + // Drain the trace buffer periodically instead of continuously, mirroring + // Perfetto's file writer which reschedules ReadBuffersIntoFile() every + // write_period_ms. + CHECK_EQ( + uv_timer_start(&read_timer_, OnReadTimer, kReadPeriodMs, kReadPeriodMs), + 0); +} + +void PerfettoSessionReader::Read() { + if (stop_requested_) return; + // Skip if a previous periodic read is still draining the buffer. + bool expected = false; + if (!read_in_progress_.compare_exchange_strong(expected, true)) return; + tracing_session_->ReadTrace(std::bind( + &PerfettoSessionReader::ReadTraceCallback, this, std::placeholders::_1)); +} + +void PerfettoSessionReader::ReadTraceCallback( + perfetto::TracingSession::ReadTraceCallbackArgs args) { + // On Perfetto internal thread. + { + Mutex::ScopedLock lock(chunks_mutex_); + if (args.size > 0) + pending_chunks_.emplace_back(args.data, args.data + args.size); + } + // A single ReadTrace() cycle can yield multiple callbacks; the last one has + // has_more == false, which clears read_in_progress_ so the next timer tick + // can start a new read. + read_in_progress_ = args.has_more; + uv_async_send(&read_async_); +} + +void PerfettoSessionReader::SessionStopCallback() { + stop_requested_ = true; + uv_async_send(&read_async_); +} + +// static +void PerfettoSessionReader::OnReadAsync(uv_async_t* async) { + PerfettoSessionReader* reader = + static_cast(async->data); + std::list> chunks_to_write; + { + Mutex::ScopedLock lock(reader->chunks_mutex_); + std::swap(chunks_to_write, reader->pending_chunks_); + } + + while (!chunks_to_write.empty()) { + std::vector& chunk = chunks_to_write.front(); + reader->writer_->AppendTraceChunk(std::move(chunk)); + chunks_to_write.pop_front(); + } + + if (reader->stop_requested_ && reader->handles_pending_close_ == 0) { + reader->writer_->Flush(true); + + reader->handles_pending_close_ = 2; + uv_timer_stop(&reader->read_timer_); + uv_close(reinterpret_cast(&reader->read_async_), + OnHandleClose); + uv_close(reinterpret_cast(&reader->read_timer_), + OnHandleClose); + } +} + +// static +void PerfettoSessionReader::OnReadTimer(uv_timer_t* timer) { + PerfettoSessionReader* reader = + static_cast(timer->data); + reader->Read(); +} + +// static +void PerfettoSessionReader::OnHandleClose(uv_handle_t* handle) { + PerfettoSessionReader* reader = + static_cast(handle->data); + if (--reader->handles_pending_close_ == 0) delete reader; +} + +PerfettoTracingAgent::PerfettoTracingAgent() { + CHECK_EQ(uv_loop_init(&tracing_loop_), 0); + CHECK_EQ(uv_async_init(&tracing_loop_, + &initialize_writer_async_, + [](uv_async_t* async) { + PerfettoTracingAgent* agent = ContainerOf( + &PerfettoTracingAgent::initialize_writer_async_, + async); + agent->InitializeWritersOnThread(); + }), + 0); + + // Set up the in-process backend that the tracing controller will connect + // to. + perfetto::TracingInitArgs init_args; + init_args.backends = perfetto::BackendType::kInProcessBackend; + perfetto::Tracing::Initialize(init_args); + + node::TrackEvent::Register(); +} + +void PerfettoTracingAgent::InitializeWritersOnThread() { + Mutex::ScopedLock lock(initialize_writer_mutex_); + while (!to_be_initialized_.empty()) { + auto head = *to_be_initialized_.begin(); + head->InitializeOnThread(&tracing_loop_); + to_be_initialized_.erase(head); + } + initialize_writer_condvar_.Broadcast(lock); + + uv_unref(reinterpret_cast(&initialize_writer_async_)); +} + +PerfettoTracingAgent::~PerfettoTracingAgent() { + StopTracing(); + + categories_.clear(); + writers_.clear(); + + uv_close(reinterpret_cast(&initialize_writer_async_), nullptr); + uv_run(&tracing_loop_, UV_RUN_ONCE); + CheckedUvLoopClose(&tracing_loop_); +} + +void PerfettoTracingAgent::Start() { + if (started_) return; + + // This thread should be created *after* async handles are created + // (within NodeTraceWriter and NodeTraceBuffer constructors). + // Otherwise the thread could shut down prematurely. + CHECK_EQ(0, + uv_thread_create( + &thread_, + [](void* arg) { + uv_thread_setname("TraceEventWorker"); + PerfettoTracingAgent* agent = + static_cast(arg); + uv_run(&agent->tracing_loop_, UV_RUN_DEFAULT); + }, + this)); + + started_ = true; +} + +AgentWriterHandle* PerfettoTracingAgent::GetDefaultWriterHandle() { + return tracing_file_writer_.has_value() ? &tracing_file_writer_.value() + : nullptr; +} + +AgentWriterHandle PerfettoTracingAgent::AddClient( + const std::set& categories, + std::unique_ptr writer, + enum UseDefaultCategoryMode mode) { + Start(); + + const std::set* use_categories = &categories; + + std::set categories_with_default; + if (mode == kUseDefaultCategories) { + categories_with_default.insert(categories.begin(), categories.end()); + categories_with_default.insert(categories_[kDefaultHandleId].begin(), + categories_[kDefaultHandleId].end()); + use_categories = &categories_with_default; + } + + int id = next_writer_id_++; + PerfettoSessionReader::Ptr reader = PerfettoSessionReader::Create( + CreateTraceConfig({use_categories->begin(), use_categories->end()}), + std::move(writer)); + + auto* raw = reader.get(); + writers_[id] = std::move(reader); + categories_[id] = {use_categories->begin(), use_categories->end()}; + + { + Mutex::ScopedLock lock(initialize_writer_mutex_); + to_be_initialized_.insert(raw); + uv_async_send(&initialize_writer_async_); + while (to_be_initialized_.count(raw) > 0) + initialize_writer_condvar_.Wait(lock); + } + + return AgentWriterHandle(this, id); +} + +void PerfettoTracingAgent::StartTracing(const std::string& categories) { + if (tracing_file_writer_.has_value()) return; + + using std::operator""sv; + auto parts = std::views::split(categories, ","sv); + + std::set categories_set; + for (const auto& s : parts) { + categories_set.emplace(std::string(s.data(), s.size())); + } + + tracing_file_writer_ = + AddClient(categories_set, + std::make_unique( + per_process::cli_options->trace_event_file_pattern), + kUseDefaultCategories); +} + +void PerfettoTracingAgent::StopTracing() { + if (!started_) return; + // Perform final Flush on TraceBuffer. We don't want the tracing controller + // to flush the buffer again on destruction of the V8::Platform. + writers_.clear(); + started_ = false; + + // Thread should finish when the tracing loop is stopped. + uv_thread_join(&thread_); +} + +void PerfettoTracingAgent::Disconnect(int client) { + if (client == kDefaultHandleId) return; + { + Mutex::ScopedLock lock(initialize_writer_mutex_); + to_be_initialized_.erase(writers_[client].get()); + } + + writers_.erase(client); + categories_.erase(client); +} + +void PerfettoTracingAgent::Enable(int id, + const std::set& categories) { + if (categories.empty()) return; + + categories_[id].insert(categories.begin(), categories.end()); + + writers_[id]->ChangeTraceConfig(CreateTraceConfig(categories_[id])); +} + +void PerfettoTracingAgent::Disable(int id, + const std::set& categories) { + std::multiset& writer_categories = categories_[id]; + for (const std::string& category : categories) { + auto it = writer_categories.find(category); + if (it != writer_categories.end()) writer_categories.erase(it); + } + + writers_[id]->ChangeTraceConfig(CreateTraceConfig(writer_categories)); +} + +std::string PerfettoTracingAgent::GetEnabledCategories() const { + std::string categories; + for (const std::string& category : flatten(categories_)) { + if (!categories.empty()) categories += ','; + categories += category; + } + return categories; +} + +perfetto::TraceConfig PerfettoTracingAgent::CreateTraceConfig( + std::multiset categories) const { + perfetto::TraceConfig perfetto_trace_config; + perfetto_trace_config.add_buffers()->set_size_kb(4096); + auto ds_config = perfetto_trace_config.add_data_sources()->mutable_config(); + ds_config->set_name("track_event"); + perfetto::protos::gen::TrackEventConfig te_config; + te_config.add_disabled_categories("*"); + for (const auto& category : categories) + te_config.add_enabled_categories(category); + ds_config->set_track_event_config_raw(te_config.SerializeAsString()); + return perfetto_trace_config; +} + +} // namespace tracing +} // namespace node diff --git a/src/tracing/agent_perfetto.h b/src/tracing/agent_perfetto.h new file mode 100644 index 000000000000..fc12b4d0f3f1 --- /dev/null +++ b/src/tracing/agent_perfetto.h @@ -0,0 +1,145 @@ +#ifndef SRC_TRACING_AGENT_PERFETTO_H_ +#define SRC_TRACING_AGENT_PERFETTO_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_mutex.h" +#include "tracing/agent.h" +#include "tracing/trace_event_perfetto.h" +#include "util.h" +#include "uv.h" +#include "v8-platform.h" + +#include +#include +#include +#include + +namespace node::tracing { + +// Consumes trace data produced by a PerfettoSessionReader. All methods run on +// the agent's dedicated background tracing thread (the loop passed to +// InitializeOnThread), never on the application's main thread or Perfetto's +// internal thread, so implementations may block (e.g. do synchronous I/O). +class TraceWriter { + public: + virtual ~TraceWriter() = default; + // 'chunk' is guaranteed to contain 1 or more full trace packets, which + // can be decoded using trace.proto. No partial or truncated packets are + // exposed. + virtual void AppendTraceChunk(std::vector chunk) = 0; + virtual void Flush(bool blocking) = 0; + virtual void InitializeOnThread(uv_loop_t* loop) {} +}; + +// Owns one Perfetto tracing session and pumps its trace data out to a +// TraceWriter using the consumer ReadTrace() API, reading on a fixed period. +// +// Threading: two threads are involved. +// - Perfetto's internal thread invokes ReadTraceCallback. It only copies the +// borrowed chunk into pending_chunks_ (under chunks_mutex_) and signals +// read_async_; it never touches the writer or the file. +// - The agent's dedicated tracing loop thread runs everything else. A +// repeating timer (read_timer_) starts a read every kReadPeriodMs, and +// OnReadAsync drains pending_chunks_ into the writer there. This is why the +// writer can safely block on synchronous I/O (see TraceWriter). +// read_in_progress_ prevents a timer tick from starting a new read while a +// previous ReadTrace() cycle is still delivering chunks. +class PerfettoSessionReader final { + public: + struct Deleter { + void operator()(PerfettoSessionReader* ptr) const noexcept; + }; + using Ptr = std::unique_ptr; + static Ptr Create(perfetto::TraceConfig config, + std::unique_ptr writer) { + return Ptr(new PerfettoSessionReader(config, std::move(writer))); + } + + void ChangeTraceConfig(perfetto::TraceConfig config); + + void InitializeOnThread(uv_loop_t* loop); + + private: + explicit PerfettoSessionReader(perfetto::TraceConfig config, + std::unique_ptr writer); + ~PerfettoSessionReader(); + void ReadTraceCallback(perfetto::TracingSession::ReadTraceCallbackArgs args); + void SessionStopCallback(); + void Read(); + + static void OnReadAsync(uv_async_t* async); + static void OnReadTimer(uv_timer_t* timer); + static void OnHandleClose(uv_handle_t* handle); + + uv_loop_t* tracing_loop_; + uv_async_t read_async_; + uv_timer_t read_timer_; + int handles_pending_close_ = 0; + std::atomic stop_requested_ = false; + std::atomic read_in_progress_ = false; + + Mutex chunks_mutex_; + std::list> pending_chunks_; + std::unique_ptr tracing_session_ = nullptr; + + std::unique_ptr writer_; +}; + +class PerfettoTracingAgent final : public Agent { + public: + enum UseDefaultCategoryMode { + kUseDefaultCategories, + kIgnoreDefaultCategories + }; + + PerfettoTracingAgent(); + ~PerfettoTracingAgent() override; + + v8::TracingController* GetTracingController() override { return nullptr; } + + AgentWriterHandle AddClient(const std::set& categories, + std::unique_ptr writer, + enum UseDefaultCategoryMode mode); + std::string GetEnabledCategories() const override; + + void StartTracing(const std::string& categories) override; + void StopTracing() override; + AgentWriterHandle* GetDefaultWriterHandle() override; + + private: + static constexpr int kDefaultHandleId = -1; + + void Disconnect(int client) override; + + void Enable(int id, const std::set& categories) override; + void Disable(int id, const std::set& categories) override; + + void InitializeWritersOnThread(); + void Start(); + + perfetto::TraceConfig CreateTraceConfig( + std::multiset categories) const; + + uv_thread_t thread_; + uv_loop_t tracing_loop_; + + bool started_ = false; + + int next_writer_id_ = 1; + std::unordered_map> categories_; + std::unordered_map writers_; + + Mutex initialize_writer_mutex_; + ConditionVariable initialize_writer_condvar_; + uv_async_t initialize_writer_async_; + std::set to_be_initialized_; + + std::optional tracing_file_writer_; +}; + +} // namespace node::tracing + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_TRACING_AGENT_PERFETTO_H_ diff --git a/src/tracing/trace_event.h b/src/tracing/trace_event.h index 21d158fb16fb..3169505626c0 100644 --- a/src/tracing/trace_event.h +++ b/src/tracing/trace_event.h @@ -1,9 +1,15 @@ #ifndef SRC_TRACING_TRACE_EVENT_H_ #define SRC_TRACING_TRACE_EVENT_H_ +#include "tracing/agent.h" + #if defined(V8_USE_PERFETTO) -#error "Perfetto is not supported" +#define TRACING_CATEGORY_NODE "node" +#define TRACING_CATEGORY_NODE1(one) TRACING_CATEGORY_NODE "." #one +#define TRACING_CATEGORY_NODE2(one, two) TRACING_CATEGORY_NODE "." #one "." #two + +#include "tracing/trace_event_perfetto.h" #else // defined(V8_USE_PERFETTO) diff --git a/src/tracing/trace_event_perfetto.cc b/src/tracing/trace_event_perfetto.cc new file mode 100644 index 000000000000..87227b95fd56 --- /dev/null +++ b/src/tracing/trace_event_perfetto.cc @@ -0,0 +1,5 @@ +#include "tracing/trace_event_perfetto.h" + +#if defined(V8_USE_PERFETTO) +PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(node); +#endif diff --git a/src/tracing/trace_event_perfetto.h b/src/tracing/trace_event_perfetto.h new file mode 100644 index 000000000000..60add492d36a --- /dev/null +++ b/src/tracing/trace_event_perfetto.h @@ -0,0 +1,47 @@ +#ifndef SRC_TRACING_TRACE_EVENT_PERFETTO_H_ +#define SRC_TRACING_TRACE_EVENT_PERFETTO_H_ + +#if !defined(V8_USE_PERFETTO) +#error Perfetto is not enabled. +#endif + +// For now most of Node.js uses legacy trace events. +#define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 1 + +#include "perfetto.h" + +#define TRACING_CATEGORY_NODE "node" +#define TRACING_CATEGORY_NODE1(one) TRACING_CATEGORY_NODE "." #one +#define TRACING_CATEGORY_NODE2(one, two) TRACING_CATEGORY_NODE "." #one "." #two + +// List of categories used by built-in Node.js trace events. +// clang-format off +PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE( + node, + perfetto::Category("__metadata"), // TODO(legendecas): remove this + perfetto::Category("node"), + perfetto::Category("node.async_hooks"), + perfetto::Category("node.environment"), + perfetto::Category("node.realm"), + perfetto::Category("node.bootstrap"), + perfetto::Category("node.dns.native"), + perfetto::Category("node.net.native"), + perfetto::Category("node.vm.script"), + perfetto::Category("node.fs_dir.sync"), + perfetto::Category("node.fs_dir.async"), + perfetto::Category("node.fs.sync"), + perfetto::Category("node.fs.async"), + perfetto::Category("node.perf.event_loop"), + perfetto::Category("node.promises.rejections"), + perfetto::Category("node.threadpoolwork.sync"), + perfetto::Category("node.threadpoolwork.async"), +// JavaScript namespaces + perfetto::Category("node.console"), + perfetto::Category("node.http"), + perfetto::Category("node.module_timer"), + ); // NOLINT(whitespace/parens) +// clang-format on + +PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(node); + +#endif // SRC_TRACING_TRACE_EVENT_PERFETTO_H_ diff --git a/src/tracing/traced_value.cc b/src/tracing/traced_value.cc index a7c9b9b5b30a..dcecbfd976ad 100644 --- a/src/tracing/traced_value.cc +++ b/src/tracing/traced_value.cc @@ -258,5 +258,21 @@ std::unique_ptr ProcessMeta::Cast() const { return trace_process; } +#if defined(V8_USE_PERFETTO) +void ProcessMeta::WriteIntoTrace(perfetto::TracedValue context) const { + auto dict = std::move(context).WriteDictionary(); + auto versions_dict = dict.AddDictionary("versions"); + for (const auto& version : per_process::metadata.versions.pairs()) { + versions_dict.Add(perfetto::DynamicString(std::string(version.first)), + version.second); + } + dict.Add("arch", per_process::metadata.arch.c_str()); + dict.Add("platform", per_process::metadata.platform.c_str()); + + auto release_dict = dict.AddDictionary("release"); + release_dict.Add("name", per_process::metadata.release.name.c_str()); +} +#endif + } // namespace tracing } // namespace node diff --git a/src/tracing/traced_value.h b/src/tracing/traced_value.h index 0bc9df81d875..e04ffff48efb 100644 --- a/src/tracing/traced_value.h +++ b/src/tracing/traced_value.h @@ -11,13 +11,24 @@ #include #include +#if defined(V8_USE_PERFETTO) +#include "tracing/trace_event_perfetto.h" +#endif + namespace node { namespace tracing { +#if defined(V8_USE_PERFETTO) +template +T CastTracedValue(const T& value) { + return value; +} +#else template std::unique_ptr CastTracedValue(const T& value) { return value.Cast(); } +#endif class EnvironmentArgs { public: @@ -27,6 +38,20 @@ class EnvironmentArgs { std::unique_ptr Cast() const; +#if defined(V8_USE_PERFETTO) + void WriteIntoTrace(perfetto::TracedValue context) const { + auto dict = std::move(context).WriteDictionary(); + auto args_array = dict.AddArray("args"); + for (const auto& arg : args_) { + args_array.Append(arg); + } + auto exec_args_array = dict.AddArray("exec_args"); + for (const auto& arg : exec_args_) { + exec_args_array.Append(arg); + } + } +#endif + private: std::span args_; std::span exec_args_; @@ -40,6 +65,15 @@ class AsyncWrapArgs { std::unique_ptr Cast() const; +#if defined(V8_USE_PERFETTO) + void WriteIntoTrace(perfetto::TracedValue context) const { + auto dict = std::move(context).WriteDictionary(); + dict.Add("executionAsyncId", execution_async_id_); + dict.Add("triggerAsyncId", trigger_async_id_); + dict.Add("foo", "bar"); + } +#endif + private: int64_t execution_async_id_; int64_t trigger_async_id_; @@ -48,6 +82,10 @@ class AsyncWrapArgs { class ProcessMeta { public: std::unique_ptr Cast() const; + +#if defined(V8_USE_PERFETTO) + void WriteIntoTrace(perfetto::TracedValue context) const; +#endif }; // Do not use this class directly. Define a custom structured class to provide diff --git a/test/parallel/test-trace-events-perfetto-pftrace.js b/test/parallel/test-trace-events-perfetto-pftrace.js new file mode 100644 index 000000000000..8cfcb52182df --- /dev/null +++ b/test/parallel/test-trace-events-perfetto-pftrace.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); + +if (!process.config.variables.v8_use_perfetto) + common.skip('perfetto tracing is not enabled'); + +const assert = require('assert'); +const cp = require('child_process'); +const fs = require('fs'); + +const CODE = + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +// Perfetto builds default the file pattern to a .pftrace extension. +const FILE_NAME = tmpdir.resolve('node_trace.1.pftrace'); + +const proc = cp.spawn(process.execPath, + [ '--trace-events-enabled', '-e', CODE ], + { cwd: tmpdir.path }); + +proc.once('exit', common.mustCall(() => { + assert(fs.existsSync(FILE_NAME)); + // The perfetto trace is a binary protobuf, so just check it has content. + const stat = fs.statSync(FILE_NAME); + assert(stat.size > 0); +})); From c25b8e33313ed9c1755b930b09ae32e8c6a94b3b Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Sat, 18 Jul 2026 23:45:16 +0200 Subject: [PATCH 023/152] readline: reduce createInterface overhead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Speed up Interface construction: - Hoist the history accessor property descriptors to module scope and define them with a single ObjectDefineProperties call, instead of allocating six closures and four descriptor objects per instance. - Stop assigning the history options onto the input stream. This avoids hidden class transitions on the user provided stream and no longer mutates it observably. - Only check process.env.TERM for a dumb terminal when the interface is in terminal mode. Reading process.env goes through the environment interceptor and is comparatively expensive, and _ttyWrite is never called when terminal is false. Signed-off-by: Matteo Collina PR-URL: https://github.com/nodejs/node/pull/64585 Reviewed-By: James M Snell Reviewed-By: Rafael Gonzaga Reviewed-By: Yagiz Nizipli Reviewed-By: Benjamin Gruenbaum Reviewed-By: Gürgün Dayıoğlu --- .../readline/readline-createInterface.js | 26 ++++ lib/internal/readline/interface.js | 120 ++++++++++-------- lib/readline.js | 4 +- 3 files changed, 97 insertions(+), 53 deletions(-) create mode 100644 benchmark/readline/readline-createInterface.js diff --git a/benchmark/readline/readline-createInterface.js b/benchmark/readline/readline-createInterface.js new file mode 100644 index 000000000000..06cd28175456 --- /dev/null +++ b/benchmark/readline/readline-createInterface.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common.js'); +const readline = require('readline'); +const { Readable, Writable } = require('stream'); + +const bench = common.createBenchmark(main, { + n: [1e5], + terminal: [0, 1], +}); + +function main({ n, terminal }) { + bench.start(); + for (let i = 0; i < n; i++) { + const input = new Readable({ read() {} }); + const output = new Writable({ write(chunk, encoding, callback) { + callback(); + } }); + const rl = readline.createInterface({ + input, + output, + terminal: Boolean(terminal), + }); + rl.close(); + } + bench.end(n); +} diff --git a/lib/internal/readline/interface.js b/lib/internal/readline/interface.js index 08f7aaa9e3e7..729892ff670d 100644 --- a/lib/internal/readline/interface.js +++ b/lib/internal/readline/interface.js @@ -17,9 +17,10 @@ const { MathMax, MathMaxApply, NumberIsFinite, - ObjectDefineProperty, + ObjectDefineProperties, ObjectSetPrototypeOf, RegExpPrototypeExec, + RegExpPrototypeSymbolSplit, SafeStringIterator, StringPrototypeCodePointAt, StringPrototypeEndsWith, @@ -172,6 +173,7 @@ function InterfaceConstructor(input, output, completer, terminal) { let crlfDelay; let prompt = '> '; let signal; + let historyOptions; if (input?.input) { // An options object was given @@ -210,12 +212,15 @@ function InterfaceConstructor(input, output, completer, terminal) { crlfDelay = input.crlfDelay; input = input.input; - input.size = historySize; - input.history = history; - input.removeHistoryDuplicates = removeHistoryDuplicates; + historyOptions = { + __proto__: null, + size: historySize, + history, + removeHistoryDuplicates, + }; } - this.setupHistoryManager(input); + this.setupHistoryManager(historyOptions ?? input); if (completer !== undefined && typeof completer !== 'function') { throw new ERR_INVALID_ARG_VALUE('completer', completer); @@ -358,6 +363,30 @@ function InterfaceConstructor(input, output, completer, terminal) { ObjectSetPrototypeOf(InterfaceConstructor.prototype, EventEmitter.prototype); ObjectSetPrototypeOf(InterfaceConstructor, EventEmitter); +// Shared descriptors for the history accessors defined on each instance. +// Hoisted to avoid allocating fresh closures on every construction. +const kHistoryAccessorDescriptors = { + __proto__: null, + history: { + __proto__: null, configurable: true, enumerable: true, + get() { return this.historyManager.history; }, + set(newHistory) { return this.historyManager.history = newHistory; }, + }, + historyIndex: { + __proto__: null, configurable: true, enumerable: true, + get() { return this.historyManager.index; }, + set(historyIndex) { return this.historyManager.index = historyIndex; }, + }, + historySize: { + __proto__: null, configurable: true, enumerable: true, + get() { return this.historyManager.size; }, + }, + isFlushing: { + __proto__: null, configurable: true, enumerable: true, + get() { return this.historyManager.isFlushing; }, + }, +}; + class Interface extends InterfaceConstructor { get columns() { if (this.output?.columns) return this.output.columns; @@ -388,27 +417,7 @@ class Interface extends InterfaceConstructor { this.historyManager.initialize(options.onHistoryFileLoaded); } - ObjectDefineProperty(this, 'history', { - __proto__: null, configurable: true, enumerable: true, - get() { return this.historyManager.history; }, - set(newHistory) { return this.historyManager.history = newHistory; }, - }); - - ObjectDefineProperty(this, 'historyIndex', { - __proto__: null, configurable: true, enumerable: true, - get() { return this.historyManager.index; }, - set(historyIndex) { return this.historyManager.index = historyIndex; }, - }); - - ObjectDefineProperty(this, 'historySize', { - __proto__: null, configurable: true, enumerable: true, - get() { return this.historyManager.size; }, - }); - - ObjectDefineProperty(this, 'isFlushing', { - __proto__: null, configurable: true, enumerable: true, - get() { return this.historyManager.isFlushing; }, - }); + ObjectDefineProperties(this, kHistoryAccessorDescriptors); } [kSetRawMode](mode) { @@ -622,37 +631,44 @@ class Interface extends InterfaceConstructor { this[kSawReturnAt] = 0; } - // Run test() on the new string chunk, not on the entire line buffer. - let newPartContainsEnding = RegExpPrototypeExec(lineEnding, string); - if (newPartContainsEnding !== null) { - if (this[kLine_buffer]) { - string = this[kLine_buffer] + string; - this[kLine_buffer] = null; - lineEnding.lastIndex = 0; // Start the search from the beginning of the string. - newPartContainsEnding = RegExpPrototypeExec(lineEnding, string); - } - this[kSawReturnAt] = StringPrototypeEndsWith(string, '\r') ? - DateNow() : - 0; - - const indexes = [0, newPartContainsEnding.index, lineEnding.lastIndex]; - let nextMatch; - while ((nextMatch = RegExpPrototypeExec(lineEnding, string)) !== null) { - ArrayPrototypePush(indexes, nextMatch.index, lineEnding.lastIndex); - } - const lastIndex = indexes.length - 1; - // Either '' or (conceivably) the unfinished portion of the next line - this[kLine_buffer] = StringPrototypeSlice(string, indexes[lastIndex]); - for (let i = 1; i < lastIndex; i += 2) { - this[kOnLine](StringPrototypeSlice(string, indexes[i - 1], indexes[i])); - } - } else if (string) { - // No newlines this time, save what we have for next time + if (!string) { + return; + } + + // Split the new string chunk, not the entire line buffer: a single + // split pass avoids allocating a match object per line ending. + // When the chunk contains none of the rare line endings, a plain + // string split is much cheaper than the regular expression. + const lines = + StringPrototypeIncludes(string, '\r') || + StringPrototypeIncludes(string, '\u2028') || + StringPrototypeIncludes(string, '\u2029') ? + RegExpPrototypeSymbolSplit(lineEnding, string) : + StringPrototypeSplit(string, '\n'); + const lastIndex = lines.length - 1; + if (lastIndex === 0) { + // No line endings this time, save what we have for next time. if (this[kLine_buffer]) { this[kLine_buffer] += string; } else { this[kLine_buffer] = string; } + return; + } + + this[kSawReturnAt] = StringPrototypeEndsWith(string, '\r') ? + DateNow() : + 0; + + let first = lines[0]; + if (this[kLine_buffer]) { + first = this[kLine_buffer] + first; + } + // Either '' or (conceivably) the unfinished portion of the next line + this[kLine_buffer] = lines[lastIndex]; + this[kOnLine](first); + for (let i = 1; i < lastIndex; i++) { + this[kOnLine](lines[i]); } } diff --git a/lib/readline.js b/lib/readline.js index 1eaf35d75029..7fb0f9d40af3 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -115,7 +115,9 @@ function Interface(input, output, completer, terminal) { FunctionPrototypeCall(InterfaceConstructor, this, input, output, completer, terminal); - if (process.env.TERM === 'dumb') { + // Reading process.env is expensive and _ttyWrite is only used in + // terminal mode, so only check for a dumb terminal when relevant. + if (this.terminal && process.env.TERM === 'dumb') { this._ttyWrite = FunctionPrototypeBind(_ttyWriteDumb, this); } } From fe9e0dbdc2290434933ef0ce9767372e834b66b5 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 9 Jul 2026 17:04:13 -0700 Subject: [PATCH 024/152] net: support AF_UNIX paths in net.BoundSocket Signed-off-by: Guy Bedford PR-URL: https://github.com/nodejs/node/pull/64399 Reviewed-By: Ethan Arrowood Reviewed-By: James M Snell Reviewed-By: Matteo Collina --- doc/api/net.md | 44 +++++- lib/net.js | 108 +++++++++++++-- test/parallel/test-net-boundsocket.js | 184 ++++++++++++++++++++++++++ 3 files changed, 323 insertions(+), 13 deletions(-) diff --git a/doc/api/net.md b/doc/api/net.md index 4ff3c703b6e4..ebc592c61305 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -1699,9 +1699,20 @@ to `listen()` or `new net.Socket()` later on. For `listen()` this enables synchronous port reservation, while for `new net.Socket()`, it allows control over the local egress port/IP, via `bind(2)` semantics. +A `BoundSocket` binds either a TCP endpoint (`host` or `port`) or a +Unix domain/named-pipe endpoint (`path`); the two are mutually exclusive. For a +`path`, the file system entry is reserved in the constructor, so conflicts such +as `EADDRINUSE` throw synchronously exactly as a TCP bind does. On Linux a +leading `'\0'` in `path` selects the abstract namespace (no file system entry); +an abstract path on any other platform throws [`ERR_INVALID_ARG_VALUE`][]. + Adoption transfers ownership of the socket; afterwards `address()` and `close()` throw [`ERR_SOCKET_HANDLE_ADOPTED`][]. A handle that is never adopted must be -closed to avoid leaking the socket. +closed to avoid leaking the socket. Closing a pipe `BoundSocket` removes its +file system entry; abstract and TCP binds have none to remove. + +When a pipe `BoundSocket` bound to a source `path` is adopted as a client, that +path is reported as the socket's `localAddress` once it connects. When an adopted `BoundSocket` connects to a numeric IP literal, `connect(2)` is issued synchronously, so [`socket.localAddress`][] is resolved once @@ -1723,6 +1734,10 @@ server.listen(bound); // Adopt as a server, or pass to new net.Socket() instead. * `options` {Object} @@ -1737,19 +1752,41 @@ added: v26.4.0 * `reusePort` {boolean} Sets `SO_REUSEPORT`, allowing multiple sockets to bind the same address and port for kernel-level load balancing. Support is platform-dependent. **Default:** `false`. + * `path` {string} Binds a Unix domain socket (or Windows named pipe) at the + given path instead of a TCP endpoint. A leading `'\0'` selects the Linux + abstract namespace. Mutually exclusive with `host`, `port`, `ipv6Only`, and + `reusePort`; combining them throws [`ERR_INVALID_ARG_VALUE`][]. ### `boundSocket.address()` -* Returns: {Object} An object with `address`, `family`, and `port` properties, - as [`server.address()`][] returns. +* Returns: {Object|string} For a TCP bind, an object with `address`, `family`, + and `port` properties, as [`server.address()`][] returns. For a pipe bind, the + bound path string, as [`server.address()`][] returns for a pipe server. Returns the bound local address. When bound with `port: 0`, `port` is the OS-assigned ephemeral port. +### `boundSocket.isPipe` + + + +* {boolean} + +`true` when the socket was bound with a `path` (a Unix domain socket or Windows +named pipe), `false` for a TCP bind. The getter's presence on +`net.BoundSocket.prototype` also serves as a capability probe for `path` +support. + ### `boundSocket.fd()` diff --git a/deps/crates/vendor/icu_calendar_data/build.rs b/deps/crates/vendor/icu_calendar_data-v2/build.rs similarity index 100% rename from deps/crates/vendor/icu_calendar_data/build.rs rename to deps/crates/vendor/icu_calendar_data-v2/build.rs diff --git a/deps/crates/vendor/icu_calendar_data/data/calendar_japanese_modern_v1.rs.data b/deps/crates/vendor/icu_calendar_data-v2/data/calendar_japanese_modern_v1.rs.data similarity index 94% rename from deps/crates/vendor/icu_calendar_data/data/calendar_japanese_modern_v1.rs.data rename to deps/crates/vendor/icu_calendar_data-v2/data/calendar_japanese_modern_v1.rs.data index 31c7a3de95d2..ecfc337c398d 100644 --- a/deps/crates/vendor/icu_calendar_data/data/calendar_japanese_modern_v1.rs.data +++ b/deps/crates/vendor/icu_calendar_data-v2/data/calendar_japanese_modern_v1.rs.data @@ -16,14 +16,14 @@ #[macro_export] macro_rules! __impl_calendar_japanese_modern_v1 { ($ provider : ty) => { - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO; - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl $provider { #[doc(hidden)] pub const SINGLETON_CALENDAR_JAPANESE_MODERN_V1: &'static ::DataStruct = &icu::calendar::provider::JapaneseEras { dates_to_eras: unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"L\x07\0\0\n\x17meiji\0\0\0\0\0\0\0\0\0\0\0x\x07\0\0\x07\x1Etaisho\0\0\0\0\0\0\0\0\0\0\x86\x07\0\0\x0C\x19showa\0\0\0\0\0\0\0\0\0\0\0\xC5\x07\0\0\x01\x08heisei\0\0\0\0\0\0\0\0\0\0\xE3\x07\0\0\x05\x01reiwa\0\0\0\0\0\0\0\0\0\0\0") } }; } - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl icu_provider::DataProvider for $provider { fn load(&self, req: icu_provider::DataRequest) -> Result, icu_provider::DataError> { if req.id.locale.is_unknown() { @@ -36,7 +36,7 @@ macro_rules! __impl_calendar_japanese_modern_v1 { }; ($ provider : ty , ITER) => { __impl_calendar_japanese_modern_v1!($provider); - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl icu_provider::IterableDataProvider for $provider { fn iter_ids(&self) -> Result>, icu_provider::DataError> { Ok([Default::default()].into_iter().collect()) @@ -45,7 +45,7 @@ macro_rules! __impl_calendar_japanese_modern_v1 { }; ($ provider : ty , DRY) => { __impl_calendar_japanese_modern_v1!($provider); - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl icu_provider::DryDataProvider for $provider { fn dry_load(&self, req: icu_provider::DataRequest) -> Result { if req.id.locale.is_unknown() { @@ -58,7 +58,7 @@ macro_rules! __impl_calendar_japanese_modern_v1 { }; ($ provider : ty , DRY , ITER) => { __impl_calendar_japanese_modern_v1!($provider); - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl icu_provider::DryDataProvider for $provider { fn dry_load(&self, req: icu_provider::DataRequest) -> Result { if req.id.locale.is_unknown() { @@ -68,7 +68,7 @@ macro_rules! __impl_calendar_japanese_modern_v1 { } } } - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl icu_provider::IterableDataProvider for $provider { fn iter_ids(&self) -> Result>, icu_provider::DataError> { Ok([Default::default()].into_iter().collect()) diff --git a/deps/crates/vendor/icu_calendar_data/data/calendar_week_v1.rs.data b/deps/crates/vendor/icu_calendar_data-v2/data/calendar_week_v1.rs.data similarity index 98% rename from deps/crates/vendor/icu_calendar_data/data/calendar_week_v1.rs.data rename to deps/crates/vendor/icu_calendar_data-v2/data/calendar_week_v1.rs.data index d28cb3677c50..dc2bc0feff28 100644 --- a/deps/crates/vendor/icu_calendar_data/data/calendar_week_v1.rs.data +++ b/deps/crates/vendor/icu_calendar_data-v2/data/calendar_week_v1.rs.data @@ -17,9 +17,9 @@ #[macro_export] macro_rules! __impl_calendar_week_v1 { ($ provider : ty) => { - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO; - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl $provider { const DATA_CALENDAR_WEEK_V1: icu_provider::baked::zerotrie::Data = { const TRIE: icu_provider::baked::zerotrie::ZeroTrieSimpleAscii<&'static [u8]> = icu_provider::baked::zerotrie::ZeroTrieSimpleAscii { store: b"und\x80-\xD7ABCDEGHIJKLMNOPQSTUVWYZ\t\x1E$06 for $provider { fn load(&self, req: icu_provider::DataRequest) -> Result, icu_provider::DataError> { let mut metadata = icu_provider::DataResponseMetadata::default(); @@ -53,7 +53,7 @@ macro_rules! __impl_calendar_week_v1 { }; ($ provider : ty , ITER) => { __impl_calendar_week_v1!($provider); - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl icu_provider::IterableDataProvider for $provider { fn iter_ids(&self) -> Result>, icu_provider::DataError> { Ok(icu_provider::baked::DataStore::iter(&Self::DATA_CALENDAR_WEEK_V1).collect()) diff --git a/deps/crates/vendor/icu_calendar_data/data/mod.rs b/deps/crates/vendor/icu_calendar_data-v2/data/mod.rs similarity index 88% rename from deps/crates/vendor/icu_calendar_data/data/mod.rs rename to deps/crates/vendor/icu_calendar_data-v2/data/mod.rs index 1ad69179b496..ffa33a336237 100644 --- a/deps/crates/vendor/icu_calendar_data/data/mod.rs +++ b/deps/crates/vendor/icu_calendar_data-v2/data/mod.rs @@ -1,5 +1,4 @@ // @generated -include!("calendar_japanese_extended_v1.rs.data"); include!("calendar_japanese_modern_v1.rs.data"); include!("calendar_week_v1.rs.data"); /// Marks a type as a data provider. You can then use macros like @@ -17,7 +16,7 @@ include!("calendar_week_v1.rs.data"); #[macro_export] macro_rules! __make_provider { ($ name : ty) => { - #[clippy::msrv = "1.83"] + #[clippy::msrv = "1.86"] impl $name { #[allow(dead_code)] pub(crate) const MUST_USE_MAKE_PROVIDER_MACRO: () = (); @@ -36,7 +35,6 @@ pub use __make_provider as make_provider; macro_rules! impl_data_provider { ($ provider : ty) => { make_provider!($provider); - impl_calendar_japanese_extended_v1!($provider); impl_calendar_japanese_modern_v1!($provider); impl_calendar_week_v1!($provider); }; diff --git a/deps/crates/vendor/icu_calendar_data/src/lib.rs b/deps/crates/vendor/icu_calendar_data-v2/src/lib.rs similarity index 89% rename from deps/crates/vendor/icu_calendar_data/src/lib.rs rename to deps/crates/vendor/icu_calendar_data-v2/src/lib.rs index 02ec18a35cc9..90d2b19b7e3c 100644 --- a/deps/crates/vendor/icu_calendar_data/src/lib.rs +++ b/deps/crates/vendor/icu_calendar_data-v2/src/lib.rs @@ -4,7 +4,7 @@ //! Data for the `icu_calendar` crate //! -//! This data was generated with CLDR version 48.0.0, ICU version release-78.1rc, and +//! This data was generated with CLDR version 48.2.0, ICU version release-78.1rc, and //! LSTM segmenter version v0.1.0. #![no_std] diff --git a/deps/crates/vendor/icu_calendar_data/.cargo-checksum.json b/deps/crates/vendor/icu_calendar_data/.cargo-checksum.json deleted file mode 100644 index 5581eabccf52..000000000000 --- a/deps/crates/vendor/icu_calendar_data/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{".cargo_vcs_info.json":"5ddfa3bbe4563249b2050508901cbefb05bd351ea39f5f4add8bc520346ce932","Cargo.lock":"af8b0f1887466167d9776494d3fd9cd8d63b742147ef69538987e7ec190da413","Cargo.toml":"ecdae7056aa05f5e981175be5c1ba744f819438cef7db31d3f9873fbbfe22575","Cargo.toml.orig":"31d1f8d246401f127cd0738522290375cd6124a8ef9232a650aded78f8071ded","LICENSE":"f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2","README.md":"089095cfdb8ce866d7ca71b51433144fb4878dc4ca9b5590b2327b11fdb64b1b","build.rs":"c2d446772e3d766a804963dbf36e51729f910920f91f4b68c0c199fe6ca0853e","data/calendar_japanese_extended_v1.rs.data":"079e4f46ffd69977a95364c12df5455342cd8dc03593b44d2fb73fe06c7145ef","data/calendar_japanese_modern_v1.rs.data":"3b836a21d5e537e06e3b767d44aee023222113415bcc3dbf87248fa89de70928","data/calendar_week_v1.rs.data":"c59edb26ec8b391b52ffa6ad9088d8a51b8353cffc04268701a824e0e9b76c21","data/mod.rs":"ad146e2fac0e7858fea7a10082a8bd63ae0a014b6ff885b041c66c7cc2579bbd","src/lib.rs":"bfc8989a13fe15781d30b59aa152d1806d99a77dbf326d118c5e196a534ccef0"},"package":"527f04223b17edfe0bd43baf14a0cb1b017830db65f3950dc00224860a9a446d"} \ No newline at end of file diff --git a/deps/crates/vendor/icu_calendar_data/.cargo_vcs_info.json b/deps/crates/vendor/icu_calendar_data/.cargo_vcs_info.json deleted file mode 100644 index 266c749a2887..000000000000 --- a/deps/crates/vendor/icu_calendar_data/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "38a49da495248dd1ded84cf306e4ca42e64d5bb3" - }, - "path_in_vcs": "provider/data/calendar" -} \ No newline at end of file diff --git a/deps/crates/vendor/icu_calendar_data/data/calendar_japanese_extended_v1.rs.data b/deps/crates/vendor/icu_calendar_data/data/calendar_japanese_extended_v1.rs.data deleted file mode 100644 index 96a1dd8fd318..000000000000 --- a/deps/crates/vendor/icu_calendar_data/data/calendar_japanese_extended_v1.rs.data +++ /dev/null @@ -1,80 +0,0 @@ -// @generated -/// Implement `DataProvider` on the given struct using the data -/// hardcoded in this file. This allows the struct to be used with -/// `icu`'s `_unstable` constructors. -/// -/// Using this implementation will embed the following data in the binary's data segment: -/// * 5238B[^1] for the singleton data struct -/// -/// [^1]: these numbers can be smaller in practice due to linker deduplication -/// -/// This macro requires the following crates: -/// * `icu` -/// * `icu_provider` -/// * `zerovec` -#[doc(hidden)] -#[macro_export] -macro_rules! __impl_calendar_japanese_extended_v1 { - ($ provider : ty) => { - #[clippy::msrv = "1.83"] - const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO; - #[clippy::msrv = "1.83"] - impl $provider { - #[doc(hidden)] - pub const SINGLETON_CALENDAR_JAPANESE_EXTENDED_V1: &'static ::DataStruct = &icu::calendar::provider::JapaneseEras { dates_to_eras: unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"\x85\x02\0\0\x06\x13taika-645\0\0\0\0\0\0\0\x8A\x02\0\0\x02\x0Fhakuchi-650\0\0\0\0\0\xA0\x02\0\0\x01\x01hakuho-672\0\0\0\0\0\0\xAE\x02\0\0\x07\x14shucho-686\0\0\0\0\0\0\xBD\x02\0\0\x03\x15taiho-701\0\0\0\0\0\0\0\xC0\x02\0\0\x05\nkeiun-704\0\0\0\0\0\0\0\xC4\x02\0\0\x01\x0Bwado-708\0\0\0\0\0\0\0\0\xCB\x02\0\0\t\x02reiki-715\0\0\0\0\0\0\0\xCD\x02\0\0\x0B\x11yoro-717\0\0\0\0\0\0\0\0\xD4\x02\0\0\x02\x04jinki-724\0\0\0\0\0\0\0\xD9\x02\0\0\x08\x05tenpyo-729\0\0\0\0\0\0\xED\x02\0\0\x04\x0Etenpyokampo-749\0\xED\x02\0\0\x07\x02tenpyoshoho-749\0\xF5\x02\0\0\x08\x12tenpyohoji-757\0\0\xFD\x02\0\0\x01\x07tenpyojingo-765\0\xFF\x02\0\0\x08\x10jingokeiun-767\0\0\x02\x03\0\0\n\x01hoki-770\0\0\0\0\0\0\0\0\r\x03\0\0\x01\x01teno-781\0\0\0\0\0\0\0\0\x0E\x03\0\0\x08\x13enryaku-782\0\0\0\0\0&\x03\0\0\x05\x12daido-806\0\0\0\0\0\0\0*\x03\0\0\t\x13konin-810\0\0\0\0\0\0\08\x03\0\0\x01\x05tencho-824\0\0\0\0\0\0B\x03\0\0\x01\x03jowa-834\0\0\0\0\0\0\0\0P\x03\0\0\x06\rkajo-848\0\0\0\0\0\0\0\0S\x03\0\0\x04\x1Cninju-851\0\0\0\0\0\0\0V\x03\0\0\x0B\x1Esaiko-854\0\0\0\0\0\0\0Y\x03\0\0\x02\x15tenan-857\0\0\0\0\0\0\0[\x03\0\0\x04\x0Fjogan-859\0\0\0\0\0\0\0m\x03\0\0\x04\x10gangyo-877\0\0\0\0\0\0u\x03\0\0\x02\x15ninna-885\0\0\0\0\0\0\0y\x03\0\0\x04\x1Bkanpyo-889\0\0\0\0\0\0\x82\x03\0\0\x04\x1Ashotai-898\0\0\0\0\0\0\x85\x03\0\0\x07\x0Fengi-901\0\0\0\0\0\0\0\0\x9B\x03\0\0\x04\x0Bencho-923\0\0\0\0\0\0\0\xA3\x03\0\0\x04\x1Ajohei-931\0\0\0\0\0\0\0\xAA\x03\0\0\x05\x16tengyo-938\0\0\0\0\0\0\xB3\x03\0\0\x04\x16tenryaku-947\0\0\0\0\xBD\x03\0\0\n\x1Btentoku-957\0\0\0\0\0\xC1\x03\0\0\x02\x10owa-961\0\0\0\0\0\0\0\0\0\xC4\x03\0\0\x07\nkoho-964\0\0\0\0\0\0\0\0\xC8\x03\0\0\x08\ranna-968\0\0\0\0\0\0\0\0\xCA\x03\0\0\x03\x19tenroku-970\0\0\0\0\0\xCD\x03\0\0\x0C\x14tenen-973\0\0\0\0\0\0\0\xD0\x03\0\0\x07\rjogen-976\0\0\0\0\0\0\0\xD2\x03\0\0\x0B\x1Dtengen-978\0\0\0\0\0\0\xD7\x03\0\0\x04\x0Feikan-983\0\0\0\0\0\0\0\xD9\x03\0\0\x04\x1Bkanna-985\0\0\0\0\0\0\0\xDB\x03\0\0\x04\x05eien-987\0\0\0\0\0\0\0\0\xDD\x03\0\0\x08\x08eiso-989\0\0\0\0\0\0\0\0\xDE\x03\0\0\x0B\x07shoryaku-990\0\0\0\0\xE3\x03\0\0\x02\x16chotoku-995\0\0\0\0\0\xE7\x03\0\0\x01\rchoho-999\0\0\0\0\0\0\0\xEC\x03\0\0\x07\x14kanko-1004\0\0\0\0\0\0\xF4\x03\0\0\x0C\x19chowa-1012\0\0\0\0\0\0\xF9\x03\0\0\x04\x17kannin-1017\0\0\0\0\0\xFD\x03\0\0\x02\x02jian-1021\0\0\0\0\0\0\0\0\x04\0\0\x07\rmanju-1024\0\0\0\0\0\0\x04\x04\0\0\x07\x19chogen-1028\0\0\0\0\0\r\x04\0\0\x04\x15choryaku-1037\0\0\0\x10\x04\0\0\x0B\nchokyu-1040\0\0\0\0\0\x14\x04\0\0\x0B\x18kantoku-1044\0\0\0\0\x16\x04\0\0\x04\x0Eeisho-1046\0\0\0\0\0\0\x1D\x04\0\0\x01\x0Btengi-1053\0\0\0\0\0\0\"\x04\0\0\x08\x1Dkohei-1058\0\0\0\0\0\0)\x04\0\0\x08\x02jiryaku-1065\0\0\0\0-\x04\0\0\x04\renkyu-1069\0\0\0\0\0\x002\x04\0\0\x08\x17shoho-1074\0\0\0\0\0\x005\x04\0\0\x0B\x11shoryaku-1077\0\0\09\x04\0\0\x02\neiho-1081\0\0\0\0\0\0\0<\x04\0\0\x02\x07otoku-1084\0\0\0\0\0\0?\x04\0\0\x04\x07kanji-1087\0\0\0\0\0\0F\x04\0\0\x0C\x0Fkaho-1094\0\0\0\0\0\0\0H\x04\0\0\x0C\x11eicho-1096\0\0\0\0\0\0I\x04\0\0\x0B\x15jotoku-1097\0\0\0\0\0K\x04\0\0\x08\x1Ckowa-1099\0\0\0\0\0\0\0P\x04\0\0\x02\nchoji-1104\0\0\0\0\0\0R\x04\0\0\x04\tkasho-1106\0\0\0\0\0\0T\x04\0\0\x08\x03tennin-1108\0\0\0\0\0V\x04\0\0\x07\rtenei-1110\0\0\0\0\0\0Y\x04\0\0\x07\reikyu-1113\0\0\0\0\0\0^\x04\0\0\x04\x03genei-1118\0\0\0\0\0\0`\x04\0\0\x04\nhoan-1120\0\0\0\0\0\0\0d\x04\0\0\x04\x03tenji-1124\0\0\0\0\0\0f\x04\0\0\x01\x16daiji-1126\0\0\0\0\0\0k\x04\0\0\x01\x1Dtensho-1131\0\0\0\0\0l\x04\0\0\x08\x0Bchosho-1132\0\0\0\0\0o\x04\0\0\x04\x1Bhoen-1135\0\0\0\0\0\0\0u\x04\0\0\x07\neiji-1141\0\0\0\0\0\0\0v\x04\0\0\x04\x1Ckoji-1142\0\0\0\0\0\0\0x\x04\0\0\x02\x17tenyo-1144\0\0\0\0\0\0y\x04\0\0\x07\x16kyuan-1145\0\0\0\0\0\0\x7F\x04\0\0\x01\x1Aninpei-1151\0\0\0\0\0\x82\x04\0\0\n\x1Ckyuju-1154\0\0\0\0\0\0\x84\x04\0\0\x04\x1Bhogen-1156\0\0\0\0\0\0\x87\x04\0\0\x04\x14heiji-1159\0\0\0\0\0\0\x88\x04\0\0\x01\neiryaku-1160\0\0\0\0\x89\x04\0\0\t\x04oho-1161\0\0\0\0\0\0\0\0\x8B\x04\0\0\x03\x1Dchokan-1163\0\0\0\0\0\x8D\x04\0\0\x06\x05eiman-1165\0\0\0\0\0\0\x8E\x04\0\0\x08\x1Bninan-1166\0\0\0\0\0\0\x91\x04\0\0\x04\x08kao-1169\0\0\0\0\0\0\0\0\x93\x04\0\0\x04\x15shoan-1171\0\0\0\0\0\0\x97\x04\0\0\x07\x1Cangen-1175\0\0\0\0\0\0\x99\x04\0\0\x08\x04jisho-1177\0\0\0\0\0\0\x9D\x04\0\0\x07\x0Eyowa-1181\0\0\0\0\0\0\0\x9E\x04\0\0\x05\x1Bjuei-1182\0\0\0\0\0\0\0\xA0\x04\0\0\x04\x10genryaku-1184\0\0\0\xA1\x04\0\0\x08\x0Ebunji-1185\0\0\0\0\0\0\xA6\x04\0\0\x04\x0Bkenkyu-1190\0\0\0\0\0\xAF\x04\0\0\x04\x1Bshoji-1199\0\0\0\0\0\0\xB1\x04\0\0\x02\rkennin-1201\0\0\0\0\0\xB4\x04\0\0\x02\x14genkyu-1204\0\0\0\0\0\xB6\x04\0\0\x04\x1Bkenei-1206\0\0\0\0\0\0\xB7\x04\0\0\n\x19jogen-1207\0\0\0\0\0\0\xBB\x04\0\0\x03\tkenryaku-1211\0\0\0\xBD\x04\0\0\x0C\x06kenpo-1213\0\0\0\0\0\0\xC3\x04\0\0\x04\x0Cjokyu-1219\0\0\0\0\0\0\xC6\x04\0\0\x04\rjoo-1222\0\0\0\0\0\0\0\0\xC8\x04\0\0\x0B\x14gennin-1224\0\0\0\0\0\xC9\x04\0\0\x04\x14karoku-1225\0\0\0\0\0\xCB\x04\0\0\x0C\nantei-1227\0\0\0\0\0\0\xCD\x04\0\0\x03\x05kanki-1229\0\0\0\0\0\0\xD0\x04\0\0\x04\x02joei-1232\0\0\0\0\0\0\0\xD1\x04\0\0\x04\x0Ftenpuku-1233\0\0\0\0\xD2\x04\0\0\x0B\x05bunryaku-1234\0\0\0\xD3\x04\0\0\t\x13katei-1235\0\0\0\0\0\0\xD6\x04\0\0\x0B\x17ryakunin-1238\0\0\0\xD7\x04\0\0\x02\x07eno-1239\0\0\0\0\0\0\0\0\xD8\x04\0\0\x07\x10ninji-1240\0\0\0\0\0\0\xDB\x04\0\0\x02\x1Akangen-1243\0\0\0\0\0\xDF\x04\0\0\x02\x1Choji-1247\0\0\0\0\0\0\0\xE1\x04\0\0\x03\x12kencho-1249\0\0\0\0\0\xE8\x04\0\0\n\x05kogen-1256\0\0\0\0\0\0\xE9\x04\0\0\x03\x0Eshoka-1257\0\0\0\0\0\0\xEB\x04\0\0\x03\x1Ashogen-1259\0\0\0\0\0\xEC\x04\0\0\x04\rbuno-1260\0\0\0\0\0\0\0\xED\x04\0\0\x02\x14kocho-1261\0\0\0\0\0\0\xF0\x04\0\0\x02\x1Cbunei-1264\0\0\0\0\0\0\xFB\x04\0\0\x04\x19kenji-1275\0\0\0\0\0\0\xFE\x04\0\0\x02\x1Ckoan-1278\0\0\0\0\0\0\0\x08\x05\0\0\x04\x1Cshoo-1288\0\0\0\0\0\0\0\r\x05\0\0\x08\x05einin-1293\0\0\0\0\0\0\x13\x05\0\0\x04\x19shoan-1299\0\0\0\0\0\0\x16\x05\0\0\x0B\x15kengen-1302\0\0\0\0\0\x17\x05\0\0\x08\x05kagen-1303\0\0\0\0\0\0\x1A\x05\0\0\x0C\x0Etokuji-1306\0\0\0\0\0\x1C\x05\0\0\n\tenkyo-1308\0\0\0\0\0\0\x1F\x05\0\0\x04\x1Cocho-1311\0\0\0\0\0\0\0 \x05\0\0\x03\x14showa-1312\0\0\0\0\0\0%\x05\0\0\x02\x03bunpo-1317\0\0\0\0\0\0'\x05\0\0\x04\x1Cgeno-1319\0\0\0\0\0\0\0)\x05\0\0\x02\x17genko-1321\0\0\0\0\0\0,\x05\0\0\x0C\tshochu-1324\0\0\0\0\0.\x05\0\0\x04\x1Akaryaku-1326\0\0\0\x001\x05\0\0\x08\x1Dgentoku-1329\0\0\0\x003\x05\0\0\x08\tgenko-1331\0\0\0\0\0\x006\x05\0\0\x01\x1Dkenmu-1334\0\0\0\0\0\08\x05\0\0\x02\x1Dengen-1336\0\0\0\0\0\0<\x05\0\0\x04\x1Ckokoku-1340\0\0\0\0\0B\x05\0\0\x0C\x08shohei-1346\0\0\0\0\0Z\x05\0\0\x07\x18kentoku-1370\0\0\0\0\\\x05\0\0\x04\x01bunchu-1372\0\0\0\0\0_\x05\0\0\x05\x1Btenju-1375\0\0\0\0\0\0c\x05\0\0\x03\x16koryaku-1379\0\0\0\0e\x05\0\0\x02\nkowa-1381\0\0\0\0\0\0\0h\x05\0\0\x04\x1Cgenchu-1384\0\0\0\0\0k\x05\0\0\x08\x16meitoku-1387\0\0\0\0k\x05\0\0\x08\x17kakei-1387\0\0\0\0\0\0m\x05\0\0\x02\tkoo-1389\0\0\0\0\0\0\0\0n\x05\0\0\x03\x1Ameitoku-1390\0\0\0\0r\x05\0\0\x07\x05oei-1394\0\0\0\0\0\0\0\0\x94\x05\0\0\x04\x1Bshocho-1428\0\0\0\0\0\x95\x05\0\0\t\x05eikyo-1429\0\0\0\0\0\0\xA1\x05\0\0\x02\x11kakitsu-1441\0\0\0\0\xA4\x05\0\0\x02\x05bunan-1444\0\0\0\0\0\0\xA9\x05\0\0\x07\x1Chotoku-1449\0\0\0\0\0\xAC\x05\0\0\x07\x19kyotoku-1452\0\0\0\0\xAF\x05\0\0\x07\x19kosho-1455\0\0\0\0\0\0\xB1\x05\0\0\t\x1Cchoroku-1457\0\0\0\0\xB4\x05\0\0\x0C\x15kansho-1460\0\0\0\0\0\xBA\x05\0\0\x02\x1Cbunsho-1466\0\0\0\0\0\xBB\x05\0\0\x03\x03onin-1467\0\0\0\0\0\0\0\xBD\x05\0\0\x04\x1Cbunmei-1469\0\0\0\0\0\xCF\x05\0\0\x07\x1Dchokyo-1487\0\0\0\0\0\xD1\x05\0\0\x08\x15entoku-1489\0\0\0\0\0\xD4\x05\0\0\x07\x13meio-1492\0\0\0\0\0\0\0\xDD\x05\0\0\x02\x1Cbunki-1501\0\0\0\0\0\0\xE0\x05\0\0\x02\x1Deisho-1504\0\0\0\0\0\0\xF1\x05\0\0\x08\x17taiei-1521\0\0\0\0\0\0\xF8\x05\0\0\x08\x14kyoroku-1528\0\0\0\0\xFC\x05\0\0\x07\x1Dtenbun-1532\0\0\0\0\0\x13\x06\0\0\n\x17koji-1555\0\0\0\0\0\0\0\x16\x06\0\0\x02\x1Ceiroku-1558\0\0\0\0\0\"\x06\0\0\x04\x17genki-1570\0\0\0\0\0\0%\x06\0\0\x07\x1Ctensho-1573\0\0\0\0\08\x06\0\0\x0C\x08bunroku-1592\0\0\0\0<\x06\0\0\n\x1Bkeicho-1596\0\0\0\0\0O\x06\0\0\x07\rgenna-1615\0\0\0\0\0\0X\x06\0\0\x02\x1Dkanei-1624\0\0\0\0\0\0l\x06\0\0\x0C\x10shoho-1644\0\0\0\0\0\0p\x06\0\0\x02\x0Fkeian-1648\0\0\0\0\0\0t\x06\0\0\t\x12joo-1652\0\0\0\0\0\0\0\0w\x06\0\0\x04\rmeireki-1655\0\0\0\0z\x06\0\0\x07\x17manji-1658\0\0\0\0\0\0}\x06\0\0\x04\x19kanbun-1661\0\0\0\0\0\x89\x06\0\0\t\x15enpo-1673\0\0\0\0\0\0\0\x91\x06\0\0\t\x1Dtenna-1681\0\0\0\0\0\0\x94\x06\0\0\x02\x15jokyo-1684\0\0\0\0\0\0\x98\x06\0\0\t\x1Egenroku-1688\0\0\0\0\xA8\x06\0\0\x03\rhoei-1704\0\0\0\0\0\0\0\xAF\x06\0\0\x04\x19shotoku-1711\0\0\0\0\xB4\x06\0\0\x06\x16kyoho-1716\0\0\0\0\0\0\xC8\x06\0\0\x04\x1Cgenbun-1736\0\0\0\0\0\xCD\x06\0\0\x02\x1Bkanpo-1741\0\0\0\0\0\0\xD0\x06\0\0\x02\x15enkyo-1744\0\0\0\0\0\0\xD4\x06\0\0\x07\x0Ckanen-1748\0\0\0\0\0\0\xD7\x06\0\0\n\x1Bhoreki-1751\0\0\0\0\0\xE4\x06\0\0\x06\x02meiwa-1764\0\0\0\0\0\0\xEC\x06\0\0\x0B\x10anei-1772\0\0\0\0\0\0\0\xF5\x06\0\0\x04\x02tenmei-1781\0\0\0\0\0\xFD\x06\0\0\x01\x19kansei-1789\0\0\0\0\0\t\x07\0\0\x02\x05kyowa-1801\0\0\0\0\0\0\x0C\x07\0\0\x02\x0Bbunka-1804\0\0\0\0\0\0\x1A\x07\0\0\x04\x16bunsei-1818\0\0\0\0\0&\x07\0\0\x0C\ntenpo-1830\0\0\0\0\0\x004\x07\0\0\x0C\x02koka-1844\0\0\0\0\0\0\08\x07\0\0\x02\x1Ckaei-1848\0\0\0\0\0\0\0>\x07\0\0\x0B\x1Bansei-1854\0\0\0\0\0\0D\x07\0\0\x03\x12manen-1860\0\0\0\0\0\0E\x07\0\0\x02\x13bunkyu-1861\0\0\0\0\0H\x07\0\0\x02\x14genji-1864\0\0\0\0\0\0I\x07\0\0\x04\x07keio-1865\0\0\0\0\0\0\0L\x07\0\0\n\x17meiji\0\0\0\0\0\0\0\0\0\0\0x\x07\0\0\x07\x1Etaisho\0\0\0\0\0\0\0\0\0\0\x86\x07\0\0\x0C\x19showa\0\0\0\0\0\0\0\0\0\0\0\xC5\x07\0\0\x01\x08heisei\0\0\0\0\0\0\0\0\0\0\xE3\x07\0\0\x05\x01reiwa\0\0\0\0\0\0\0\0\0\0\0") } }; - } - #[clippy::msrv = "1.83"] - impl icu_provider::DataProvider for $provider { - fn load(&self, req: icu_provider::DataRequest) -> Result, icu_provider::DataError> { - if req.id.locale.is_unknown() { - Ok(icu_provider::DataResponse { payload: icu_provider::DataPayload::from_static_ref(Self::SINGLETON_CALENDAR_JAPANESE_EXTENDED_V1), metadata: icu_provider::DataResponseMetadata::default() }) - } else { - Err(icu_provider::DataErrorKind::InvalidRequest.with_req(::INFO, req)) - } - } - } - }; - ($ provider : ty , ITER) => { - __impl_calendar_japanese_extended_v1!($provider); - #[clippy::msrv = "1.83"] - impl icu_provider::IterableDataProvider for $provider { - fn iter_ids(&self) -> Result>, icu_provider::DataError> { - Ok([Default::default()].into_iter().collect()) - } - } - }; - ($ provider : ty , DRY) => { - __impl_calendar_japanese_extended_v1!($provider); - #[clippy::msrv = "1.83"] - impl icu_provider::DryDataProvider for $provider { - fn dry_load(&self, req: icu_provider::DataRequest) -> Result { - if req.id.locale.is_unknown() { - Ok(icu_provider::DataResponseMetadata::default()) - } else { - Err(icu_provider::DataErrorKind::InvalidRequest.with_req(::INFO, req)) - } - } - } - }; - ($ provider : ty , DRY , ITER) => { - __impl_calendar_japanese_extended_v1!($provider); - #[clippy::msrv = "1.83"] - impl icu_provider::DryDataProvider for $provider { - fn dry_load(&self, req: icu_provider::DataRequest) -> Result { - if req.id.locale.is_unknown() { - Ok(icu_provider::DataResponseMetadata::default()) - } else { - Err(icu_provider::DataErrorKind::InvalidRequest.with_req(::INFO, req)) - } - } - } - #[clippy::msrv = "1.83"] - impl icu_provider::IterableDataProvider for $provider { - fn iter_ids(&self) -> Result>, icu_provider::DataError> { - Ok([Default::default()].into_iter().collect()) - } - } - }; -} -#[doc(inline)] -pub use __impl_calendar_japanese_extended_v1 as impl_calendar_japanese_extended_v1; diff --git a/deps/crates/vendor/icu_collections-v2/.cargo-checksum.json b/deps/crates/vendor/icu_collections-v2/.cargo-checksum.json new file mode 100644 index 000000000000..697c9ce2fbb4 --- /dev/null +++ b/deps/crates/vendor/icu_collections-v2/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{}} diff --git a/deps/crates/vendor/icu_collections-v2/.cargo_vcs_info.json b/deps/crates/vendor/icu_collections-v2/.cargo_vcs_info.json new file mode 100644 index 000000000000..2997b36686a6 --- /dev/null +++ b/deps/crates/vendor/icu_collections-v2/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "c9fac4e625ccb2c6a7aa35079fff9709db4385ac" + }, + "path_in_vcs": "components/collections" +} \ No newline at end of file diff --git a/deps/crates/vendor/icu_collections/Cargo.lock b/deps/crates/vendor/icu_collections-v2/Cargo.lock similarity index 81% rename from deps/crates/vendor/icu_collections/Cargo.lock rename to deps/crates/vendor/icu_collections-v2/Cargo.lock index ae29068d4b09..31aff5335ad5 100644 --- a/deps/crates/vendor/icu_collections/Cargo.lock +++ b/deps/crates/vendor/icu_collections-v2/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -19,9 +19,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "autocfg" @@ -31,9 +31,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "cast" @@ -177,9 +177,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "databake" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6ee9e2d2afb173bcdeee45934c89ec341ab26f91c9933774fc15c2b58f83ef" +checksum = "74d4b1db5ca40636726f1f73daff0d626accbd49bcd8136fcade87d7cf1e6bbb" dependencies = [ "databake-derive", "proc-macro2", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "databake-derive" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6834770958c7b84223607e49758ec0dde273c4df915e734aad50f62968a4c134" +checksum = "72b537745234cbf0e296a3bd836d70a614dff4cb522b14e2680ef006bb1ed5ff" dependencies = [ "proc-macro2", "quote", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hermit-abi" @@ -263,7 +263,7 @@ checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678" [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" dependencies = [ "criterion", "databake", @@ -274,6 +274,7 @@ dependencies = [ "serde", "serde_json", "toml", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -311,15 +312,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -327,21 +328,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "log" -version = "0.4.28" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "num-traits" @@ -354,9 +349,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "oorandom" @@ -406,9 +401,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "serde_core", "zerovec", @@ -416,18 +411,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -454,9 +449,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -466,9 +461,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -477,9 +472,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rustversion" @@ -487,12 +482,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -534,15 +523,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -562,9 +551,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "syn" -version = "2.0.108" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -584,18 +573,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -648,9 +637,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.20" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "walkdir" @@ -664,9 +659,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -675,25 +670,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -701,31 +682,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -757,18 +738,18 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ "memchr", ] [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -777,9 +758,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -789,18 +770,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -810,9 +791,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "databake", "serde", @@ -823,11 +804,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/deps/crates/vendor/litemap/Cargo.toml b/deps/crates/vendor/icu_collections-v2/Cargo.toml similarity index 67% rename from deps/crates/vendor/litemap/Cargo.toml rename to deps/crates/vendor/icu_collections-v2/Cargo.toml index a5d635d59522..7f57b47901a3 100644 --- a/deps/crates/vendor/litemap/Cargo.toml +++ b/deps/crates/vendor/icu_collections-v2/Cargo.toml @@ -11,9 +11,9 @@ [package] edition = "2021" -rust-version = "1.82" -name = "litemap" -version = "0.8.2" +rust-version = "1.86" +name = "icu_collections" +version = "2.2.0" authors = ["The ICU4X Project Developers"] build = false include = [ @@ -32,22 +32,22 @@ autobins = false autoexamples = false autotests = false autobenches = false -description = "A key-value Map implementation based on a flat, sorted Vec." -documentation = "https://docs.rs/litemap" +description = "Collection of API for use in ICU libraries." +homepage = "https://icu4x.unicode.org" readme = "README.md" keywords = [ + "unicode", "data-structures", - "memory-efficiency", - "sorted", - "vec", - "map", +] +categories = [ + "internationalization", + "localization", + "no-std", + "embedded", ] license = "Unicode-3.0" repository = "https://github.com/unicode-org/icu4x" -[package.metadata.workspaces] -independent = true - [package.metadata.docs.rs] all-features = true @@ -55,98 +55,113 @@ all-features = true max_combination_size = 3 [features] -alloc = [] -databake = ["dep:databake"] -default = ["alloc"] +alloc = [ + "serde?/alloc", + "zerovec/alloc", +] +databake = [ + "dep:databake", + "zerovec/databake", +] serde = [ - "dep:serde_core", + "dep:serde", + "zerovec/serde", + "potential_utf/serde", "alloc", ] -testing = ["alloc"] -yoke = ["dep:yoke"] [lib] -name = "litemap" +name = "icu_collections" path = "src/lib.rs" bench = false -[[example]] -name = "language_names_hash_map" -path = "examples/language_names_hash_map.rs" - -[[example]] -name = "language_names_lite_map" -path = "examples/language_names_lite_map.rs" - -[[example]] -name = "litemap_bincode" -path = "examples/litemap_bincode.rs" -required-features = ["serde"] - -[[example]] -name = "litemap_postcard" -path = "examples/litemap_postcard.rs" -required-features = ["serde"] - [[test]] -name = "rkyv" -path = "tests/rkyv.rs" +name = "char16trie" +path = "tests/char16trie.rs" [[test]] -name = "serde" -path = "tests/serde.rs" -required-features = ["serde"] +name = "cpt" +path = "tests/cpt.rs" -[[test]] -name = "store" -path = "tests/store.rs" -required-features = ["testing"] +[[bench]] +name = "codepointtrie" +path = "benches/codepointtrie.rs" +harness = false + +[[bench]] +name = "iai_cpt" +path = "benches/iai_cpt.rs" +harness = false [[bench]] -name = "litemap" -path = "benches/litemap.rs" +name = "inv_list" +path = "benches/inv_list.rs" harness = false -required-features = ["serde"] [dependencies.databake] version = "0.2.0" +features = ["derive"] optional = true default-features = false -[dependencies.serde_core] +[dependencies.displaydoc] +version = "0.2.3" +default-features = false + +[dependencies.potential_utf] +version = "0.1.3" +features = ["zerovec"] +default-features = false + +[dependencies.serde] version = "1.0.220" -features = ["alloc"] +features = ["derive"] optional = true default-features = false +[dependencies.utf8_iter] +version = "1.0.2" +default-features = false + [dependencies.yoke] version = "0.8.2" features = ["derive"] -optional = true default-features = false -[dev-dependencies.bincode] -version = "1.3.1" +[dependencies.zerofrom] +version = "0.1.6" +features = ["derive"] +default-features = false -[dev-dependencies.postcard] -version = "1.0.3" -features = ["use-std"] +[dependencies.zerovec] +version = "0.11.6" +features = [ + "derive", + "yoke", +] default-features = false -[dev-dependencies.rand] -version = "0.9" +[dev-dependencies.iai] +version = "0.1.1" -[dev-dependencies.rkyv] -version = "0.7" -features = ["validation"] +[dev-dependencies.postcard] +version = "1.0.3" +features = ["alloc"] +default-features = false -[dev-dependencies.serde_core] +[dev-dependencies.serde] version = "1.0.220" +features = ["derive"] default-features = false [dev-dependencies.serde_json] version = "1.0.45" +[dev-dependencies.toml] +version = "0.8.0" +features = ["parse"] +default-features = false + [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies.criterion] version = "0.5.0" diff --git a/deps/crates/vendor/icu_collections/Cargo.toml.orig b/deps/crates/vendor/icu_collections-v2/Cargo.toml.orig similarity index 92% rename from deps/crates/vendor/icu_collections/Cargo.toml.orig rename to deps/crates/vendor/icu_collections-v2/Cargo.toml.orig index 5e93a0cd333c..c15f61500b46 100644 --- a/deps/crates/vendor/icu_collections/Cargo.toml.orig +++ b/deps/crates/vendor/icu_collections-v2/Cargo.toml.orig @@ -5,9 +5,10 @@ [package] name = "icu_collections" description = "Collection of API for use in ICU libraries." +categories.workspace = true +keywords = ["unicode", "data-structures"] authors.workspace = true -categories.workspace = true edition.workspace = true homepage.workspace = true include.workspace = true @@ -19,12 +20,16 @@ version.workspace = true [package.metadata.docs.rs] all-features = true +[package.metadata.cargo-all-features] +max_combination_size = 3 + [dependencies] displaydoc = { workspace = true } yoke = { workspace = true, features = ["derive"] } zerofrom = { workspace = true, features = ["derive"] } zerovec = { workspace = true, features = ["derive", "yoke"] } potential_utf = { workspace = true, features = ["zerovec"] } +utf8_iter = { workspace = true } serde = { workspace = true, features = ["derive"], optional = true } databake = { workspace = true, features = ["derive"], optional = true } @@ -64,3 +69,6 @@ path = "benches/iai_cpt.rs" name = "inv_list" harness = false path = "benches/inv_list.rs" + +[lints] +workspace = true diff --git a/deps/crates/vendor/icu_calendar_data/LICENSE b/deps/crates/vendor/icu_collections-v2/LICENSE similarity index 100% rename from deps/crates/vendor/icu_calendar_data/LICENSE rename to deps/crates/vendor/icu_collections-v2/LICENSE diff --git a/deps/crates/vendor/icu_collections-v2/OWNERS b/deps/crates/vendor/icu_collections-v2/OWNERS new file mode 100644 index 000000000000..cbe7d0620c40 --- /dev/null +++ b/deps/crates/vendor/icu_collections-v2/OWNERS @@ -0,0 +1,2 @@ +# This file has been auto-generated by the `gnrt` tool. +file://third_party/rust/icu_collections/OWNERS diff --git a/deps/crates/vendor/icu_collections/README.md b/deps/crates/vendor/icu_collections-v2/README.md similarity index 100% rename from deps/crates/vendor/icu_collections/README.md rename to deps/crates/vendor/icu_collections-v2/README.md diff --git a/deps/crates/vendor/icu_collections/benches/codepointtrie.rs b/deps/crates/vendor/icu_collections-v2/benches/codepointtrie.rs similarity index 100% rename from deps/crates/vendor/icu_collections/benches/codepointtrie.rs rename to deps/crates/vendor/icu_collections-v2/benches/codepointtrie.rs diff --git a/deps/crates/vendor/icu_collections/benches/iai_cpt.rs b/deps/crates/vendor/icu_collections-v2/benches/iai_cpt.rs similarity index 100% rename from deps/crates/vendor/icu_collections/benches/iai_cpt.rs rename to deps/crates/vendor/icu_collections-v2/benches/iai_cpt.rs diff --git a/deps/crates/vendor/icu_collections/benches/inv_list.rs b/deps/crates/vendor/icu_collections-v2/benches/inv_list.rs similarity index 100% rename from deps/crates/vendor/icu_collections/benches/inv_list.rs rename to deps/crates/vendor/icu_collections-v2/benches/inv_list.rs diff --git a/deps/crates/vendor/icu_collections/benches/tries/gc_fast.rs b/deps/crates/vendor/icu_collections-v2/benches/tries/gc_fast.rs similarity index 100% rename from deps/crates/vendor/icu_collections/benches/tries/gc_fast.rs rename to deps/crates/vendor/icu_collections-v2/benches/tries/gc_fast.rs diff --git a/deps/crates/vendor/icu_collections/benches/tries/gc_small.rs b/deps/crates/vendor/icu_collections-v2/benches/tries/gc_small.rs similarity index 100% rename from deps/crates/vendor/icu_collections/benches/tries/gc_small.rs rename to deps/crates/vendor/icu_collections-v2/benches/tries/gc_small.rs diff --git a/deps/crates/vendor/icu_collections/benches/tries/mod.rs b/deps/crates/vendor/icu_collections-v2/benches/tries/mod.rs similarity index 100% rename from deps/crates/vendor/icu_collections/benches/tries/mod.rs rename to deps/crates/vendor/icu_collections-v2/benches/tries/mod.rs diff --git a/deps/crates/vendor/icu_collections/src/char16trie/mod.rs b/deps/crates/vendor/icu_collections-v2/src/char16trie/mod.rs similarity index 100% rename from deps/crates/vendor/icu_collections/src/char16trie/mod.rs rename to deps/crates/vendor/icu_collections-v2/src/char16trie/mod.rs diff --git a/deps/crates/vendor/icu_collections/src/char16trie/trie.rs b/deps/crates/vendor/icu_collections-v2/src/char16trie/trie.rs similarity index 98% rename from deps/crates/vendor/icu_collections/src/char16trie/trie.rs rename to deps/crates/vendor/icu_collections-v2/src/char16trie/trie.rs index 5aedccabe5e1..668a0ca84bbd 100644 --- a/deps/crates/vendor/icu_collections/src/char16trie/trie.rs +++ b/deps/crates/vendor/icu_collections-v2/src/char16trie/trie.rs @@ -79,6 +79,7 @@ fn skip_node_value(pos: usize, lead: u16) -> usize { #[cfg_attr(feature = "databake", derive(databake::Bake))] #[cfg_attr(feature = "databake", databake(path = icu_collections::char16trie))] #[derive(Clone, Debug, PartialEq, Eq, ZeroFrom)] +#[allow(clippy::exhaustive_structs)] // effectively exhaustive, struct-constructible for baking pub struct Char16Trie<'data> { /// An array of u16 containing the trie data. #[cfg_attr(feature = "serde", serde(borrow))] @@ -101,9 +102,9 @@ impl<'data> Char16Trie<'data> { } /// This struct represents an iterator over a [`Char16Trie`]. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Char16TrieIterator<'a> { - /// A reference to the Char16Trie data to iterate over. + /// A reference to the [`Char16Trie`] data to iterate over. trie: &'a ZeroSlice, /// Index of next trie unit to read, or `None` if there are no more matches. pos: Option, @@ -114,6 +115,7 @@ pub struct Char16TrieIterator<'a> { /// An enum representing the return value from a lookup in [`Char16Trie`]. #[derive(Clone, Copy, Debug, PartialEq)] +#[allow(clippy::exhaustive_enums)] pub enum TrieResult { /// The input unit(s) did not continue a matching string. /// Once `next()` returns `TrieResult::NoMatch`, all further calls to `next()` diff --git a/deps/crates/vendor/icu_collections/src/codepointinvlist/builder.rs b/deps/crates/vendor/icu_collections-v2/src/codepointinvlist/builder.rs similarity index 99% rename from deps/crates/vendor/icu_collections/src/codepointinvlist/builder.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointinvlist/builder.rs index d12cba386262..d24f9f66ac83 100644 --- a/deps/crates/vendor/icu_collections/src/codepointinvlist/builder.rs +++ b/deps/crates/vendor/icu_collections-v2/src/codepointinvlist/builder.rs @@ -13,7 +13,7 @@ use zerovec::{ule::AsULE, ZeroVec}; /// A builder for [`CodePointInversionList`]. /// /// Provides exposure to builder functions and conversion to [`CodePointInversionList`] -#[derive(Default)] +#[derive(Default, Clone, Debug)] pub struct CodePointInversionListBuilder { // A sorted list of even length, with values <= char::MAX + 1 intervals: Vec, diff --git a/deps/crates/vendor/icu_collections/src/codepointinvlist/conversions.rs b/deps/crates/vendor/icu_collections-v2/src/codepointinvlist/conversions.rs similarity index 100% rename from deps/crates/vendor/icu_collections/src/codepointinvlist/conversions.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointinvlist/conversions.rs diff --git a/deps/crates/vendor/icu_collections/src/codepointinvlist/cpinvlist.rs b/deps/crates/vendor/icu_collections-v2/src/codepointinvlist/cpinvlist.rs similarity index 99% rename from deps/crates/vendor/icu_collections/src/codepointinvlist/cpinvlist.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointinvlist/cpinvlist.rs index 02f624cdd16a..74fa5ee57682 100644 --- a/deps/crates/vendor/icu_collections/src/codepointinvlist/cpinvlist.rs +++ b/deps/crates/vendor/icu_collections-v2/src/codepointinvlist/cpinvlist.rs @@ -329,7 +329,7 @@ impl<'data> CodePointInversionList<'data> { /// (expected[1] - expected[0]) as usize /// ); /// ``` - pub fn all() -> Self { + pub const fn all() -> Self { Self { inv_list: ALL_VEC, size: (char::MAX as u32) + 1, diff --git a/deps/crates/vendor/icu_collections/src/codepointinvlist/mod.rs b/deps/crates/vendor/icu_collections-v2/src/codepointinvlist/mod.rs similarity index 83% rename from deps/crates/vendor/icu_collections/src/codepointinvlist/mod.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointinvlist/mod.rs index 34b99b3b8cfd..be47ec5ca0d2 100644 --- a/deps/crates/vendor/icu_collections/src/codepointinvlist/mod.rs +++ b/deps/crates/vendor/icu_collections-v2/src/codepointinvlist/mod.rs @@ -16,7 +16,7 @@ //! ## Creating a `CodePointInversionList` //! //! `CodePointSets` are created from either serialized [`CodePointSets`](CodePointInversionList), -//! represented by [inversion lists](http://userguide.icu-project.org/strings/properties), +//! represented by [inversion lists](https://unicode-org.github.io/icu/userguide/strings/properties.html), //! the [`CodePointInversionListBuilder`], or from the Properties API. //! //! ``` @@ -51,8 +51,6 @@ //! //! [`ICU4X`]: ../icu/index.html -#![warn(missing_docs)] - #[cfg(feature = "alloc")] #[macro_use] mod builder; @@ -61,6 +59,8 @@ mod conversions; mod cpinvlist; mod utils; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; #[cfg(feature = "alloc")] pub use builder::CodePointInversionListBuilder; pub use cpinvlist::CodePointInversionList; @@ -68,13 +68,13 @@ pub use cpinvlist::CodePointInversionListULE; use displaydoc::Display; #[derive(Display, Debug)] -/// A CodePointInversionList was constructed with an invalid inversion list +/// A [`CodePointInversionList`] was constructed with an invalid inversion list #[cfg_attr(feature = "alloc", displaydoc("Invalid set: {0:?}"))] -pub struct InvalidSetError( - #[cfg(feature = "alloc")] pub alloc::vec::Vec, -); +#[allow(clippy::exhaustive_structs)] // newtype +pub struct InvalidSetError(#[cfg(feature = "alloc")] pub Vec); -/// A CodePointInversionList was constructed from an invalid range +/// A [`CodePointInversionList`] was constructed from an invalid range #[derive(Display, Debug)] #[displaydoc("Invalid range: {0}..{1}")] +#[allow(clippy::exhaustive_structs)] // newtype pub struct RangeError(pub u32, pub u32); diff --git a/deps/crates/vendor/icu_collections/src/codepointinvlist/utils.rs b/deps/crates/vendor/icu_collections-v2/src/codepointinvlist/utils.rs similarity index 100% rename from deps/crates/vendor/icu_collections/src/codepointinvlist/utils.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointinvlist/utils.rs diff --git a/deps/crates/vendor/icu_collections/src/codepointinvliststringlist/mod.rs b/deps/crates/vendor/icu_collections-v2/src/codepointinvliststringlist/mod.rs similarity index 96% rename from deps/crates/vendor/icu_collections/src/codepointinvliststringlist/mod.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointinvliststringlist/mod.rs index 136f5ef2c219..9fb15c64bfe0 100644 --- a/deps/crates/vendor/icu_collections/src/codepointinvliststringlist/mod.rs +++ b/deps/crates/vendor/icu_collections-v2/src/codepointinvliststringlist/mod.rs @@ -173,6 +173,20 @@ impl<'data> CodePointInversionListAndStringList<'data> { self.str_list.binary_search(s).is_ok() } + /// See [`Self::contains_str`] + pub fn contains_utf8(&self, s: &[u8]) -> bool { + use utf8_iter::Utf8CharsEx; + let mut chars = s.chars(); + if let Some(first_char) = chars.next() { + if chars.next().is_none() { + return self.contains(first_char); + } + } + self.str_list + .binary_search_by(|t| t.as_bytes().cmp(s)) + .is_ok() + } + /// /// # Examples /// ``` @@ -267,6 +281,7 @@ impl<'a> FromIterator<&'a str> for CodePointInversionListAndStringList<'_> { /// Custom Errors for [`CodePointInversionListAndStringList`]. #[derive(Display, Debug)] +#[allow(clippy::exhaustive_enums)] // todo, missed in 2.0 pub enum InvalidStringList { /// A string in the string list had an invalid length #[cfg_attr(feature = "alloc", displaydoc("Invalid string length for string: {0}"))] diff --git a/deps/crates/vendor/icu_collections/src/codepointtrie/cptrie.rs b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/cptrie.rs similarity index 98% rename from deps/crates/vendor/icu_collections/src/codepointtrie/cptrie.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointtrie/cptrie.rs index 5b184a46f894..086d936e2b57 100644 --- a/deps/crates/vendor/icu_collections/src/codepointtrie/cptrie.rs +++ b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/cptrie.rs @@ -43,6 +43,7 @@ use zerovec::ZeroVec; #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "databake", derive(databake::Bake))] #[cfg_attr(feature = "databake", databake(path = icu_collections::codepointtrie))] +#[allow(clippy::exhaustive_enums)] // based on a stable serialized form pub enum TrieType { /// Represents the "fast" type code point tries for the /// [`TrieType`] trait. The "fast max" limit is set to `0xffff`. @@ -60,7 +61,7 @@ pub enum TrieType { /// This trait is used as a type parameter in constructing a `CodePointTrie`. /// /// This trait can be implemented on anything that can be represented as a u32s worth of data. -pub trait TrieValue: Copy + Eq + PartialEq + zerovec::ule::AsULE + 'static { +pub trait TrieValue: Copy + Eq + PartialEq + AsULE + 'static { /// Last-resort fallback value to return if we cannot read data from the trie. /// /// In most cases, the error value is read from the last element of the `data` array, @@ -89,6 +90,7 @@ macro_rules! impl_primitive_trie_value { Self::try_from(i) } + #[allow(trivial_numeric_casts)] fn to_u32(self) -> u32 { // bitcast when the same size, zero-extend/sign-extend // when not the same size @@ -126,7 +128,7 @@ fn maybe_filter_value(value: T, trie_null_value: T, null_value: T) /// ICU binary data. /// /// For more information: -/// - [ICU Site design doc](http://site.icu-project.org/design/struct/utrie) +/// - [ICU Site design doc](https://unicode-org.github.io/icu/design/struct/utrie) /// - [ICU User Guide section on Properties lookup](https://unicode-org.github.io/icu/userguide/strings/properties.html#lookup) // serde impls in crate::serde #[derive(Debug, Eq, PartialEq, Yokeable, ZeroFrom)] @@ -172,6 +174,7 @@ pub struct CodePointTrie<'trie, T: TrieValue> { #[cfg_attr(feature = "databake", derive(databake::Bake))] #[cfg_attr(feature = "databake", databake(path = icu_collections::codepointtrie))] #[derive(Copy, Clone, Debug, Eq, PartialEq, Yokeable, ZeroFrom)] +#[allow(clippy::exhaustive_structs)] // based on a stable serialized form pub struct CodePointTrieHeader { /// The code point of the start of the last range of the trie. A /// range is defined as a partition of the code point space such that the @@ -210,13 +213,13 @@ pub struct CodePointTrieHeader { } impl TryFrom for TrieType { - type Error = crate::codepointtrie::error::Error; + type Error = Error; - fn try_from(trie_type_int: u8) -> Result { + fn try_from(trie_type_int: u8) -> Result { match trie_type_int { 0 => Ok(TrieType::Fast), 1 => Ok(TrieType::Small), - _ => Err(crate::codepointtrie::error::Error::FromDeserialized { + _ => Err(Error::FromDeserialized { reason: "Cannot parse value for trie_type", }), } @@ -409,12 +412,10 @@ impl<'trie, T: TrieValue> CodePointTrie<'trie, T> { // actual trie type agrees with the semantics of the typed wrapper. match self.header.trie_type { TrieType::Fast => Typed::Fast(unsafe { - core::mem::transmute::<&CodePointTrie<'trie, T>, &FastCodePointTrie<'trie, T>>(self) + &*(self as *const CodePointTrie<'trie, T> as *const FastCodePointTrie<'trie, T>) }), TrieType::Small => Typed::Small(unsafe { - core::mem::transmute::<&CodePointTrie<'trie, T>, &SmallCodePointTrie<'trie, T>>( - self, - ) + &*(self as *const CodePointTrie<'trie, T> as *const SmallCodePointTrie<'trie, T>) }), } } @@ -1365,7 +1366,7 @@ impl> CodePointTrie<'_, T> { impl Clone for CodePointTrie<'_, T> where - ::ULE: Clone, + ::ULE: Clone, { fn clone(&self) -> Self { CodePointTrie { @@ -1383,6 +1384,7 @@ where /// The start and end of the interval is represented as a /// `RangeInclusive`, and the value is represented as `T`. #[derive(PartialEq, Eq, Debug, Clone)] +#[allow(clippy::exhaustive_structs)] // based on a stable serialized form pub struct CodePointMapRange { /// Range of code points from start to end (inclusive). pub range: RangeInclusive, @@ -1392,6 +1394,7 @@ pub struct CodePointMapRange { /// A custom [`Iterator`] type specifically for a code point trie that returns /// [`CodePointMapRange`]s. +#[derive(Debug)] pub struct CodePointMapRangeIterator<'a, T: TrieValue> { cpt: &'a CodePointTrie<'a, T>, // Initialize `range` to Some(CodePointMapRange{ start: u32::MAX, end: u32::MAX, value: 0}). @@ -1664,6 +1667,8 @@ pub struct TypedCodePointTrieError; /// Holder for either fast or small trie with the trie /// type encoded into the Rust type. +#[allow(clippy::exhaustive_enums)] +#[derive(Debug)] pub enum Typed { /// The trie type is fast. Fast(F), @@ -1680,7 +1685,7 @@ mod tests { #[test] #[cfg(feature = "serde")] fn test_serde_with_postcard_roundtrip() -> Result<(), postcard::Error> { - let trie = crate::codepointtrie::planes::get_planes_trie(); + let trie = planes::get_planes_trie(); let trie_serialized: Vec = postcard::to_allocvec(&trie).unwrap(); // Assert an expected (golden data) version of the serialized trie. diff --git a/deps/crates/vendor/icu_collections/src/codepointtrie/error.rs b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/error.rs similarity index 100% rename from deps/crates/vendor/icu_collections/src/codepointtrie/error.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointtrie/error.rs diff --git a/deps/crates/vendor/icu_collections/src/codepointtrie/impl_const.rs b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/impl_const.rs similarity index 100% rename from deps/crates/vendor/icu_collections/src/codepointtrie/impl_const.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointtrie/impl_const.rs diff --git a/deps/crates/vendor/icu_collections/src/codepointtrie/mod.rs b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/mod.rs similarity index 100% rename from deps/crates/vendor/icu_collections/src/codepointtrie/mod.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointtrie/mod.rs diff --git a/deps/crates/vendor/icu_collections/src/codepointtrie/planes.rs b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/planes.rs similarity index 100% rename from deps/crates/vendor/icu_collections/src/codepointtrie/planes.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointtrie/planes.rs diff --git a/deps/crates/vendor/icu_collections/src/codepointtrie/serde.rs b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/serde.rs similarity index 100% rename from deps/crates/vendor/icu_collections/src/codepointtrie/serde.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointtrie/serde.rs diff --git a/deps/crates/vendor/icu_collections/src/codepointtrie/toml.rs b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/toml.rs similarity index 97% rename from deps/crates/vendor/icu_collections/src/codepointtrie/toml.rs rename to deps/crates/vendor/icu_collections-v2/src/codepointtrie/toml.rs index af54098e1f21..0e05f16510da 100644 --- a/deps/crates/vendor/icu_collections/src/codepointtrie/toml.rs +++ b/deps/crates/vendor/icu_collections-v2/src/codepointtrie/toml.rs @@ -2,7 +2,7 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). -//! Utilities for reading CodePointTrie data from TOML files. +//! Utilities for reading [`CodePointTrie`] data from TOML files. use crate::codepointtrie::error::Error; use crate::codepointtrie::CodePointTrie; @@ -18,7 +18,7 @@ use zerovec::ZeroVec; /// generated by ICU4C. /// /// Use `TryInto` to convert [`CodePointTrieToml`] to a proper [`CodePointTrie`]. -#[derive(serde::Deserialize)] +#[derive(serde::Deserialize, Debug)] pub struct CodePointTrieToml { #[serde(skip)] _short_name: String, @@ -55,6 +55,7 @@ pub struct CodePointTrieToml { /// ICU4C exports data as either `u8`, `u16`, or `u32`, which may be converted /// to other types as appropriate. #[allow(clippy::exhaustive_enums)] // based on a stable serialized form +#[derive(Debug)] pub enum CodePointDataSlice<'a> { /// A serialized [`CodePointTrie`] data array 8-bit values. U8(&'a [u8]), diff --git a/deps/crates/vendor/icu_collections/src/iterator_utils.rs b/deps/crates/vendor/icu_collections-v2/src/iterator_utils.rs similarity index 95% rename from deps/crates/vendor/icu_collections/src/iterator_utils.rs rename to deps/crates/vendor/icu_collections-v2/src/iterator_utils.rs index 0701f32ffd74..913e3535eeb6 100644 --- a/deps/crates/vendor/icu_collections/src/iterator_utils.rs +++ b/deps/crates/vendor/icu_collections-v2/src/iterator_utils.rs @@ -60,10 +60,10 @@ where #[cfg(test)] mod tests { + use crate::codepointinvlist::CodePointInversionListBuilder; use core::fmt::Debug; - use icu::collections::codepointinvlist::CodePointInversionListBuilder; - use icu::properties::props::{BinaryProperty, EnumeratedProperty}; - use icu::properties::{CodePointMapData, CodePointSetData}; + use icu_properties::props::{BinaryProperty, EnumeratedProperty}; + use icu_properties::{CodePointMapData, CodePointSetData}; fn test_set(name: &str) { let mut builder = CodePointInversionListBuilder::new(); @@ -106,7 +106,7 @@ mod tests { #[test] fn test_complement_sets() { - use icu::properties::props::*; + use icu_properties::props::*; // Stress test the RangeListIteratorComplementer logic by ensuring it works for // a whole bunch of binary properties test_set::("ASCII_Hex_Digit"); @@ -178,7 +178,7 @@ mod tests { #[test] fn test_complement_maps() { - use icu::properties::props::{GeneralCategory, Script}; + use icu_properties::props::{GeneralCategory, Script}; test_map(GeneralCategory::UppercaseLetter, "gc"); test_map(GeneralCategory::OtherPunctuation, "gc"); test_map(Script::Devanagari, "script"); diff --git a/deps/crates/vendor/icu_collections/src/lib.rs b/deps/crates/vendor/icu_collections-v2/src/lib.rs similarity index 98% rename from deps/crates/vendor/icu_collections/src/lib.rs rename to deps/crates/vendor/icu_collections-v2/src/lib.rs index a20545d0887e..f19010e72bf6 100644 --- a/deps/crates/vendor/icu_collections/src/lib.rs +++ b/deps/crates/vendor/icu_collections-v2/src/lib.rs @@ -2,6 +2,19 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). +// https://github.com/unicode-org/icu4x/blob/main/documents/process/boilerplate.md#library-annotations +#![cfg_attr(not(any(test, doc)), no_std)] +#![cfg_attr( + not(test), + deny( + clippy::indexing_slicing, + clippy::unwrap_used, + clippy::expect_used, + clippy::panic, + ) +)] +#![warn(missing_docs)] + //! Efficient collections for Unicode data. //! //! This module is published as its own crate ([`icu_collections`](https://docs.rs/icu_collections/latest/icu_collections/)) @@ -20,19 +33,6 @@ //! It is an implementation of the existing [ICU4C UCharsTrie](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1UCharsTrie.html) //! / [ICU4J CharsTrie](https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/com/ibm/icu/util/CharsTrie.html) API. -// https://github.com/unicode-org/icu4x/blob/main/documents/process/boilerplate.md#library-annotations -#![cfg_attr(not(any(test, doc)), no_std)] -#![cfg_attr( - not(test), - deny( - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::expect_used, - clippy::panic - ) -)] -#![warn(missing_docs)] - #[cfg(feature = "alloc")] extern crate alloc; diff --git a/deps/crates/vendor/icu_collections/tests/char16trie.rs b/deps/crates/vendor/icu_collections-v2/tests/char16trie.rs similarity index 99% rename from deps/crates/vendor/icu_collections/tests/char16trie.rs rename to deps/crates/vendor/icu_collections-v2/tests/char16trie.rs index ac0308870c7f..dcb4424c635e 100644 --- a/deps/crates/vendor/icu_collections/tests/char16trie.rs +++ b/deps/crates/vendor/icu_collections-v2/tests/char16trie.rs @@ -294,11 +294,11 @@ fn months() { } #[derive(serde::Deserialize)] -pub struct TestFile { +struct TestFile { ucharstrie: Char16TrieVec, } #[derive(serde::Deserialize)] -pub struct Char16TrieVec { +struct Char16TrieVec { data: Vec, } diff --git a/deps/crates/vendor/icu_collections/tests/cpt.rs b/deps/crates/vendor/icu_collections-v2/tests/cpt.rs similarity index 89% rename from deps/crates/vendor/icu_collections/tests/cpt.rs rename to deps/crates/vendor/icu_collections-v2/tests/cpt.rs index f4b04d55963b..1218ea79558a 100644 --- a/deps/crates/vendor/icu_collections/tests/cpt.rs +++ b/deps/crates/vendor/icu_collections-v2/tests/cpt.rs @@ -2,6 +2,8 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). +#![allow(dead_code)] + use icu_collections::codepointtrie::planes::get_planes_trie; use icu_collections::codepointtrie::*; use zerovec::ZeroVec; @@ -231,16 +233,16 @@ fn small0_in_fast_small16() { /// See [`UCPTrieValueWidth`](https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/ucptrie_8h.html) in ICU4C. #[derive(Clone, Copy, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum ValueWidthEnum { +enum ValueWidthEnum { Bits16 = 0, Bits32 = 1, Bits8 = 2, } -/// Test .get() on CodePointTrie by iterating through each range in -/// check_ranges and assert that the associated +/// Test .`get()` on [`CodePointTrie`] by iterating through each range in +/// `check_ranges` and assert that the associated /// value matches the trie value for each code point in the range. -pub fn check_trie>(trie: &CodePointTrie, check_ranges: &[u32]) { +fn check_trie>(trie: &CodePointTrie, check_ranges: &[u32]) { assert_eq!( 0, check_ranges.len() % 2, @@ -261,12 +263,12 @@ pub fn check_trie>(trie: &CodePointTrie, check_range } } -/// Test `.get_range()` / `.iter_ranges()` on CodePointTrie by calling +/// Test `.get_range()` / `.iter_ranges()` on [`CodePointTrie`] by calling /// `.iter_ranges()` on the trie. /// /// `.iter_ranges()` returns an iterator that produces values -/// by calls to .get_range, and this checks if it matches the values in check_ranges. -pub fn test_check_ranges_get_ranges>( +/// by calls to .`get_range`, and this checks if it matches the values in `check_ranges`. +fn test_check_ranges_get_ranges>( trie: &CodePointTrie, check_ranges: &[u32], ) { @@ -308,8 +310,8 @@ pub fn test_check_ranges_get_ranges>( assert!(trie_ranges.next().is_none(), "CodePointTrie iter_ranges() produces more ranges than the check_ranges field in testdata has"); } -/// Run above tests that verify the validity of CodePointTrie methods -pub fn run_trie_tests>(trie: &CodePointTrie, check_ranges: &[u32]) { +/// Run above tests that verify the validity of [`CodePointTrie`] methods +fn run_trie_tests>(trie: &CodePointTrie, check_ranges: &[u32]) { check_trie(trie, check_ranges); test_check_ranges_get_ranges(trie, check_ranges); } @@ -318,27 +320,27 @@ pub fn run_trie_tests>(trie: &CodePointTrie, check_r // main `CodePointTrie` struct in the corresponding data provider. #[cfg_attr(any(feature = "serde", test), derive(serde::Deserialize))] -pub struct UnicodeEnumeratedProperty { - pub code_point_map: EnumPropCodePointMap, - pub code_point_trie: EnumPropSerializedCPT, +struct UnicodeEnumeratedProperty { + code_point_map: EnumPropCodePointMap, + code_point_trie: EnumPropSerializedCPT, } #[cfg_attr(any(feature = "serde", test), derive(serde::Deserialize))] -pub struct EnumPropCodePointMap { - pub data: EnumPropCodePointMapData, +struct EnumPropCodePointMap { + data: EnumPropCodePointMapData, } #[cfg_attr(any(feature = "serde", test), derive(serde::Deserialize))] -pub struct EnumPropCodePointMapData { - pub long_name: String, - pub name: String, - pub ranges: Vec<(u32, u32, u32)>, +struct EnumPropCodePointMapData { + long_name: String, + name: String, + ranges: Vec<(u32, u32, u32)>, } #[cfg_attr(any(feature = "serde", test), derive(serde::Deserialize))] -pub struct EnumPropSerializedCPT { +struct EnumPropSerializedCPT { #[cfg_attr(any(feature = "serde", test), serde(rename = "struct"))] - pub trie_struct: EnumPropSerializedCPTStruct, + trie_struct: EnumPropSerializedCPTStruct, } // These structs support the test data dumped as TOML files from ICU. @@ -347,32 +349,32 @@ pub struct EnumPropSerializedCPT { // into main code at a later point. #[cfg_attr(any(feature = "serde", test), derive(serde::Deserialize))] -pub struct EnumPropSerializedCPTStruct { +struct EnumPropSerializedCPTStruct { #[cfg_attr(any(feature = "serde", test), serde(skip))] - pub long_name: String, - pub name: String, - pub index: Vec, - pub data_8: Option>, - pub data_16: Option>, - pub data_32: Option>, + long_name: String, + name: String, + index: Vec, + data_8: Option>, + data_16: Option>, + data_32: Option>, #[cfg_attr(any(feature = "serde", test), serde(skip))] - pub index_length: u32, + index_length: u32, #[cfg_attr(any(feature = "serde", test), serde(skip))] - pub data_length: u32, + data_length: u32, #[cfg_attr(any(feature = "serde", test), serde(rename = "highStart"))] - pub high_start: u32, + high_start: u32, #[cfg_attr(any(feature = "serde", test), serde(rename = "shifted12HighStart"))] - pub shifted12_high_start: u16, + shifted12_high_start: u16, #[cfg_attr(any(feature = "serde", test), serde(rename = "type"))] - pub trie_type_enum_val: u8, + trie_type_enum_val: u8, #[cfg_attr(any(feature = "serde", test), serde(rename = "valueWidth"))] - pub value_width_enum_val: u8, + value_width_enum_val: u8, #[cfg_attr(any(feature = "serde", test), serde(rename = "index3NullOffset"))] - pub index3_null_offset: u16, + index3_null_offset: u16, #[cfg_attr(any(feature = "serde", test), serde(rename = "dataNullOffset"))] - pub data_null_offset: u32, + data_null_offset: u32, #[cfg_attr(any(feature = "serde", test), serde(rename = "nullValue"))] - pub null_value: u32, + null_value: u32, } // Given a .toml file dumped from ICU4C test data for UCPTrie, run the test @@ -381,17 +383,17 @@ pub struct EnumPropSerializedCPTStruct { // "check ranges" (inversion map ranges) using `check_trie` to verify the // validity of the `CodePointTrie`'s behavior for all code points. #[allow(dead_code)] -pub fn run_deserialize_test_from_test_data(test_file: &str) { +fn run_deserialize_test_from_test_data(test_file: &str) { // The following structs are specific to the TOML format files for dumped ICU // test data. #[derive(serde::Deserialize)] - pub struct TestFile { + struct TestFile { code_point_trie: TestCodePointTrie, } #[derive(serde::Deserialize)] - pub struct TestCodePointTrie { + struct TestCodePointTrie { // The trie_struct field for test data files is dumped from the same source // (ICU4C) using the same function (usrc_writeUCPTrie) as property data // for the provider, so we can reuse the same struct here. @@ -402,7 +404,7 @@ pub fn run_deserialize_test_from_test_data(test_file: &str) { } #[derive(serde::Deserialize)] - pub struct TestData { + struct TestData { #[serde(rename(deserialize = "checkRanges"))] check_ranges: Vec, } diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/empty.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/empty.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/empty.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/empty.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/months.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/months.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/months.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/months.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/test_a.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_a.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/test_a.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_a.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/test_a_ab.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_a_ab.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/test_a_ab.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_a_ab.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/test_branches.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_branches.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/test_branches.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_branches.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/test_compact.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_compact.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/test_compact.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_compact.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/test_long_branch.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_long_branch.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/test_long_branch.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_long_branch.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/test_long_sequence.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_long_sequence.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/test_long_sequence.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_long_sequence.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/char16trie/test_shortest_branch.toml b/deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_shortest_branch.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/char16trie/test_shortest_branch.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/char16trie/test_shortest_branch.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/free-blocks.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/free-blocks.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/free-blocks.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/free-blocks.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/free-blocks.32.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/free-blocks.32.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/free-blocks.32.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/free-blocks.32.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/free-blocks.8.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/free-blocks.8.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/free-blocks.8.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/free-blocks.8.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/free-blocks.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/free-blocks.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/free-blocks.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/free-blocks.small16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/grow-data.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/grow-data.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/grow-data.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/grow-data.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/grow-data.32.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/grow-data.32.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/grow-data.32.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/grow-data.32.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/grow-data.8.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/grow-data.8.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/grow-data.8.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/grow-data.8.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/grow-data.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/grow-data.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/grow-data.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/grow-data.small16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/planes.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/planes.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/planes.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/planes.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set-empty.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-empty.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set-empty.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-empty.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set-empty.32.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-empty.32.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set-empty.32.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-empty.32.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set-empty.8.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-empty.8.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set-empty.8.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-empty.8.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set-empty.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-empty.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set-empty.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-empty.small16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set-single-value.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-single-value.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set-single-value.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-single-value.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set-single-value.32.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-single-value.32.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set-single-value.32.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-single-value.32.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set-single-value.8.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-single-value.8.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set-single-value.8.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-single-value.8.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set-single-value.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-single-value.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set-single-value.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set-single-value.small16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set1.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set1.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set1.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set1.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set1.32.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set1.32.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set1.32.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set1.32.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set1.8.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set1.8.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set1.8.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set1.8.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set1.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set1.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set1.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set1.small16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set2-overlap.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set2-overlap.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set2-overlap.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set2-overlap.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set2-overlap.32.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set2-overlap.32.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set2-overlap.32.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set2-overlap.32.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set2-overlap.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set2-overlap.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set2-overlap.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set2-overlap.small16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set3-initial-9.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set3-initial-9.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set3-initial-9.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set3-initial-9.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set3-initial-9.32.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set3-initial-9.32.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set3-initial-9.32.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set3-initial-9.32.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set3-initial-9.8.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set3-initial-9.8.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set3-initial-9.8.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set3-initial-9.8.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/set3-initial-9.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/set3-initial-9.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/set3-initial-9.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/set3-initial-9.small16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/short-all-same.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/short-all-same.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/short-all-same.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/short-all-same.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/short-all-same.8.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/short-all-same.8.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/short-all-same.8.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/short-all-same.8.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/short-all-same.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/short-all-same.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/short-all-same.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/short-all-same.small16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/small0-in-fast.16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/small0-in-fast.16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/small0-in-fast.16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/small0-in-fast.16.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/small0-in-fast.32.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/small0-in-fast.32.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/small0-in-fast.32.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/small0-in-fast.32.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/small0-in-fast.8.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/small0-in-fast.8.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/small0-in-fast.8.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/small0-in-fast.8.toml diff --git a/deps/crates/vendor/icu_collections/tests/data/cpt/small0-in-fast.small16.toml b/deps/crates/vendor/icu_collections-v2/tests/data/cpt/small0-in-fast.small16.toml similarity index 100% rename from deps/crates/vendor/icu_collections/tests/data/cpt/small0-in-fast.small16.toml rename to deps/crates/vendor/icu_collections-v2/tests/data/cpt/small0-in-fast.small16.toml diff --git a/deps/crates/vendor/icu_collections/.cargo-checksum.json b/deps/crates/vendor/icu_collections/.cargo-checksum.json deleted file mode 100644 index c83ec60da50d..000000000000 --- a/deps/crates/vendor/icu_collections/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{".cargo_vcs_info.json":"f5ad06d6d816d1d211ec86d639b4fd2b6551db6e9bdcd37c3c7ccc47f1873618","Cargo.lock":"281b1642001f508afaf929a328b2d2801baa78b91f7e98e9aee6f39f7bd467a9","Cargo.toml":"8f7a2c13e5f27b3edcadac862f50ff5fd16689ec83bcca9a5a6bb0670918263d","Cargo.toml.orig":"963d26428f923ead6ef52219b65549eabcba96bef3e3dcb67279e6fa9c4cef3d","LICENSE":"f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2","README.md":"b1a0b37b61d42026996dda3b7d524d42888181f3e7eaf940c9b2e5f561c449e3","benches/codepointtrie.rs":"4052e9e3a3a744955a5ab3f7089cafaf2920ad5c7566fc8b3b77ee1d39376c20","benches/iai_cpt.rs":"a8ed4e67866d415e6ed0c8c95a9b1887b4198ea409d4cd9efafb50b8c13b3ecc","benches/inv_list.rs":"39e0386fd0e908d6e18b16e021345ba8c88fa70e06e26a29913dfa218a68b03d","benches/tries/gc_fast.rs":"4761a339f9b266813f4fa7ffa229b3464ced2ed106ce712ae6a96c0e49e5224e","benches/tries/gc_small.rs":"a1a75d2325dbce031e0127de8d84e900ca7c73ec81da1679965ab0127a99c205","benches/tries/mod.rs":"82cfdd1c5870d343613098fdff5a7961cfe5645a33480c6a252efae9a074e022","src/char16trie/mod.rs":"5950a1b427743e956b459956cadd72ff7531fe46bfb871d8e3a848b1e4ab0657","src/char16trie/trie.rs":"20ac75a0e2f3bcb74c619e98f44b3cc1f41bea2787ceef186d54cf997a1d9264","src/codepointinvlist/builder.rs":"bb0796333ca76d7714ee8425201a5957ab6b26b55dc049c34b3fb1839afc1581","src/codepointinvlist/conversions.rs":"8ac096ae63fa508ee2d7b2805aca864971110accaa358c178855540074e2ad21","src/codepointinvlist/cpinvlist.rs":"f9b47dd0432379637e194c2adf5acfcb91372a7f896cfab378928ffe65969532","src/codepointinvlist/mod.rs":"d48bc8ff1d103c4e37c4729181bc90b5e5e0db7701a49bd53c4d8c67e9089eb0","src/codepointinvlist/utils.rs":"6d6b4f5884beb830c16b6e5df07688f8cd70e2293a96d03037be690896d514e1","src/codepointinvliststringlist/mod.rs":"1fdb0b404f0b9a44218157ae1b58712d3ff7fe5b1d23521514a5eb36587d68df","src/codepointtrie/cptrie.rs":"dc7133751f32c74f3a87b1eb60e705aa0766a622ce48845aaa466f055267f6f2","src/codepointtrie/error.rs":"028dd55421b0bebf1634ad99752dbe349b40ab10ca565aa6bb134c5885ffd87b","src/codepointtrie/impl_const.rs":"ff25fa0d54e174c289b25a6060f50e03c8aa41bad922c9386de3148f8ad95109","src/codepointtrie/mod.rs":"d77de5edc258bbe79e0b6a37fedd315478fea6f9fd46c6923c564d045ac053d6","src/codepointtrie/planes.rs":"8a063b1efce889e46e69c7000ce48eeb420bb861f4ceafa839faaa944715c7bb","src/codepointtrie/serde.rs":"103bf57194a4d2111ed064d9bf2796b4ff7b5c2b1a1222a7378bad8143aa453f","src/codepointtrie/toml.rs":"6185421c1384e6a9a69b57153e1dcd793665ceb385dff2b61a2c1b0508609ec3","src/iterator_utils.rs":"2a5dfab19a752ae495617d68c07d11d1c0fbded233221dc43e9dd8f959001cd9","src/lib.rs":"a15ba283ccb32fc9a1cbe67166fa8eac5cd9225e4de022d09b057be4ff8eb688","tests/char16trie.rs":"f8018b90187f3b99a6a835c1c41bced18d98a28c41822abf3536cd1f0f08c44f","tests/cpt.rs":"3a151ba56273a29777f0a7656e533f4a603281569dd5349e15f837d2b9ec65e8","tests/data/char16trie/empty.toml":"b900365b9c786f1b185307f5d8835a901d4860b9b097bce13aa0e592fcb384c4","tests/data/char16trie/months.toml":"439245fc4385fe693f283b7bc179bf5558dd8039a89987314dfc8c4a9e0092b2","tests/data/char16trie/test_a.toml":"34b179dd4e7eef73ce373b74e426ecfa05fb7aa2b0fc15e437bfcf3ea4242a9f","tests/data/char16trie/test_a_ab.toml":"b84452cb9c310203599d3cb8279a3f000c39ff8026728573152026f0d5e2c798","tests/data/char16trie/test_branches.toml":"d787ffc89c62a564b487f3c8fb94588258be0d891044e5b3a79c2ab876cd3e57","tests/data/char16trie/test_compact.toml":"30cb3e794d0989e3e087c12de3db077449d82ce6af57666290166d43bdcd27d3","tests/data/char16trie/test_long_branch.toml":"877d408f7d58063723befd01af6e3b0f01060a9ca1c5611ee1ea88d73383fa5a","tests/data/char16trie/test_long_sequence.toml":"f58645b3f14e47850e2a03733f7e47b69b193067f4cccf96124846b1ad49d970","tests/data/char16trie/test_shortest_branch.toml":"af356553506dbc28a2413b9f0ca1489050cd11a72e90ba4b5020bcb596862e64","tests/data/cpt/free-blocks.16.toml":"e0a66e777c13c0885b6a0f6839cdc9bbbddded2f5fbef4710f8e69a0808791d9","tests/data/cpt/free-blocks.32.toml":"b0bb6068a84d08d3fbcbd54dce7a59385a42b2dcdf0a6fe9d4160754a80a5f9e","tests/data/cpt/free-blocks.8.toml":"2de9950cd4475c3cb9cca9c30cb6793b90f3881ea2dd1353bea0701110c76953","tests/data/cpt/free-blocks.small16.toml":"17cbdef8edec19e8d85802eda775b1095ae2d423a589a4b56064b3b13604af36","tests/data/cpt/grow-data.16.toml":"802c7c61c2bb70dd7c96ce4633584a2ac4d2e6a61ecfb6f5d19c3c419b76f97c","tests/data/cpt/grow-data.32.toml":"3cbf6ad1276eda17f18cbeba4db671340ebec0fe6c0b631ea1f52dab63669881","tests/data/cpt/grow-data.8.toml":"e2d0b066e8a0f0bbf21cffa72393e0fd8c9a7534887886438211600bf7717129","tests/data/cpt/grow-data.small16.toml":"1d4c5d4b47a3da6b392729be3aa7cfa2cfb58d9ae37565cf10dbc10f0b48beca","tests/data/cpt/planes.toml":"a2577942758490bd53f4022b30731cff947fae22f4cbbb03b5ae72c65357aa12","tests/data/cpt/set-empty.16.toml":"6a01c051c5dbbe816097da806b943ca1856312059b0842a1238fe75ad5df220b","tests/data/cpt/set-empty.32.toml":"ec9dec0296b150ccabde07c255e2971ba407f647824a7c37af0272fa2ba89e2d","tests/data/cpt/set-empty.8.toml":"478dd49db8670f447f5aae78bcd3cdbeb6f91f7861dec256d480623b5d00941b","tests/data/cpt/set-empty.small16.toml":"5ff232c9b020bdaaaf762f7c0afe48ccb6f94b40dce08c1ba7dcb66c46ad2423","tests/data/cpt/set-single-value.16.toml":"6720f4a4d8c113ab7085e6308792ea0005cc8fe3c5d8acacb2cf43e74b12f4b3","tests/data/cpt/set-single-value.32.toml":"cb93935dc9ce5ed3cbe0f0acb50b49308b232e2d3fc8e39b08d1c33127c9911d","tests/data/cpt/set-single-value.8.toml":"e5cb6b02341bb8992ec93e49a7b2b059af1fd046ede3021dc207f1833c6a3c95","tests/data/cpt/set-single-value.small16.toml":"3aef34147b20a5c2c2c6e20f6024d38f7c0518313371b05d3cb83ae78a9ef0db","tests/data/cpt/set1.16.toml":"01cc1078f281102bd0c9e854a373b7241f2295bb19caa9488caad1b5350d0beb","tests/data/cpt/set1.32.toml":"cd264d011692df42e9007df87c6500bdde4e098a3a85a89a185f7deeff025a3b","tests/data/cpt/set1.8.toml":"039aa2fca8f140f5a060bcf01a1da3d45618242dfb0a3c437d4263d5166398a3","tests/data/cpt/set1.small16.toml":"b8bf6e216201e0a0c61c4b2456eccd1c3b4f38647d67ba251cd7b08f340df0dc","tests/data/cpt/set2-overlap.16.toml":"49f1d7212cb3f7b10f4a0dbab267b64aeb59380cc28d86c67fd640737da5af0a","tests/data/cpt/set2-overlap.32.toml":"406104b0532ae249c603c03729f4596ab24460ccbb3a244e04fa2b8401795ef0","tests/data/cpt/set2-overlap.small16.toml":"4e8d9c5b25cda2fd80fa72e7e9863e6858548608d3fe2a2d774026eda95c959f","tests/data/cpt/set3-initial-9.16.toml":"f5fdc3d7c6d6813cc527ddffecf35db2d144e2706be0541277bbe4672ea29851","tests/data/cpt/set3-initial-9.32.toml":"da2887d40005fc071eee1bf4e828f40410266c44585deda208e78d369b5ca9a9","tests/data/cpt/set3-initial-9.8.toml":"5974d1961bd8334e6fb1be7431f794dfc87d0fecdee4b832ad1c82e2302a6617","tests/data/cpt/set3-initial-9.small16.toml":"f62d46d2b6492c02944d089885b2ce6b975468e6bceabf986b495ab42a06d595","tests/data/cpt/short-all-same.16.toml":"5c0252591699a35398f75b3bec977d6ed44cfffbf335a87ed7c3cff86ff74391","tests/data/cpt/short-all-same.8.toml":"23f7aa3a8e6c3cc75669b8df55827b03c6791aac1ec9f7c8d7f0bbbdd54f23a9","tests/data/cpt/short-all-same.small16.toml":"5c0252591699a35398f75b3bec977d6ed44cfffbf335a87ed7c3cff86ff74391","tests/data/cpt/small0-in-fast.16.toml":"8d00f5657ee11c3b00c7a3c1e6333632b37dba0762cd4868b2f8ef90c03aab6c","tests/data/cpt/small0-in-fast.32.toml":"4736b4ae18cb0e77adc523382ec44bc517a01e17abdb91522b8859d77fbdc7c6","tests/data/cpt/small0-in-fast.8.toml":"e9214d75c421989eb79b7060817f51fa1d648d55b854fad651ff729962bc49e4","tests/data/cpt/small0-in-fast.small16.toml":"20eb07e89364a1fd6ddf26f3bc302e92c1aaa724be792d9bf8656e4e5a7a6379"},"package":"4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"} \ No newline at end of file diff --git a/deps/crates/vendor/icu_collections/.cargo_vcs_info.json b/deps/crates/vendor/icu_collections/.cargo_vcs_info.json deleted file mode 100644 index 4364a1d8632a..000000000000 --- a/deps/crates/vendor/icu_collections/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "38a49da495248dd1ded84cf306e4ca42e64d5bb3" - }, - "path_in_vcs": "components/collections" -} \ No newline at end of file diff --git a/deps/crates/vendor/icu_locale-v2/.cargo-checksum.json b/deps/crates/vendor/icu_locale-v2/.cargo-checksum.json new file mode 100644 index 000000000000..697c9ce2fbb4 --- /dev/null +++ b/deps/crates/vendor/icu_locale-v2/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{}} diff --git a/deps/crates/vendor/litemap/.cargo_vcs_info.json b/deps/crates/vendor/icu_locale-v2/.cargo_vcs_info.json similarity index 66% rename from deps/crates/vendor/litemap/.cargo_vcs_info.json rename to deps/crates/vendor/icu_locale-v2/.cargo_vcs_info.json index fbadd4056443..0064f117e0b1 100644 --- a/deps/crates/vendor/litemap/.cargo_vcs_info.json +++ b/deps/crates/vendor/icu_locale-v2/.cargo_vcs_info.json @@ -2,5 +2,5 @@ "git": { "sha1": "c9fac4e625ccb2c6a7aa35079fff9709db4385ac" }, - "path_in_vcs": "utils/litemap" + "path_in_vcs": "components/locale" } \ No newline at end of file diff --git a/deps/crates/vendor/icu_locale/Cargo.lock b/deps/crates/vendor/icu_locale-v2/Cargo.lock similarity index 79% rename from deps/crates/vendor/icu_locale/Cargo.lock rename to deps/crates/vendor/icu_locale-v2/Cargo.lock index 921a35c9b342..245cb515af5f 100644 --- a/deps/crates/vendor/icu_locale/Cargo.lock +++ b/deps/crates/vendor/icu_locale-v2/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -19,9 +19,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "autocfg" @@ -31,9 +31,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "cast" @@ -177,9 +177,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "databake" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6ee9e2d2afb173bcdeee45934c89ec341ab26f91c9933774fc15c2b58f83ef" +checksum = "74d4b1db5ca40636726f1f73daff0d626accbd49bcd8136fcade87d7cf1e6bbb" dependencies = [ "databake-derive", "proc-macro2", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "databake-derive" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6834770958c7b84223607e49758ec0dde273c4df915e734aad50f62968a4c134" +checksum = "72b537745234cbf0e296a3bd836d70a614dff4cb522b14e2680ef006bb1ed5ff" dependencies = [ "proc-macro2", "quote", @@ -217,9 +217,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "erased-serde" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" dependencies = [ "serde", "serde_core", @@ -244,14 +244,15 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "databake", "displaydoc", "potential_utf", "serde", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -259,7 +260,7 @@ dependencies = [ [[package]] name = "icu_locale" -version = "2.1.1" +version = "2.2.0" dependencies = [ "criterion", "databake", @@ -276,9 +277,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "databake", "displaydoc", @@ -291,15 +292,15 @@ dependencies = [ [[package]] name = "icu_locale_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03e2fcaefecdf05619f3d6f91740e79ab969b4dd54f77cbf546b1d0d28e3147" +checksum = "d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "databake", "displaydoc", @@ -337,15 +338,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -353,27 +354,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "log" -version = "0.4.28" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "num-traits" @@ -386,9 +381,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "oorandom" @@ -436,9 +431,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "serde_core", "writeable", @@ -447,18 +442,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -485,9 +480,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -497,9 +492,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -508,9 +503,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rustversion" @@ -518,12 +513,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -565,15 +554,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -584,9 +573,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "syn" -version = "2.0.108" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -606,18 +595,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -626,9 +615,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "databake", "displaydoc", @@ -654,9 +643,15 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "unicode-ident" -version = "1.0.20" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "walkdir" @@ -670,9 +665,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -681,25 +676,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -707,31 +688,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -769,9 +750,9 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -780,9 +761,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -792,18 +773,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -813,18 +794,19 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", + "zerovec", ] [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "databake", "serde", @@ -835,11 +817,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/deps/crates/vendor/icu_locale/Cargo.toml b/deps/crates/vendor/icu_locale-v2/Cargo.toml similarity index 64% rename from deps/crates/vendor/icu_locale/Cargo.toml rename to deps/crates/vendor/icu_locale-v2/Cargo.toml index 270bc9bfc418..ea69b0cb6a28 100644 --- a/deps/crates/vendor/icu_locale/Cargo.toml +++ b/deps/crates/vendor/icu_locale-v2/Cargo.toml @@ -11,9 +11,9 @@ [package] edition = "2021" -rust-version = "1.83" +rust-version = "1.86" name = "icu_locale" -version = "2.1.1" +version = "2.2.0" authors = ["The ICU4X Project Developers"] build = false include = [ @@ -35,7 +35,17 @@ autobenches = false description = "API for Unicode Language and Locale Identifiers canonicalization" homepage = "https://icu4x.unicode.org" readme = "README.md" -categories = ["internationalization"] +keywords = [ + "unicode", + "language-tags", + "bcp47", +] +categories = [ + "internationalization", + "localization", + "no-std", + "embedded", +] license = "Unicode-3.0" repository = "https://github.com/unicode-org/icu4x" @@ -92,11 +102,11 @@ optional = true default-features = false [dependencies.icu_collections] -version = "~2.1.1" +version = "~2.2.0" default-features = false [dependencies.icu_locale_core] -version = "2.1.1" +version = "2.2.0" features = [ "alloc", "zerovec", @@ -104,12 +114,12 @@ features = [ default-features = false [dependencies.icu_locale_data] -version = "~2.1.1" +version = "~2.2.0" optional = true default-features = false [dependencies.icu_provider] -version = "2.1.1" +version = "2.2.0" features = ["alloc"] default-features = false @@ -131,7 +141,7 @@ optional = true default-features = false [dependencies.tinystr] -version = "0.8.0" +version = "0.8.3" features = [ "alloc", "zerovec", @@ -139,7 +149,7 @@ features = [ default-features = false [dependencies.zerovec] -version = "0.11.3" +version = "0.11.6" features = [ "alloc", "yoke", @@ -156,3 +166,52 @@ version = "1.0.45" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies.criterion] version = "0.5.0" + +[lints.clippy] +alloc-instead-of-core = "warn" +branches-sharing-code = "warn" +collection_is_never_read = "warn" +crosspointer_transmute = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +exhaustive_enums = "deny" +exhaustive_structs = "deny" +fn_to_numeric_cast_any = "warn" +infinite_loop = "warn" +large_stack_arrays = "warn" +mismatching_type_param_order = "warn" +missing_fields_in_debug = "warn" +missing_transmute_annotations = "warn" +negative_feature_names = "warn" +or-fun-call = "warn" +same_functions_in_if_condition = "warn" +todo = "warn" +transmute_bytes_to_str = "warn" +transmute_int_to_bool = "warn" +transmute_int_to_non_zero = "warn" +transmute_ptr_to_ptr = "warn" +transmute_ptr_to_ref = "warn" +transmute_undefined_repr = "warn" +transmutes_expressible_as_ptr_casts = "warn" +trivially_copy_pass_by_ref = "deny" +unnecessary-wraps = "warn" +useless_transmute = "warn" +wildcard_dependencies = "warn" + +[lints.rust] +missing_debug_implementations = "deny" +trivial_numeric_casts = "deny" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[lints.rust.unexpected_cfgs] +level = "warn" +priority = 0 +check-cfg = [ + "cfg(icu4c_enable_renaming)", + "cfg(needs_alloc_error_handler)", + "cfg(icu4x_run_size_tests)", + "cfg(icu4x_unstable_fast_trie_only)", +] diff --git a/deps/crates/vendor/icu_locale/Cargo.toml.orig b/deps/crates/vendor/icu_locale-v2/Cargo.toml.orig similarity index 96% rename from deps/crates/vendor/icu_locale/Cargo.toml.orig rename to deps/crates/vendor/icu_locale-v2/Cargo.toml.orig index 1d96b0151d25..41e83c924d50 100644 --- a/deps/crates/vendor/icu_locale/Cargo.toml.orig +++ b/deps/crates/vendor/icu_locale-v2/Cargo.toml.orig @@ -5,9 +5,10 @@ [package] name = "icu_locale" description = "API for Unicode Language and Locale Identifiers canonicalization" +categories.workspace = true +keywords = ["unicode", "language-tags", "bcp47"] authors.workspace = true -categories.workspace = true edition.workspace = true homepage.workspace = true include.workspace = true @@ -60,3 +61,6 @@ harness = false [[test]] name = "locale_canonicalizer" required-features = ["serde"] + +[lints] +workspace = true diff --git a/deps/crates/vendor/icu_collections/LICENSE b/deps/crates/vendor/icu_locale-v2/LICENSE similarity index 100% rename from deps/crates/vendor/icu_collections/LICENSE rename to deps/crates/vendor/icu_locale-v2/LICENSE diff --git a/deps/crates/vendor/icu_locale-v2/OWNERS b/deps/crates/vendor/icu_locale-v2/OWNERS new file mode 100644 index 000000000000..07a258f68f0b --- /dev/null +++ b/deps/crates/vendor/icu_locale-v2/OWNERS @@ -0,0 +1,2 @@ +# This file has been auto-generated by the `gnrt` tool. +file://third_party/rust/icu_locale/OWNERS diff --git a/deps/crates/vendor/icu_locale/README.md b/deps/crates/vendor/icu_locale-v2/README.md similarity index 94% rename from deps/crates/vendor/icu_locale/README.md rename to deps/crates/vendor/icu_locale-v2/README.md index 80f92c8af984..418237748bcb 100644 --- a/deps/crates/vendor/icu_locale/README.md +++ b/deps/crates/vendor/icu_locale-v2/README.md @@ -64,9 +64,9 @@ assert_eq!(locale, locale!("zh")); ``` [`ICU4X`]: ../icu/index.html -[`CLDR`]: http://cldr.unicode.org/ +[`CLDR`]: https://cldr.unicode.org/ [`UTS #35: Unicode LDML 3. Likely Subtags`]: https://www.unicode.org/reports/tr35/#Likely_Subtags. -[`UTS #35: Unicode LDML 3. LocaleId Canonicalization`]: http://unicode.org/reports/tr35/#LocaleId_Canonicalization, +[`UTS #35: Unicode LDML 3. LocaleId Canonicalization`]: https://unicode.org/reports/tr35/#LocaleId_Canonicalization, diff --git a/deps/crates/vendor/icu_locale/benches/fixtures/locales.json b/deps/crates/vendor/icu_locale-v2/benches/fixtures/locales.json similarity index 100% rename from deps/crates/vendor/icu_locale/benches/fixtures/locales.json rename to deps/crates/vendor/icu_locale-v2/benches/fixtures/locales.json diff --git a/deps/crates/vendor/icu_locale/benches/fixtures/uncanonicalized-locales.json b/deps/crates/vendor/icu_locale-v2/benches/fixtures/uncanonicalized-locales.json similarity index 100% rename from deps/crates/vendor/icu_locale/benches/fixtures/uncanonicalized-locales.json rename to deps/crates/vendor/icu_locale-v2/benches/fixtures/uncanonicalized-locales.json diff --git a/deps/crates/vendor/icu_locale/benches/locale_canonicalizer.rs b/deps/crates/vendor/icu_locale-v2/benches/locale_canonicalizer.rs similarity index 100% rename from deps/crates/vendor/icu_locale/benches/locale_canonicalizer.rs rename to deps/crates/vendor/icu_locale-v2/benches/locale_canonicalizer.rs diff --git a/deps/crates/vendor/icu_locale/src/canonicalizer.rs b/deps/crates/vendor/icu_locale-v2/src/canonicalizer.rs similarity index 98% rename from deps/crates/vendor/icu_locale/src/canonicalizer.rs rename to deps/crates/vendor/icu_locale-v2/src/canonicalizer.rs index ea6a14a3d437..880a7fdc9b76 100644 --- a/deps/crates/vendor/icu_locale/src/canonicalizer.rs +++ b/deps/crates/vendor/icu_locale-v2/src/canonicalizer.rs @@ -35,7 +35,7 @@ use tinystr::TinyAsciiStr; /// assert_eq!(locale, "ja-Latn-alalc97-fonipa".parse().unwrap()); /// ``` /// -/// [UTS #35: Annex C, LocaleId Canonicalization]: http://unicode.org/reports/tr35/#LocaleId_Canonicalization +/// [UTS #35: Annex C, LocaleId Canonicalization]: https://unicode.org/reports/tr35/#LocaleId_Canonicalization #[derive(Debug)] pub struct LocaleCanonicalizer { /// Data to support canonicalization. @@ -270,9 +270,7 @@ impl> LocaleCanonicalizer { #[cfg(feature = "compiled_data")] pub const fn new_with_expander(expander: Expander) -> Self { Self { - aliases: DataPayload::from_static_ref( - crate::provider::Baked::SINGLETON_LOCALE_ALIASES_V1, - ), + aliases: DataPayload::from_static_ref(Baked::SINGLETON_LOCALE_ALIASES_V1), expander, } } @@ -301,7 +299,7 @@ impl> LocaleCanonicalizer { /// The canonicalize method potentially updates a passed in locale in place /// depending up the results of running the canonicalization algorithm - /// from . + /// from . /// /// Some BCP47 canonicalization data is not part of the CLDR json package. Because /// of this, some canonicalizations are not performed, e.g. the canonicalization of diff --git a/deps/crates/vendor/icu_locale/src/directionality.rs b/deps/crates/vendor/icu_locale-v2/src/directionality.rs similarity index 99% rename from deps/crates/vendor/icu_locale/src/directionality.rs rename to deps/crates/vendor/icu_locale-v2/src/directionality.rs index a746b57142d3..6a2464645d3e 100644 --- a/deps/crates/vendor/icu_locale/src/directionality.rs +++ b/deps/crates/vendor/icu_locale-v2/src/directionality.rs @@ -130,7 +130,7 @@ impl> LocaleDirectionality { pub const fn new_with_expander(expander: Expander) -> Self { LocaleDirectionality { script_direction: DataPayload::from_static_ref( - crate::provider::Baked::SINGLETON_LOCALE_SCRIPT_DIRECTION_V1, + Baked::SINGLETON_LOCALE_SCRIPT_DIRECTION_V1, ), expander, } diff --git a/deps/crates/vendor/icu_locale/src/exemplar_chars.rs b/deps/crates/vendor/icu_locale-v2/src/exemplar_chars.rs similarity index 100% rename from deps/crates/vendor/icu_locale/src/exemplar_chars.rs rename to deps/crates/vendor/icu_locale-v2/src/exemplar_chars.rs diff --git a/deps/crates/vendor/icu_locale/src/expander.rs b/deps/crates/vendor/icu_locale-v2/src/expander.rs similarity index 97% rename from deps/crates/vendor/icu_locale/src/expander.rs rename to deps/crates/vendor/icu_locale-v2/src/expander.rs index f1d6684dd4a6..6b921b44fecb 100644 --- a/deps/crates/vendor/icu_locale/src/expander.rs +++ b/deps/crates/vendor/icu_locale-v2/src/expander.rs @@ -224,10 +224,10 @@ impl LocaleExpander { pub const fn new_common() -> Self { LocaleExpander { likely_subtags_l: DataPayload::from_static_ref( - crate::provider::Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_LANGUAGE_V1, + Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_LANGUAGE_V1, ), likely_subtags_sr: DataPayload::from_static_ref( - crate::provider::Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_SCRIPT_REGION_V1, + Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_SCRIPT_REGION_V1, ), likely_subtags_ext: None, } @@ -272,13 +272,13 @@ impl LocaleExpander { pub const fn new_extended() -> Self { LocaleExpander { likely_subtags_l: DataPayload::from_static_ref( - crate::provider::Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_LANGUAGE_V1, + Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_LANGUAGE_V1, ), likely_subtags_sr: DataPayload::from_static_ref( - crate::provider::Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_SCRIPT_REGION_V1, + Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_SCRIPT_REGION_V1, ), likely_subtags_ext: Some(DataPayload::from_static_ref( - crate::provider::Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_EXTENDED_V1, + Baked::SINGLETON_LOCALE_LIKELY_SUBTAGS_EXTENDED_V1, )), } } diff --git a/deps/crates/vendor/icu_locale/src/fallback/algorithms.rs b/deps/crates/vendor/icu_locale-v2/src/fallback/algorithms.rs similarity index 93% rename from deps/crates/vendor/icu_locale/src/fallback/algorithms.rs rename to deps/crates/vendor/icu_locale-v2/src/fallback/algorithms.rs index 7e9e5f0419cf..7b7ab742b059 100644 --- a/deps/crates/vendor/icu_locale/src/fallback/algorithms.rs +++ b/deps/crates/vendor/icu_locale-v2/src/fallback/algorithms.rs @@ -9,12 +9,13 @@ use super::*; impl LocaleFallbackerWithConfig<'_> { pub(crate) fn normalize(&self, locale: &mut DataLocale, default_script: &mut Option