diff --git a/benchmark/misc/structured-clone.js b/benchmark/misc/structured-clone.js index 257671df44b07f..38c0d0aed232b3 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 0ae80d9a728725..f7b87097f20702 100644 --- a/lib/internal/worker/js_transferable.js +++ b/lib/internal/worker/js_transferable.js @@ -115,6 +115,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 09e36ad47940ea..c0f8137a1ed8a3 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();