Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add structuredClone global method #39759

Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -57,6 +57,8 @@ rules:
# disabled with --no-harmony-sharedarraybuffer CLI flag.
- name: SharedArrayBuffer
message: "Use `const { SharedArrayBuffer } = globalThis;` instead of the global."
- name: StructuredClone
Ethan-Arrowood marked this conversation as resolved.
Show resolved Hide resolved
message: "Use `const { structuredClone } = require('internal/structured_clone');` instead of the global."
- name: TextDecoder
message: "Use `const { TextDecoder } = require('internal/encoding');` instead of the global."
- name: TextEncoder
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
};
2 changes: 1 addition & 1 deletion test/fixtures/wpt/LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The 3-Clause BSD License

Copyright 2019 web-platform-tests contributors
Copyright © web-platform-tests contributors

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

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.
Comment on lines +10 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious: why is this called with await in all of the tests? structuredClone doesn't return a promise, does it?

* - `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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of using Buffer.allocUnsafe to introduce some randomness to the test?

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