Skip to content

Commit 9692208

Browse files
addaleaxcodebytere
authored andcommitted
worker: allow passing JS wrapper objects via postMessage
Enable JS wrapper objects to be used as transferable or cloneable objects in `postMessage()` calls, by having them extend a C++-backed class. This requires a few internal changes: - This commit adds the possibility for transferred objects to read/write JS values at the end of the serialization/deserialization phases. - This commit adds the possibility for transferred objects to list sub-transferables, e.g. typically the public JS wrapper class would list its C++ handle in there. - This commit adds usage of `BaseObject` in a few more places, because now during deserialization weakly held objects can also be involved, in addition to `MessagePort`s. PR-URL: #33772 Backport-PR-URL: #33965 Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent a3d0b0e commit 9692208

10 files changed

+384
-33
lines changed

lib/internal/bootstrap/node.js

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ process._exiting = false;
5858

5959
// process.config is serialized config.gypi
6060
process.config = JSONParse(internalBinding('native_module').config);
61+
require('internal/worker/js_transferable').setup();
6162

6263
// Bootstrappers for all threads, including worker threads and main thread
6364
const perThreadSetup = require('internal/process/per_thread');
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
const {
3+
messaging_deserialize_symbol,
4+
messaging_transfer_symbol,
5+
messaging_clone_symbol,
6+
messaging_transfer_list_symbol
7+
} = internalBinding('symbols');
8+
const {
9+
JSTransferable,
10+
setDeserializerCreateObjectFunction
11+
} = internalBinding('messaging');
12+
13+
function setup() {
14+
// Register the handler that will be used when deserializing JS-based objects
15+
// from .postMessage() calls. The format of `deserializeInfo` is generally
16+
// 'module:Constructor', e.g. 'internal/fs/promises:FileHandle'.
17+
setDeserializerCreateObjectFunction((deserializeInfo) => {
18+
const [ module, ctor ] = deserializeInfo.split(':');
19+
const Ctor = require(module)[ctor];
20+
return new Ctor();
21+
});
22+
}
23+
24+
module.exports = {
25+
setup,
26+
JSTransferable,
27+
kClone: messaging_clone_symbol,
28+
kDeserialize: messaging_deserialize_symbol,
29+
kTransfer: messaging_transfer_symbol,
30+
kTransferList: messaging_transfer_list_symbol
31+
};

node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@
218218
'lib/internal/vm/module.js',
219219
'lib/internal/worker.js',
220220
'lib/internal/worker/io.js',
221+
'lib/internal/worker/js_transferable.js',
221222
'lib/internal/watchdog.js',
222223
'lib/internal/streams/lazy_transform.js',
223224
'lib/internal/streams/async_iterator.js',

src/base_object.h

+9
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,17 @@ class BaseObject : public MemoryRetainer {
123123
// make sure that they are not accidentally destroyed on the sending side.
124124
// TransferForMessaging() will be called to get a representation of the
125125
// object that is used for subsequent deserialization.
126+
// The NestedTransferables() method can be used to transfer other objects
127+
// along with this one, if a situation requires it.
126128
// - kCloneable:
127129
// This object can be cloned without being modified.
128130
// CloneForMessaging() will be called to get a representation of the
129131
// object that is used for subsequent deserialization, unless the
130132
// object is listed in transferList, in which case TransferForMessaging()
131133
// is attempted first.
134+
// After a successful clone, FinalizeTransferRead() is called on the receiving
135+
// end, and can read deserialize JS data possibly serialized by a previous
136+
// FinalizeTransferWrite() call.
132137
enum class TransferMode {
133138
kUntransferable,
134139
kTransferable,
@@ -137,6 +142,10 @@ class BaseObject : public MemoryRetainer {
137142
virtual TransferMode GetTransferMode() const;
138143
virtual std::unique_ptr<worker::TransferData> TransferForMessaging();
139144
virtual std::unique_ptr<worker::TransferData> CloneForMessaging() const;
145+
virtual v8::Maybe<std::vector<BaseObjectPtrImpl<BaseObject, false>>>
146+
NestedTransferables() const;
147+
virtual v8::Maybe<bool> FinalizeTransferRead(
148+
v8::Local<v8::Context> context, v8::ValueDeserializer* deserializer);
140149

141150
virtual inline void OnGCCollect();
142151

src/env.h

+6
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ constexpr size_t kFsStatsBufferLength =
159159
V(async_id_symbol, "async_id_symbol") \
160160
V(handle_onclose_symbol, "handle_onclose") \
161161
V(no_message_symbol, "no_message_symbol") \
162+
V(messaging_deserialize_symbol, "messaging_deserialize_symbol") \
163+
V(messaging_transfer_symbol, "messaging_transfer_symbol") \
164+
V(messaging_clone_symbol, "messaging_clone_symbol") \
165+
V(messaging_transfer_list_symbol, "messaging_transfer_list_symbol") \
162166
V(oninit_symbol, "oninit") \
163167
V(owner_symbol, "owner_symbol") \
164168
V(onpskexchange_symbol, "onpskexchange") \
@@ -201,6 +205,7 @@ constexpr size_t kFsStatsBufferLength =
201205
V(crypto_rsa_pss_string, "rsa-pss") \
202206
V(cwd_string, "cwd") \
203207
V(data_string, "data") \
208+
V(deserialize_info_string, "deserializeInfo") \
204209
V(dest_string, "dest") \
205210
V(destroyed_string, "destroyed") \
206211
V(detached_string, "detached") \
@@ -454,6 +459,7 @@ constexpr size_t kFsStatsBufferLength =
454459
V(internal_binding_loader, v8::Function) \
455460
V(immediate_callback_function, v8::Function) \
456461
V(inspector_console_extension_installer, v8::Function) \
462+
V(messaging_deserialize_create_object, v8::Function) \
457463
V(message_port, v8::Object) \
458464
V(native_module_require, v8::Function) \
459465
V(performance_entry_callback, v8::Function) \

src/node_errors.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ void OnFatalError(const char* location, const char* message);
9393
V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \
9494
V(ERR_OSSL_EVP_INVALID_DIGEST, "Invalid digest used") \
9595
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, \
96-
"MessagePort was found in message but not listed in transferList") \
96+
"Object that needs transfer was found in message but not listed " \
97+
"in transferList") \
9798
V(ERR_MISSING_PLATFORM_FOR_WORKER, \
9899
"The V8 platform used by this instance of Node does not support " \
99100
"creating Workers") \

0 commit comments

Comments
 (0)