2020-06-06 16:11:31 +02:00
|
|
|
'use strict';
|
2020-11-16 17:22:35 +01:00
|
|
|
const {
|
|
|
|
|
Error,
|
|
|
|
|
StringPrototypeSplit,
|
|
|
|
|
} = primordials;
|
2024-10-14 04:41:21 +10:30
|
|
|
const {
|
|
|
|
|
codes: {
|
|
|
|
|
ERR_MISSING_ARGS,
|
|
|
|
|
},
|
|
|
|
|
} = require('internal/errors');
|
|
|
|
|
const webidl = require('internal/webidl');
|
2020-06-06 16:11:31 +02:00
|
|
|
const {
|
|
|
|
|
messaging_deserialize_symbol,
|
|
|
|
|
messaging_transfer_symbol,
|
|
|
|
|
messaging_clone_symbol,
|
2023-02-28 12:14:11 +01:00
|
|
|
messaging_transfer_list_symbol,
|
2020-06-06 16:11:31 +02:00
|
|
|
} = internalBinding('symbols');
|
|
|
|
|
const {
|
2023-02-28 12:14:11 +01:00
|
|
|
setDeserializerCreateObjectFunction,
|
2024-10-14 04:41:21 +10:30
|
|
|
structuredClone: nativeStructuredClone,
|
2020-06-06 16:11:31 +02:00
|
|
|
} = internalBinding('messaging');
|
2023-07-08 01:00:00 +08:00
|
|
|
const {
|
|
|
|
|
privateSymbols: {
|
|
|
|
|
transfer_mode_private_symbol,
|
|
|
|
|
},
|
|
|
|
|
constants: {
|
|
|
|
|
kDisallowCloneAndTransfer,
|
|
|
|
|
kTransferable,
|
|
|
|
|
kCloneable,
|
|
|
|
|
},
|
|
|
|
|
} = internalBinding('util');
|
2020-06-06 16:11:31 +02:00
|
|
|
|
|
|
|
|
function setup() {
|
|
|
|
|
// Register the handler that will be used when deserializing JS-based objects
|
|
|
|
|
// from .postMessage() calls. The format of `deserializeInfo` is generally
|
|
|
|
|
// 'module:Constructor', e.g. 'internal/fs/promises:FileHandle'.
|
|
|
|
|
setDeserializerCreateObjectFunction((deserializeInfo) => {
|
2025-03-18 16:52:50 +01:00
|
|
|
const { 0: module, 1: ctor } = StringPrototypeSplit(deserializeInfo, ':', 2);
|
2020-06-06 16:11:31 +02:00
|
|
|
const Ctor = require(module)[ctor];
|
2020-06-06 16:27:30 +02:00
|
|
|
if (typeof Ctor !== 'function' ||
|
lib: add support for JSTransferable as a mixin
Adds a new `makeTransferable()` utility that can construct a
`JSTransferable` object that does not directly extend the
`JSTransferable` JavaScript class.
Because JavaScript does not support multiple inheritance, it is
not possible (without help) to implement a class that extends
both `JSTransferable` and, for instance, `EventTarget` without
incurring a significant additional complexity and performance
cost by making all `EventTarget` instances extend `JSTransferable`...
That is, we *don't* want:
```js
class EventTarget extends JSTransferable { ... }
```
The `makeTransferable()` allows us to create objects that are
backed internally by `JSTransferable` without having to actually
extend it by leveraging the magic of `Reflect.construct()`.
```js
const {
JSTransferable,
kClone,
kDeserialize,
kConstructor,
makeTransferable,
} = require('internal/worker/js_transferable');
class E {
constructor(b) {
this.b = b;
}
}
class F extends E {
[kClone]() { /** ... **/ }
[kDeserialize]() { /** ... **/ }
static [kConstructor]() { return makeTransferable(F); }
}
const f = makeTransferable(F, 1);
f instanceof F; // true
f instanceof E; // true
f instanceof JSTransferable; // false
const mc = new MessageChannel();
mc.port1.onmessage = ({ data }) => {
data instanceof F; // true
data instanceof E; // true
data instanceof JSTransferable; // false
};
mc.port2.postMessage(f); // works!
```
The additional `internal/test/transfer.js` file is required for the
test because successfully deserializing transferable classes requires
that they be located in `lib/internal` for now.
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/38383
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Khaidi Chu <i@2333.moe>
2021-04-24 07:46:21 -07:00
|
|
|
typeof Ctor.prototype[messaging_deserialize_symbol] !== 'function') {
|
2020-06-06 16:27:30 +02:00
|
|
|
// Not one of the official errors because one should not be able to get
|
|
|
|
|
// here without messing with Node.js internals.
|
|
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
|
|
|
throw new Error(`Unknown deserialize spec ${deserializeInfo}`);
|
|
|
|
|
}
|
lib: add support for JSTransferable as a mixin
Adds a new `makeTransferable()` utility that can construct a
`JSTransferable` object that does not directly extend the
`JSTransferable` JavaScript class.
Because JavaScript does not support multiple inheritance, it is
not possible (without help) to implement a class that extends
both `JSTransferable` and, for instance, `EventTarget` without
incurring a significant additional complexity and performance
cost by making all `EventTarget` instances extend `JSTransferable`...
That is, we *don't* want:
```js
class EventTarget extends JSTransferable { ... }
```
The `makeTransferable()` allows us to create objects that are
backed internally by `JSTransferable` without having to actually
extend it by leveraging the magic of `Reflect.construct()`.
```js
const {
JSTransferable,
kClone,
kDeserialize,
kConstructor,
makeTransferable,
} = require('internal/worker/js_transferable');
class E {
constructor(b) {
this.b = b;
}
}
class F extends E {
[kClone]() { /** ... **/ }
[kDeserialize]() { /** ... **/ }
static [kConstructor]() { return makeTransferable(F); }
}
const f = makeTransferable(F, 1);
f instanceof F; // true
f instanceof E; // true
f instanceof JSTransferable; // false
const mc = new MessageChannel();
mc.port1.onmessage = ({ data }) => {
data instanceof F; // true
data instanceof E; // true
data instanceof JSTransferable; // false
};
mc.port2.postMessage(f); // works!
```
The additional `internal/test/transfer.js` file is required for the
test because successfully deserializing transferable classes requires
that they be located in `lib/internal` for now.
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/38383
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Khaidi Chu <i@2333.moe>
2021-04-24 07:46:21 -07:00
|
|
|
|
2020-06-06 16:11:31 +02:00
|
|
|
return new Ctor();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-08 01:00:00 +08:00
|
|
|
/**
|
|
|
|
|
* Mark an object as being transferable or customized cloneable in
|
|
|
|
|
* `.postMessage()`.
|
|
|
|
|
* This should only applied to host objects like Web API interfaces, Node.js'
|
|
|
|
|
* built-in objects.
|
|
|
|
|
* Objects marked as cloneable and transferable should implement the method
|
|
|
|
|
* `@@kClone` and `@@kTransfer` respectively. Method `@@kDeserialize` is
|
|
|
|
|
* required to deserialize the data to a new instance.
|
|
|
|
|
*
|
|
|
|
|
* Example implementation of a cloneable interface (assuming its located in
|
|
|
|
|
* `internal/my_interface.js`):
|
|
|
|
|
*
|
|
|
|
|
* ```
|
|
|
|
|
* class MyInterface {
|
|
|
|
|
* constructor(...args) {
|
|
|
|
|
* markTransferMode(this, true);
|
|
|
|
|
* this.args = args;
|
|
|
|
|
* }
|
|
|
|
|
* [kDeserialize](data) {
|
|
|
|
|
* this.args = data.args;
|
|
|
|
|
* }
|
|
|
|
|
* [kClone]() {
|
|
|
|
|
* return {
|
|
|
|
|
* data: { args: this.args },
|
|
|
|
|
* deserializeInfo: 'internal/my_interface:MyInterface',
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* module.exports = {
|
|
|
|
|
* MyInterface,
|
|
|
|
|
* };
|
|
|
|
|
* ```
|
|
|
|
|
* @param {object} obj Host objects that can be either cloned or transferred.
|
|
|
|
|
* @param {boolean} [cloneable] if the object can be cloned and `@@kClone` is
|
2025-07-18 05:28:21 -04:00
|
|
|
* implemented.
|
2023-07-08 01:00:00 +08:00
|
|
|
* @param {boolean} [transferable] if the object can be transferred and
|
2025-07-18 05:28:21 -04:00
|
|
|
* `@@kTransfer` is implemented.
|
2023-07-08 01:00:00 +08:00
|
|
|
*/
|
|
|
|
|
function markTransferMode(obj, cloneable = false, transferable = false) {
|
|
|
|
|
if ((typeof obj !== 'object' && typeof obj !== 'function') || obj === null)
|
|
|
|
|
return; // This object is a primitive and therefore already untransferable.
|
|
|
|
|
let mode = kDisallowCloneAndTransfer;
|
|
|
|
|
if (cloneable) mode |= kCloneable;
|
|
|
|
|
if (transferable) mode |= kTransferable;
|
|
|
|
|
obj[transfer_mode_private_symbol] = mode;
|
lib: add support for JSTransferable as a mixin
Adds a new `makeTransferable()` utility that can construct a
`JSTransferable` object that does not directly extend the
`JSTransferable` JavaScript class.
Because JavaScript does not support multiple inheritance, it is
not possible (without help) to implement a class that extends
both `JSTransferable` and, for instance, `EventTarget` without
incurring a significant additional complexity and performance
cost by making all `EventTarget` instances extend `JSTransferable`...
That is, we *don't* want:
```js
class EventTarget extends JSTransferable { ... }
```
The `makeTransferable()` allows us to create objects that are
backed internally by `JSTransferable` without having to actually
extend it by leveraging the magic of `Reflect.construct()`.
```js
const {
JSTransferable,
kClone,
kDeserialize,
kConstructor,
makeTransferable,
} = require('internal/worker/js_transferable');
class E {
constructor(b) {
this.b = b;
}
}
class F extends E {
[kClone]() { /** ... **/ }
[kDeserialize]() { /** ... **/ }
static [kConstructor]() { return makeTransferable(F); }
}
const f = makeTransferable(F, 1);
f instanceof F; // true
f instanceof E; // true
f instanceof JSTransferable; // false
const mc = new MessageChannel();
mc.port1.onmessage = ({ data }) => {
data instanceof F; // true
data instanceof E; // true
data instanceof JSTransferable; // false
};
mc.port2.postMessage(f); // works!
```
The additional `internal/test/transfer.js` file is required for the
test because successfully deserializing transferable classes requires
that they be located in `lib/internal` for now.
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/38383
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Khaidi Chu <i@2333.moe>
2021-04-24 07:46:21 -07:00
|
|
|
}
|
|
|
|
|
|
2024-10-20 22:37:00 +10:30
|
|
|
|
|
|
|
|
webidl.converters.StructuredSerializeOptions = webidl
|
|
|
|
|
.createDictionaryConverter(
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
key: 'transfer',
|
|
|
|
|
converter: webidl.converters['sequence<object>'],
|
|
|
|
|
defaultValue: () => [],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
2024-10-14 04:41:21 +10:30
|
|
|
function structuredClone(value, options) {
|
|
|
|
|
if (arguments.length === 0) {
|
|
|
|
|
throw new ERR_MISSING_ARGS('The value argument must be specified');
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-20 22:37:00 +10:30
|
|
|
const idlOptions = webidl.converters.StructuredSerializeOptions(
|
|
|
|
|
options,
|
|
|
|
|
{
|
2024-10-14 04:41:21 +10:30
|
|
|
__proto__: null,
|
2024-10-20 22:37:00 +10:30
|
|
|
prefix: "Failed to execute 'structuredClone'",
|
|
|
|
|
context: 'Options',
|
|
|
|
|
},
|
|
|
|
|
);
|
2024-10-14 04:41:21 +10:30
|
|
|
|
|
|
|
|
const serializedData = nativeStructuredClone(value, idlOptions);
|
|
|
|
|
return serializedData;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-06 16:11:31 +02:00
|
|
|
module.exports = {
|
2023-07-08 01:00:00 +08:00
|
|
|
markTransferMode,
|
2020-06-06 16:11:31 +02:00
|
|
|
setup,
|
2024-10-14 04:41:21 +10:30
|
|
|
structuredClone,
|
2020-06-06 16:11:31 +02:00
|
|
|
kClone: messaging_clone_symbol,
|
|
|
|
|
kDeserialize: messaging_deserialize_symbol,
|
|
|
|
|
kTransfer: messaging_transfer_symbol,
|
lib: add support for JSTransferable as a mixin
Adds a new `makeTransferable()` utility that can construct a
`JSTransferable` object that does not directly extend the
`JSTransferable` JavaScript class.
Because JavaScript does not support multiple inheritance, it is
not possible (without help) to implement a class that extends
both `JSTransferable` and, for instance, `EventTarget` without
incurring a significant additional complexity and performance
cost by making all `EventTarget` instances extend `JSTransferable`...
That is, we *don't* want:
```js
class EventTarget extends JSTransferable { ... }
```
The `makeTransferable()` allows us to create objects that are
backed internally by `JSTransferable` without having to actually
extend it by leveraging the magic of `Reflect.construct()`.
```js
const {
JSTransferable,
kClone,
kDeserialize,
kConstructor,
makeTransferable,
} = require('internal/worker/js_transferable');
class E {
constructor(b) {
this.b = b;
}
}
class F extends E {
[kClone]() { /** ... **/ }
[kDeserialize]() { /** ... **/ }
static [kConstructor]() { return makeTransferable(F); }
}
const f = makeTransferable(F, 1);
f instanceof F; // true
f instanceof E; // true
f instanceof JSTransferable; // false
const mc = new MessageChannel();
mc.port1.onmessage = ({ data }) => {
data instanceof F; // true
data instanceof E; // true
data instanceof JSTransferable; // false
};
mc.port2.postMessage(f); // works!
```
The additional `internal/test/transfer.js` file is required for the
test because successfully deserializing transferable classes requires
that they be located in `lib/internal` for now.
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/38383
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Khaidi Chu <i@2333.moe>
2021-04-24 07:46:21 -07:00
|
|
|
kTransferList: messaging_transfer_list_symbol,
|
2020-06-06 16:11:31 +02:00
|
|
|
};
|