Skip to content

Commit 1721b72

Browse files
committed
lib: add primitive fast path to structuredClone
1 parent af100d1 commit 1721b72

3 files changed

Lines changed: 28 additions & 1 deletion

File tree

benchmark/misc/structured-clone.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@ const common = require('../common.js');
44
const assert = require('assert');
55

66
const bench = common.createBenchmark(main, {
7-
type: ['string', 'object', 'arraybuffer'],
7+
type: ['primitive', 'string', 'object', 'arraybuffer'],
88
n: [1e4],
99
});
1010

1111
function main({ n, type }) {
1212
const data = [];
1313

1414
switch (type) {
15+
case 'primitive':
16+
for (let i = 0; i < n; ++i) {
17+
data.push(i);
18+
}
19+
break;
1520
case 'string':
1621
for (let i = 0; i < n; ++i) {
1722
data.push(new Date().toISOString());

lib/internal/worker/js_transferable.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,17 @@ function structuredClone(value, options) {
115115
throw new ERR_MISSING_ARGS('The value argument must be specified');
116116
}
117117

118+
// Fast path: primitives clone to themselves, so skip the native
119+
// serialize/deserialize round-trip when no options were provided.
120+
// Symbols are excluded. they are not cloneable and must throw below.
121+
if (options === undefined &&
122+
(value === null ||
123+
(typeof value !== 'object' &&
124+
typeof value !== 'function' &&
125+
typeof value !== 'symbol'))) {
126+
return value;
127+
}
128+
118129
const idlOptions = webidl.converters.StructuredSerializeOptions(
119130
options,
120131
{

test/parallel/test-structuredClone-global.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ assert.strictEqual(structuredClone(undefined, null), undefined);
2727
// Transfer can be null or undefined.
2828
assert.strictEqual(structuredClone(undefined, { }), undefined);
2929

30+
// Primitives clone to themselves.
31+
assert.strictEqual(structuredClone(null), null);
32+
assert.strictEqual(structuredClone(1), 1);
33+
assert.strictEqual(structuredClone('x'), 'x');
34+
assert.strictEqual(structuredClone(true), true);
35+
assert.strictEqual(structuredClone(10n), 10n);
36+
// -0 is preserved.
37+
assert.ok(Object.is(structuredClone(-0), -0));
38+
// Symbols are not cloneable and must throw.
39+
assert.throws(() => structuredClone(Symbol()), { name: 'DataCloneError' });
40+
3041
// Transferables or its subclasses should be received with its closest transferable superclass
3142
for (const StreamClass of [ReadableStream, WritableStream, TransformStream]) {
3243
const original = new StreamClass();

0 commit comments

Comments
 (0)