Skip to content

Commit

Permalink
lib: add structuredClone() global
Browse files Browse the repository at this point in the history
PR-URL: #39759
Fixes: #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>
  • Loading branch information
Ethan-Arrowood authored and jasnell committed Aug 28, 2021
1 parent 21cf618 commit d0a8986
Show file tree
Hide file tree
Showing 16 changed files with 774 additions and 1 deletion.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,5 +362,6 @@ module.exports = {
btoa: 'readable',
atob: 'readable',
performance: 'readable',
structuredClone: 'readable',
},
};
2 changes: 2 additions & 0 deletions lib/.eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ rules:
message: "Use `const { performance } = require('perf_hooks');` instead of the global."
- name: queueMicrotask
message: "Use `const { queueMicrotask } = require('internal/process/task_queues');` instead of the global."
- name: structuredClone
message: "Use `const { structuredClone } = require('internal/structured_clone');` instead of the global."
# Custom rules in tools/eslint-rules
node-core/lowercase-name-for-primitive: error
node-core/non-ascii-character: error
Expand Down
5 changes: 5 additions & 0 deletions lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ if (!config.noBrowserGlobals) {
// Non-standard extensions:
defineOperation(globalThis, 'clearImmediate', timers.clearImmediate);
defineOperation(globalThis, 'setImmediate', timers.setImmediate);

const {
structuredClone,
} = require('internal/structured_clone');
defineOperation(globalThis, 'structuredClone', structuredClone);
}

// Set the per-Environment callback that will be called
Expand Down
21 changes: 21 additions & 0 deletions lib/internal/structured_clone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const {
MessageChannel,
receiveMessageOnPort,
} = require('internal/worker/io');

let channel;
function structuredClone(value, transfer) {
// TODO: Improve this with a more efficient solution that avoids
// instantiating a MessageChannel
channel ??= new MessageChannel();
channel.port1.unref();
channel.port2.unref();
channel.port1.postMessage(value, transfer);
return receiveMessageOnPort(channel.port2).message;
}

module.exports = {
structuredClone
};
7 changes: 7 additions & 0 deletions test/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,13 @@ if (global.PerformanceMeasure) {
knownGlobals.push(global.PerformanceMeasure);
}

// TODO(@ethan-arrowood): Similar to previous checks, this can be temporary
// until v16.x is EOL. Once all supported versions have structuredClone we
// can add this to the list above instead.
if (global.structuredClone) {
knownGlobals.push(global.structuredClone);
}

function allowGlobals(...allowlist) {
knownGlobals = knownGlobals.concat(allowlist);
}
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/wpt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Last update:
- hr-time: https://github.com/web-platform-tests/wpt/tree/9910784394/hr-time
- html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob
- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing
- html/webappapis/structured-clone: https://github.com/web-platform-tests/wpt/tree/47d3fb280c/html/webappapis/structured-clone
- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/5873f2d8f1/html/webappapis/timers
- interfaces: https://github.com/web-platform-tests/wpt/tree/fc086c82d5/interfaces
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Runs a collection of tests that determine if an API implements structured clone
* correctly.
*
* The `runner` parameter has the following properties:
* - `setup()`: An optional function run once before testing starts
* - `teardown()`: An option function run once after all tests are done
* - `preTest()`: An optional, async function run before a test
* - `postTest()`: An optional, async function run after a test is done
* - `structuredClone(obj, transferList)`: Required function that somehow
* structurally clones an object.
* - `hasDocument`: When true, disables tests that require a document. True by default.
*/

function runStructuredCloneBatteryOfTests(runner) {
const defaultRunner = {
setup() {},
preTest() {},
postTest() {},
teardown() {},
hasDocument: true
};
runner = Object.assign({}, defaultRunner, runner);

let setupPromise = runner.setup();
const allTests = structuredCloneBatteryOfTests.map(test => {

if (!runner.hasDocument && test.requiresDocument) {
return;
}

return new Promise(resolve => {
promise_test(async _ => {
test = await test;
await setupPromise;
await runner.preTest(test);
await test.f(runner)
await runner.postTest(test);
resolve();
}, test.description);
}).catch(_ => {});
});
Promise.all(allTests).then(_ => runner.teardown());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
structuredCloneBatteryOfTests.push({
description: 'ArrayBuffer',
async f(runner) {
const buffer = new Uint8Array([1]).buffer;
const copy = await runner.structuredClone(buffer, [buffer]);
assert_equals(buffer.byteLength, 0);
assert_equals(copy.byteLength, 1);
}
});

structuredCloneBatteryOfTests.push({
description: 'MessagePort',
async f(runner) {
const {port1, port2} = new MessageChannel();
const copy = await runner.structuredClone(port2, [port2]);
const msg = new Promise(resolve => port1.onmessage = resolve);
copy.postMessage('ohai');
assert_equals((await msg).data, 'ohai');
}
});

// TODO: ImageBitmap
Loading

0 comments on commit d0a8986

Please sign in to comment.