Skip to content

Commit 0d35eaa

Browse files
committed
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 8e1698a commit 0d35eaa

10 files changed

+384
-33
lines changed

lib/internal/bootstrap/node.js

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

6060
// process.config is serialized config.gypi
6161
process.config = JSONParse(internalBinding('native_module').config);
62+
require('internal/worker/js_transferable').setup();
6263

6364
// Bootstrappers for all threads, including worker threads and main thread
6465
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
@@ -216,6 +216,7 @@
216216
'lib/internal/vm/module.js',
217217
'lib/internal/worker.js',
218218
'lib/internal/worker/io.js',
219+
'lib/internal/worker/js_transferable.js',
219220
'lib/internal/watchdog.js',
220221
'lib/internal/streams/lazy_transform.js',
221222
'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
@@ -167,6 +167,10 @@ constexpr size_t kFsStatsBufferLength =
167167
#define PER_ISOLATE_SYMBOL_PROPERTIES(V) \
168168
V(handle_onclose_symbol, "handle_onclose") \
169169
V(no_message_symbol, "no_message_symbol") \
170+
V(messaging_deserialize_symbol, "messaging_deserialize_symbol") \
171+
V(messaging_transfer_symbol, "messaging_transfer_symbol") \
172+
V(messaging_clone_symbol, "messaging_clone_symbol") \
173+
V(messaging_transfer_list_symbol, "messaging_transfer_list_symbol") \
170174
V(oninit_symbol, "oninit") \
171175
V(owner_symbol, "owner_symbol") \
172176
V(onpskexchange_symbol, "onpskexchange") \
@@ -209,6 +213,7 @@ constexpr size_t kFsStatsBufferLength =
209213
V(crypto_rsa_pss_string, "rsa-pss") \
210214
V(cwd_string, "cwd") \
211215
V(data_string, "data") \
216+
V(deserialize_info_string, "deserializeInfo") \
212217
V(dest_string, "dest") \
213218
V(destroyed_string, "destroyed") \
214219
V(detached_string, "detached") \
@@ -465,6 +470,7 @@ constexpr size_t kFsStatsBufferLength =
465470
V(internal_binding_loader, v8::Function) \
466471
V(immediate_callback_function, v8::Function) \
467472
V(inspector_console_extension_installer, v8::Function) \
473+
V(messaging_deserialize_create_object, v8::Function) \
468474
V(message_port, v8::Object) \
469475
V(native_module_require, v8::Function) \
470476
V(performance_entry_callback, v8::Function) \

src/node_errors.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ void OnFatalError(const char* location, const char* message);
9797
V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \
9898
V(ERR_OSSL_EVP_INVALID_DIGEST, "Invalid digest used") \
9999
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, \
100-
"MessagePort was found in message but not listed in transferList") \
100+
"Object that needs transfer was found in message but not listed " \
101+
"in transferList") \
101102
V(ERR_MISSING_PLATFORM_FOR_WORKER, \
102103
"The V8 platform used by this instance of Node does not support " \
103104
"creating Workers") \

0 commit comments

Comments
 (0)