mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
worker: add markAsUncloneable
api
External modules need a way to decorate their objects so that node can recognize it as a host object for serialization process. Exposing a way for turning off instead of turning on is much safer. PR-URL: https://github.com/nodejs/node/pull/55234 Refs: https://github.com/nodejs/node/pull/55178 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com> Reviewed-By: Matthew Aitken <maitken033380023@gmail.com>
This commit is contained in:
parent
b2161d3a13
commit
d2ad9b4fb6
@ -194,6 +194,38 @@ isMarkedAsUntransferable(pooledBuffer); // Returns true.
|
|||||||
|
|
||||||
There is no equivalent to this API in browsers.
|
There is no equivalent to this API in browsers.
|
||||||
|
|
||||||
|
## `worker.markAsUncloneable(object)`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `object` {any} Any arbitrary JavaScript value.
|
||||||
|
|
||||||
|
Mark an object as not cloneable. If `object` is used as [`message`](#event-message) in
|
||||||
|
a [`port.postMessage()`][] call, an error is thrown. This is a no-op if `object` is a
|
||||||
|
primitive value.
|
||||||
|
|
||||||
|
This has no effect on `ArrayBuffer`, or any `Buffer` like objects.
|
||||||
|
|
||||||
|
This operation cannot be undone.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { markAsUncloneable } = require('node:worker_threads');
|
||||||
|
|
||||||
|
const anyObject = { foo: 'bar' };
|
||||||
|
markAsUncloneable(anyObject);
|
||||||
|
const { port1 } = new MessageChannel();
|
||||||
|
try {
|
||||||
|
// This will throw an error, because anyObject is not cloneable.
|
||||||
|
port1.postMessage(anyObject)
|
||||||
|
} catch (error) {
|
||||||
|
// error.name === 'DataCloneError'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There is no equivalent to this API in browsers.
|
||||||
|
|
||||||
## `worker.moveMessagePortToContext(port, contextifiedSandbox)`
|
## `worker.moveMessagePortToContext(port, contextifiedSandbox)`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
@ -29,6 +29,14 @@ const {
|
|||||||
oninit: onInitSymbol,
|
oninit: onInitSymbol,
|
||||||
no_message_symbol: noMessageSymbol,
|
no_message_symbol: noMessageSymbol,
|
||||||
} = internalBinding('symbols');
|
} = internalBinding('symbols');
|
||||||
|
const {
|
||||||
|
privateSymbols: {
|
||||||
|
transfer_mode_private_symbol,
|
||||||
|
},
|
||||||
|
constants: {
|
||||||
|
kCloneable,
|
||||||
|
},
|
||||||
|
} = internalBinding('util');
|
||||||
const {
|
const {
|
||||||
MessagePort,
|
MessagePort,
|
||||||
MessageChannel,
|
MessageChannel,
|
||||||
@ -447,6 +455,13 @@ ObjectDefineProperties(BroadcastChannel.prototype, {
|
|||||||
defineEventHandler(BroadcastChannel.prototype, 'message');
|
defineEventHandler(BroadcastChannel.prototype, 'message');
|
||||||
defineEventHandler(BroadcastChannel.prototype, 'messageerror');
|
defineEventHandler(BroadcastChannel.prototype, 'messageerror');
|
||||||
|
|
||||||
|
function markAsUncloneable(obj) {
|
||||||
|
if ((typeof obj !== 'object' && typeof obj !== 'function') || obj === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
obj[transfer_mode_private_symbol] &= ~kCloneable;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
drainMessagePort,
|
drainMessagePort,
|
||||||
messageTypes,
|
messageTypes,
|
||||||
@ -454,6 +469,7 @@ module.exports = {
|
|||||||
kIncrementsPortRef,
|
kIncrementsPortRef,
|
||||||
kWaitingStreams,
|
kWaitingStreams,
|
||||||
kStdioWantsMoreDataCallback,
|
kStdioWantsMoreDataCallback,
|
||||||
|
markAsUncloneable,
|
||||||
moveMessagePortToContext,
|
moveMessagePortToContext,
|
||||||
MessagePort,
|
MessagePort,
|
||||||
MessageChannel,
|
MessageChannel,
|
||||||
|
@ -13,6 +13,7 @@ const {
|
|||||||
const {
|
const {
|
||||||
MessagePort,
|
MessagePort,
|
||||||
MessageChannel,
|
MessageChannel,
|
||||||
|
markAsUncloneable,
|
||||||
moveMessagePortToContext,
|
moveMessagePortToContext,
|
||||||
receiveMessageOnPort,
|
receiveMessageOnPort,
|
||||||
BroadcastChannel,
|
BroadcastChannel,
|
||||||
@ -31,6 +32,7 @@ module.exports = {
|
|||||||
isMainThread,
|
isMainThread,
|
||||||
MessagePort,
|
MessagePort,
|
||||||
MessageChannel,
|
MessageChannel,
|
||||||
|
markAsUncloneable,
|
||||||
markAsUntransferable,
|
markAsUntransferable,
|
||||||
isMarkedAsUntransferable,
|
isMarkedAsUntransferable,
|
||||||
moveMessagePortToContext,
|
moveMessagePortToContext,
|
||||||
|
70
test/parallel/test-worker-message-mark-as-uncloneable.js
Normal file
70
test/parallel/test-worker-message-mark-as-uncloneable.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const { markAsUncloneable } = require('node:worker_threads');
|
||||||
|
const { mustCall } = require('../common');
|
||||||
|
|
||||||
|
const expectedErrorName = 'DataCloneError';
|
||||||
|
|
||||||
|
// Uncloneables cannot be cloned during message posting
|
||||||
|
{
|
||||||
|
const anyObject = { foo: 'bar' };
|
||||||
|
markAsUncloneable(anyObject);
|
||||||
|
const { port1 } = new MessageChannel();
|
||||||
|
assert.throws(() => port1.postMessage(anyObject), {
|
||||||
|
constructor: DOMException,
|
||||||
|
name: expectedErrorName,
|
||||||
|
code: 25,
|
||||||
|
}, `Should throw ${expectedErrorName} when posting uncloneables`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncloneables cannot be cloned during structured cloning
|
||||||
|
{
|
||||||
|
class MockResponse extends Response {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
markAsUncloneable(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
structuredClone(MockResponse.prototype);
|
||||||
|
|
||||||
|
markAsUncloneable(MockResponse.prototype);
|
||||||
|
const r = new MockResponse();
|
||||||
|
assert.throws(() => structuredClone(r), {
|
||||||
|
constructor: DOMException,
|
||||||
|
name: expectedErrorName,
|
||||||
|
code: 25,
|
||||||
|
}, `Should throw ${expectedErrorName} when cloning uncloneables`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// markAsUncloneable cannot affect ArrayBuffer
|
||||||
|
{
|
||||||
|
const pooledBuffer = new ArrayBuffer(8);
|
||||||
|
const { port1, port2 } = new MessageChannel();
|
||||||
|
markAsUncloneable(pooledBuffer);
|
||||||
|
port1.postMessage(pooledBuffer);
|
||||||
|
port2.on('message', mustCall((value) => {
|
||||||
|
assert.deepStrictEqual(value, pooledBuffer);
|
||||||
|
port2.close(mustCall());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// markAsUncloneable can affect Node.js built-in object like Blob
|
||||||
|
{
|
||||||
|
const cloneableBlob = new Blob();
|
||||||
|
const { port1, port2 } = new MessageChannel();
|
||||||
|
port1.postMessage(cloneableBlob);
|
||||||
|
port2.on('message', mustCall((value) => {
|
||||||
|
assert.deepStrictEqual(value, cloneableBlob);
|
||||||
|
port2.close(mustCall());
|
||||||
|
}));
|
||||||
|
|
||||||
|
const uncloneableBlob = new Blob();
|
||||||
|
markAsUncloneable(uncloneableBlob);
|
||||||
|
assert.throws(() => port1.postMessage(uncloneableBlob), {
|
||||||
|
constructor: DOMException,
|
||||||
|
name: expectedErrorName,
|
||||||
|
code: 25,
|
||||||
|
}, `Should throw ${expectedErrorName} when cloning uncloneables`);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user