Commit bfb84d3eb15 for nodejs
commit bfb84d3eb1565879da5730f8fa3cbebe9ee1f84f
Author: Guilherme Araújo <arauujogui@gmail.com>
Date: Sun Sep 20 18:03:24 2026 -0300
lib: add primitive fast path to structuredClone
Signed-off-by: Guilherme Araújo <arauujogui@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/64190
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Ilyas Shabi <ilyasshabi94@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: James M Snell <jasnell@gmail.com>
diff --git a/benchmark/misc/structured-clone.js b/benchmark/misc/structured-clone.js
index 257671df44b..38c0d0aed23 100644
--- a/benchmark/misc/structured-clone.js
+++ b/benchmark/misc/structured-clone.js
@@ -4,7 +4,7 @@ const common = require('../common.js');
const assert = require('assert');
const bench = common.createBenchmark(main, {
- type: ['string', 'object', 'arraybuffer'],
+ type: ['int', 'string', 'object', 'arraybuffer'],
n: [1e4],
});
@@ -12,6 +12,11 @@ function main({ n, type }) {
const data = [];
switch (type) {
+ case 'int':
+ for (let i = 0; i < n; ++i) {
+ data.push(i);
+ }
+ break;
case 'string':
for (let i = 0; i < n; ++i) {
data.push(new Date().toISOString());
diff --git a/lib/internal/worker/js_transferable.js b/lib/internal/worker/js_transferable.js
index bdd0d648996..f7b21b34595 100644
--- a/lib/internal/worker/js_transferable.js
+++ b/lib/internal/worker/js_transferable.js
@@ -103,6 +103,17 @@ function structuredClone(value, options) {
throw new ERR_MISSING_ARGS('The value argument must be specified');
}
+ // Fast path: primitives clone to themselves, so skip the native
+ // serialize/deserialize round-trip when no options were provided.
+ // Symbols are excluded. they are not cloneable and must throw below.
+ if (options === undefined &&
+ (value === null ||
+ (typeof value !== 'object' &&
+ typeof value !== 'function' &&
+ typeof value !== 'symbol'))) {
+ return value;
+ }
+
const idlOptions = webidl.converters.StructuredSerializeOptions(
options,
{
diff --git a/test/parallel/test-structuredClone-global.js b/test/parallel/test-structuredClone-global.js
index 09e36ad4794..c0f8137a1ed 100644
--- a/test/parallel/test-structuredClone-global.js
+++ b/test/parallel/test-structuredClone-global.js
@@ -27,6 +27,17 @@ assert.strictEqual(structuredClone(undefined, null), undefined);
// Transfer can be null or undefined.
assert.strictEqual(structuredClone(undefined, { }), undefined);
+// Primitives clone to themselves.
+assert.strictEqual(structuredClone(null), null);
+assert.strictEqual(structuredClone(1), 1);
+assert.strictEqual(structuredClone('x'), 'x');
+assert.strictEqual(structuredClone(true), true);
+assert.strictEqual(structuredClone(10n), 10n);
+// -0 is preserved.
+assert.ok(Object.is(structuredClone(-0), -0));
+// Symbols are not cloneable and must throw.
+assert.throws(() => structuredClone(Symbol()), { name: 'DataCloneError' });
+
// Transferables or its subclasses should be received with its closest transferable superclass
for (const StreamClass of [ReadableStream, WritableStream, TransformStream]) {
const original = new StreamClass();