diff --git a/src/node_messaging.cc b/src/node_messaging.cc index ae60187b6f3ec2..6036648dd4c527 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -30,6 +30,7 @@ using v8::String; using v8::Value; using v8::ValueDeserializer; using v8::ValueSerializer; +using v8::WasmCompiledModule; namespace node { namespace worker { @@ -43,13 +44,15 @@ namespace { // `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them. class DeserializerDelegate : public ValueDeserializer::Delegate { public: - DeserializerDelegate(Message* m, - Environment* env, - const std::vector& message_ports, - const std::vector>& - shared_array_buffers) - : message_ports_(message_ports), - shared_array_buffers_(shared_array_buffers) {} + DeserializerDelegate( + Message* m, + Environment* env, + const std::vector& message_ports, + const std::vector>& shared_array_buffers, + const std::vector& wasm_modules) + : message_ports_(message_ports), + shared_array_buffers_(shared_array_buffers), + wasm_modules_(wasm_modules) {} MaybeLocal ReadHostObject(Isolate* isolate) override { // Currently, only MessagePort hosts objects are supported, so identifying @@ -67,11 +70,19 @@ class DeserializerDelegate : public ValueDeserializer::Delegate { return shared_array_buffers_[clone_id]; } + MaybeLocal GetWasmModuleFromId( + Isolate* isolate, uint32_t transfer_id) override { + CHECK_LE(transfer_id, wasm_modules_.size()); + return WasmCompiledModule::FromTransferrableModule( + isolate, wasm_modules_[transfer_id]); + } + ValueDeserializer* deserializer = nullptr; private: const std::vector& message_ports_; const std::vector>& shared_array_buffers_; + const std::vector& wasm_modules_; }; } // anonymous namespace @@ -109,7 +120,8 @@ MaybeLocal Message::Deserialize(Environment* env, } shared_array_buffers_.clear(); - DeserializerDelegate delegate(this, env, ports, shared_array_buffers); + DeserializerDelegate delegate( + this, env, ports, shared_array_buffers, wasm_modules_); ValueDeserializer deserializer( env->isolate(), reinterpret_cast(main_message_buf_.data), @@ -143,6 +155,11 @@ void Message::AddMessagePort(std::unique_ptr&& data) { message_ports_.emplace_back(std::move(data)); } +uint32_t Message::AddWASMModule(WasmCompiledModule::TransferrableModule&& mod) { + wasm_modules_.emplace_back(std::move(mod)); + return wasm_modules_.size() - 1; +} + namespace { void ThrowDataCloneException(Environment* env, Local message) { @@ -202,6 +219,11 @@ class SerializerDelegate : public ValueSerializer::Delegate { return Just(i); } + Maybe GetWasmModuleTransferId( + Isolate* isolate, Local module) override { + return Just(msg_->AddWASMModule(module->GetTransferrableModule())); + } + void Finish() { // Only close the MessagePort handles and actually transfer them // once we know that serialization succeeded. diff --git a/src/node_messaging.h b/src/node_messaging.h index e4674885d2b89e..3c79e24f24b896 100644 --- a/src/node_messaging.h +++ b/src/node_messaging.h @@ -47,6 +47,9 @@ class Message : public MemoryRetainer { // Internal method of Message that is called once serialization finishes // and that transfers ownership of `data` to this message. void AddMessagePort(std::unique_ptr&& data); + // Internal method of Message that is called when a new WebAssembly.Module + // object is encountered in the incoming value's structure. + uint32_t AddWASMModule(v8::WasmCompiledModule::TransferrableModule&& mod); // The MessagePorts that will be transferred, as recorded by Serialize(). // Used for warning user about posting the target MessagePort to itself, @@ -65,6 +68,7 @@ class Message : public MemoryRetainer { std::vector> array_buffer_contents_; std::vector shared_array_buffers_; std::vector> message_ports_; + std::vector wasm_modules_; friend class MessagePort; }; diff --git a/test/parallel/test-worker-message-port-wasm-module.js b/test/parallel/test-worker-message-port-wasm-module.js new file mode 100644 index 00000000000000..43c12a8519981b --- /dev/null +++ b/test/parallel/test-worker-message-port-wasm-module.js @@ -0,0 +1,19 @@ +// Flags: --experimental-worker +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const { Worker } = require('worker_threads'); +const wasmModule = new WebAssembly.Module(fixtures.readSync('test.wasm')); + +const worker = new Worker(` +const { parentPort } = require('worker_threads'); +parentPort.once('message', ({ wasmModule }) => { + const instance = new WebAssembly.Instance(wasmModule); + parentPort.postMessage(instance.exports.addTwo(10, 20)); +}); +`, { eval: true }); + +worker.once('message', common.mustCall((num) => assert.strictEqual(num, 30))); +worker.postMessage({ wasmModule }); diff --git a/test/parallel/test-worker-message-port-wasm-threads.js b/test/parallel/test-worker-message-port-wasm-threads.js index 6d4f21d728d1b3..891da80e30af36 100644 --- a/test/parallel/test-worker-message-port-wasm-threads.js +++ b/test/parallel/test-worker-message-port-wasm-threads.js @@ -32,15 +32,23 @@ assert(buffer instanceof SharedArrayBuffer); // stopped when we exit. const worker = new Worker(` const { parentPort } = require('worker_threads'); + + // Compile the same WASM module from its source bytes. const wasmSource = new Uint8Array([${wasmSource.join(',')}]); const wasmModule = new WebAssembly.Module(wasmSource); const instance = new WebAssembly.Instance(wasmModule); parentPort.postMessage(instance.exports.memory); + + // Do the same thing, except we receive the WASM module via transfer. + parentPort.once('message', ({ wasmModule }) => { + const instance = new WebAssembly.Instance(wasmModule); + parentPort.postMessage(instance.exports.memory); + }); `, { eval: true }); - worker.once('message', common.mustCall(({ buffer }) => { + worker.on('message', common.mustCall(({ buffer }) => { assert(buffer instanceof SharedArrayBuffer); worker.buf = buffer; // Basically just keep the reference to buffer alive. - })); + }, 2)); worker.once('exit', common.mustCall()); worker.postMessage({ wasmModule }); }