Skip to content

Commit d0a8986

Browse files
Ethan-Arrowoodjasnell
authored andcommitted
lib: add structuredClone() global
PR-URL: #39759Fixes: #39713 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
1 parent 21cf618 commit d0a8986

16 files changed

Lines changed: 774 additions & 1 deletion

‎.eslintrc.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,5 +362,6 @@ module.exports = {
362362
btoa: 'readable',
363363
atob: 'readable',
364364
performance: 'readable',
365+
structuredClone: 'readable',
365366
},
366367
};

‎lib/.eslintrc.yaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ rules:
8181
message: "Use `const { performance } = require('perf_hooks');` instead of the global."
8282
- name: queueMicrotask
8383
message: "Use `const { queueMicrotask } = require('internal/process/task_queues');` instead of the global."
84+
- name: structuredClone
85+
message: "Use `const { structuredClone } = require('internal/structured_clone');` instead of the global."
8486
# Custom rules in tools/eslint-rules
8587
node-core/lowercase-name-for-primitive: error
8688
node-core/non-ascii-character: error

‎lib/internal/bootstrap/node.js‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,11 @@ if (!config.noBrowserGlobals) {
254254
// Non-standard extensions:
255255
defineOperation(globalThis,'clearImmediate',timers.clearImmediate);
256256
defineOperation(globalThis,'setImmediate',timers.setImmediate);
257+
258+
const{
259+
structuredClone,
260+
}=require('internal/structured_clone');
261+
defineOperation(globalThis,'structuredClone',structuredClone);
257262
}
258263

259264
// Set the per-Environment callback that will be called

‎lib/internal/structured_clone.js‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
3+
const{
4+
MessageChannel,
5+
receiveMessageOnPort,
6+
}=require('internal/worker/io');
7+
8+
letchannel;
9+
functionstructuredClone(value,transfer){
10+
// TODO: Improve this with a more efficient solution that avoids
11+
// instantiating a MessageChannel
12+
channel??=newMessageChannel();
13+
channel.port1.unref();
14+
channel.port2.unref();
15+
channel.port1.postMessage(value,transfer);
16+
returnreceiveMessageOnPort(channel.port2).message;
17+
}
18+
19+
module.exports={
20+
structuredClone
21+
};

‎test/common/index.js‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,13 @@ if (global.PerformanceMeasure) {
293293
knownGlobals.push(global.PerformanceMeasure);
294294
}
295295

296+
// TODO(@ethan-arrowood): Similar to previous checks, this can be temporary
297+
// until v16.x is EOL. Once all supported versions have structuredClone we
298+
// can add this to the list above instead.
299+
if(global.structuredClone){
300+
knownGlobals.push(global.structuredClone);
301+
}
302+
296303
functionallowGlobals(...allowlist){
297304
knownGlobals=knownGlobals.concat(allowlist);
298305
}

‎test/fixtures/wpt/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Last update:
1818
- hr-time: https://github.com/web-platform-tests/wpt/tree/9910784394/hr-time
1919
- html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob
2020
- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing
21+
- html/webappapis/structured-clone: https://github.com/web-platform-tests/wpt/tree/47d3fb280c/html/webappapis/structured-clone
2122
- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/5873f2d8f1/html/webappapis/timers
2223
- interfaces: https://github.com/web-platform-tests/wpt/tree/fc086c82d5/interfaces
2324
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Runs a collection of tests that determine if an API implements structured clone
3+
* correctly.
4+
*
5+
* The `runner` parameter has the following properties:
6+
* - `setup()`: An optional function run once before testing starts
7+
* - `teardown()`: An option function run once after all tests are done
8+
* - `preTest()`: An optional, async function run before a test
9+
* - `postTest()`: An optional, async function run after a test is done
10+
* - `structuredClone(obj, transferList)`: Required function that somehow
11+
* structurally clones an object.
12+
* - `hasDocument`: When true, disables tests that require a document. True by default.
13+
*/
14+
15+
functionrunStructuredCloneBatteryOfTests(runner){
16+
constdefaultRunner={
17+
setup(){},
18+
preTest(){},
19+
postTest(){},
20+
teardown(){},
21+
hasDocument: true
22+
};
23+
runner=Object.assign({},defaultRunner,runner);
24+
25+
letsetupPromise=runner.setup();
26+
constallTests=structuredCloneBatteryOfTests.map(test=>{
27+
28+
if(!runner.hasDocument&&test.requiresDocument){
29+
return;
30+
}
31+
32+
returnnewPromise(resolve=>{
33+
promise_test(async_=>{
34+
test=awaittest;
35+
awaitsetupPromise;
36+
awaitrunner.preTest(test);
37+
awaittest.f(runner)
38+
awaitrunner.postTest(test);
39+
resolve();
40+
},test.description);
41+
}).catch(_=>{});
42+
});
43+
Promise.all(allTests).then(_=>runner.teardown());
44+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
structuredCloneBatteryOfTests.push({
2+
description: 'ArrayBuffer',
3+
asyncf(runner){
4+
constbuffer=newUint8Array([1]).buffer;
5+
constcopy=awaitrunner.structuredClone(buffer,[buffer]);
6+
assert_equals(buffer.byteLength,0);
7+
assert_equals(copy.byteLength,1);
8+
}
9+
});
10+
11+
structuredCloneBatteryOfTests.push({
12+
description: 'MessagePort',
13+
asyncf(runner){
14+
const{port1, port2}=newMessageChannel();
15+
constcopy=awaitrunner.structuredClone(port2,[port2]);
16+
constmsg=newPromise(resolve=>port1.onmessage=resolve);
17+
copy.postMessage('ohai');
18+
assert_equals((awaitmsg).data,'ohai');
19+
}
20+
});
21+
22+
// TODO: ImageBitmap

0 commit comments

Comments
 (0)