| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 337be58 commit f447acd
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -629,6 +629,12 @@ An operation outside the bounds of a `Buffer` was attempted. | |||
| 629 | 629 | An attempt has been made to create a `Buffer` larger than the maximum allowed | |
| 630 | 630 | size. | |
| 631 | 631 | ||
| 632 | + <a id="ERR_CANNOT_TRANSFER_OBJECT"></a> | ||
| 633 | + ### ERR_CANNOT_TRANSFER_OBJECT | ||
| 634 | + | ||
| 635 | + The value passed to `postMessage()` contained an object that is not supported | ||
| 636 | + for transferring. | ||
| 637 | + | ||
| 632 | 638 | <a id="ERR_CANNOT_WATCH_SIGINT"></a> | |
| 633 | 639 | ### ERR_CANNOT_WATCH_SIGINT | |
| 634 | 640 | ||
@@ -1304,6 +1310,12 @@ strict compliance with the API specification (which in some cases may accept | |||
| 1304 | 1310 | `func(undefined)` and `func()` are treated identically, and the | |
| 1305 | 1311 | [`ERR_INVALID_ARG_TYPE`][] error code may be used instead. | |
| 1306 | 1312 | ||
| 1313 | + <a id="ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST"></a> | ||
| 1314 | + ### ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST | ||
| 1315 | + | ||
| 1316 | + A `MessagePort` was found in the object passed to a `postMessage()` call, | ||
| 1317 | + but not provided in the `transferList` for that call. | ||
| 1318 | + | ||
| 1307 | 1319 | <a id="ERR_MISSING_MODULE"></a> | |
| 1308 | 1320 | ### ERR_MISSING_MODULE | |
| 1309 | 1321 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -83,7 +83,7 @@ the [HTML structured clone algorithm][]. In particular, it may contain circular | |||
| 83 | 83 | references and objects like typed arrays that the `JSON` API is not able | |
| 84 | 84 | to stringify. | |
| 85 | 85 | ||
| 86 | - `transferList` may be a list of `ArrayBuffer` objects. | ||
| 86 | + `transferList` may be a list of `ArrayBuffer` and `MessagePort` objects. | ||
| 87 | 87 | After transferring, they will not be usable on the sending side of the channel | |
| 88 | 88 | anymore (even if they are not contained in `value`). | |
| 89 | 89 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,6 +19,7 @@ namespace node { | |||
| 19 | 19 | #define ERRORS_WITH_CODE(V) \ | |
| 20 | 20 | V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \ | |
| 21 | 21 | V(ERR_BUFFER_TOO_LARGE, Error) \ | |
| 22 | + V(ERR_CANNOT_TRANSFER_OBJECT, TypeError) \ | ||
| 22 | 23 | V(ERR_CLOSED_MESSAGE_PORT, Error) \ | |
| 23 | 24 | V(ERR_CONSTRUCT_CALL_REQUIRED, Error) \ | |
| 24 | 25 | V(ERR_INDEX_OUT_OF_RANGE, RangeError) \ | |
@@ -27,6 +28,7 @@ namespace node { | |||
| 27 | 28 | V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \ | |
| 28 | 29 | V(ERR_MEMORY_ALLOCATION_FAILED, Error) \ | |
| 29 | 30 | V(ERR_MISSING_ARGS, TypeError) \ | |
| 31 | + V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, TypeError) \ | ||
| 30 | 32 | V(ERR_MISSING_MODULE, Error) \ | |
| 31 | 33 | V(ERR_STRING_TOO_LONG, Error) \ | |
| 32 | 34 | ||
@@ -51,11 +53,14 @@ namespace node { | |||
| 51 | 53 | // Errors with predefined static messages | |
| 52 | 54 | ||
| 53 | 55 | #define PREDEFINED_ERROR_MESSAGES(V) \ | |
| 56 | + V(ERR_CANNOT_TRANSFER_OBJECT, "Cannot transfer object of unsupported type")\ | ||
| 54 | 57 | V(ERR_CLOSED_MESSAGE_PORT, "Cannot send data on closed MessagePort") \ | |
| 55 | 58 | V(ERR_CONSTRUCT_CALL_REQUIRED, "Cannot call constructor without `new`") \ | |
| 56 | 59 | V(ERR_INDEX_OUT_OF_RANGE, "Index out of range") \ | |
| 57 | 60 | V(ERR_INVALID_TRANSFER_OBJECT, "Found invalid object in transferList") \ | |
| 58 | - V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") | ||
| 61 | + V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \ | ||
| 62 | + V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, \ | ||
| 63 | + "MessagePort was found in message but not listed in transferList") | ||
| 59 | 64 | ||
| 60 | 65 | #define V(code, message) \ | |
| 61 | 66 | inline v8::Local<v8::Value> code(v8::Isolate* isolate) { \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -41,14 +41,27 @@ namespace { | |||
| 41 | 41 | // `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them. | |
| 42 | 42 | class DeserializerDelegate : public ValueDeserializer::Delegate { | |
| 43 | 43 | public: | |
| 44 | - DeserializerDelegate(Message* m, Environment* env) | ||
| 45 | - : env_(env), msg_(m) {} | ||
| 44 | + DeserializerDelegate(Message* m, | ||
| 45 | + Environment* env, | ||
| 46 | + const std::vector<MessagePort*>& message_ports) | ||
| 47 | + : env_(env), msg_(m), message_ports_(message_ports) {} | ||
| 48 | + | ||
| 49 | + MaybeLocal<Object> ReadHostObject(Isolate* isolate) override { | ||
| 50 | + // Currently, only MessagePort hosts objects are supported, so identifying | ||
| 51 | + // by the index in the message's MessagePort array is sufficient. | ||
| 52 | + uint32_t id; | ||
| 53 | + if (!deserializer->ReadUint32(&id)) | ||
| 54 | + return MaybeLocal<Object>(); | ||
| 55 | + CHECK_LE(id, message_ports_.size()); | ||
| 56 | + return message_ports_[id]->object(); | ||
| 57 | + }; | ||
| 46 | 58 | ||
| 47 | 59 | ValueDeserializer* deserializer = nullptr; | |
| 48 | 60 | ||
| 49 | 61 | private: | |
| 50 | 62 | Environment* env_; | |
| 51 | 63 | Message* msg_; | |
| 64 | + const std::vector<MessagePort*>& message_ports_; | ||
| 52 | 65 | }; | |
| 53 | 66 | ||
| 54 | 67 | } // anonymous namespace | |
@@ -58,7 +71,23 @@ MaybeLocal<Value> Message::Deserialize(Environment* env, | |||
| 58 | 71 | EscapableHandleScope handle_scope(env->isolate()); | |
| 59 | 72 | Context::Scope context_scope(context); | |
| 60 | 73 | ||
| 61 | - DeserializerDelegate delegate(this, env); | ||
| 74 | + // Create all necessary MessagePort handles. | ||
| 75 | + std::vector<MessagePort*> ports(message_ports_.size()); | ||
| 76 | + for (uint32_t i = 0; i < message_ports_.size(); ++i) { | ||
| 77 | + ports[i] = MessagePort::New(env, | ||
| 78 | + context, | ||
| 79 | + std::move(message_ports_[i])); | ||
| 80 | + if (ports[i] == nullptr) { | ||
| 81 | + for (MessagePort* port : ports) { | ||
| 82 | + // This will eventually release the MessagePort object itself. | ||
| 83 | + port->Close(); | ||
| 84 | + } | ||
| 85 | + return MaybeLocal<Value>(); | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + message_ports_.clear(); | ||
| 89 | + | ||
| 90 | + DeserializerDelegate delegate(this, env, ports); | ||
| 62 | 91 | ValueDeserializer deserializer( | |
| 63 | 92 | env->isolate(), | |
| 64 | 93 | reinterpret_cast<const uint8_t*>(main_message_buf_.data), | |
@@ -83,6 +112,10 @@ MaybeLocal<Value> Message::Deserialize(Environment* env, | |||
| 83 | 112 | deserializer.ReadValue(context).FromMaybe(Local<Value>())); | |
| 84 | 113 | } | |
| 85 | 114 | ||
| 115 | + void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) { | ||
| 116 | + message_ports_.emplace_back(std::move(data)); | ||
| 117 | + } | ||
| 118 | + | ||
| 86 | 119 | namespace { | |
| 87 | 120 | ||
| 88 | 121 | // This tells V8 how to serialize objects that it does not understand | |
@@ -97,12 +130,43 @@ class SerializerDelegate : public ValueSerializer::Delegate { | |||
| 97 | 130 | env_->isolate()->ThrowException(Exception::Error(message)); | |
| 98 | 131 | } | |
| 99 | 132 | ||
| 133 | + Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override { | ||
| 134 | + if (env_->message_port_constructor_template()->HasInstance(object)) { | ||
| 135 | + return WriteMessagePort(Unwrap<MessagePort>(object)); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + THROW_ERR_CANNOT_TRANSFER_OBJECT(env_); | ||
| 139 | + return Nothing<bool>(); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + void Finish() { | ||
| 143 | + // Only close the MessagePort handles and actually transfer them | ||
| 144 | + // once we know that serialization succeeded. | ||
| 145 | + for (MessagePort* port : ports_) { | ||
| 146 | + port->Close(); | ||
| 147 | + msg_->AddMessagePort(port->Detach()); | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + | ||
| 100 | 151 | ValueSerializer* serializer = nullptr; | |
| 101 | 152 | ||
| 102 | 153 | private: | |
| 154 | + Maybe<bool> WriteMessagePort(MessagePort* port) { | ||
| 155 | + for (uint32_t i = 0; i < ports_.size(); i++) { | ||
| 156 | + if (ports_[i] == port) { | ||
| 157 | + serializer->WriteUint32(i); | ||
| 158 | + return Just(true); | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + THROW_ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST(env_); | ||
| 163 | + return Nothing<bool>(); | ||
| 164 | + } | ||
| 165 | + | ||
| 103 | 166 | Environment* env_; | |
| 104 | 167 | Local<Context> context_; | |
| 105 | 168 | Message* msg_; | |
| 169 | + std::vector<MessagePort*> ports_; | ||
| 106 | 170 | ||
| 107 | 171 | friend class worker::Message; | |
| 108 | 172 | }; | |
@@ -131,7 +195,7 @@ Maybe<bool> Message::Serialize(Environment* env, | |||
| 131 | 195 | Local<Value> entry; | |
| 132 | 196 | if (!transfer_list->Get(context, i).ToLocal(&entry)) | |
| 133 | 197 | return Nothing<bool>(); | |
| 134 | - // Currently, we support ArrayBuffers. | ||
| 198 | + // Currently, we support ArrayBuffers and MessagePorts. | ||
| 135 | 199 | if (entry->IsArrayBuffer()) { | |
| 136 | 200 | Local<ArrayBuffer> ab = entry.As<ArrayBuffer>(); | |
| 137 | 201 | // If we cannot render the ArrayBuffer unusable in this Isolate and | |
@@ -144,6 +208,12 @@ Maybe<bool> Message::Serialize(Environment* env, | |||
| 144 | 208 | array_buffers.push_back(ab); | |
| 145 | 209 | serializer.TransferArrayBuffer(id, ab); | |
| 146 | 210 | continue; | |
| 211 | + } else if (env->message_port_constructor_template() | ||
| 212 | + ->HasInstance(entry)) { | ||
| 213 | + MessagePort* port = Unwrap<MessagePort>(entry.As<Object>()); | ||
| 214 | + CHECK_NE(port, nullptr); | ||
| 215 | + delegate.ports_.push_back(port); | ||
| 216 | + continue; | ||
| 147 | 217 | } | |
| 148 | 218 | ||
| 149 | 219 | THROW_ERR_INVALID_TRANSFER_OBJECT(env); | |
@@ -167,6 +237,8 @@ Maybe<bool> Message::Serialize(Environment* env, | |||
| 167 | 237 | contents.ByteLength() }); | |
| 168 | 238 | } | |
| 169 | 239 | ||
| 240 | + delegate.Finish(); | ||
| 241 | + | ||
| 170 | 242 | // The serializer gave us a buffer allocated using `malloc()`. | |
| 171 | 243 | std::pair<uint8_t*, size_t> data = serializer.Release(); | |
| 172 | 244 | main_message_buf_ = | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -37,9 +37,14 @@ class Message { | |||
| 37 | 37 | v8::Local<v8::Value> input, | |
| 38 | 38 | v8::Local<v8::Value> transfer_list); | |
| 39 | 39 | ||
| 40 | + // Internal method of Message that is called once serialization finishes | ||
| 41 | + // and that transfers ownership of `data` to this message. | ||
| 42 | + void AddMessagePort(std::unique_ptr<MessagePortData>&& data); | ||
| 43 | + | ||
| 40 | 44 | private: | |
| 41 | 45 | MallocedBuffer<char> main_message_buf_; | |
| 42 | 46 | std::vector<MallocedBuffer<char>> array_buffer_contents_; | |
| 47 | + std::vector<std::unique_ptr<MessagePortData>> message_ports_; | ||
| 43 | 48 | ||
| 44 | 49 | friend class MessagePort; | |
| 45 | 50 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,23 @@ | |||
| 1 | + // Flags: --experimental-worker | ||
| 2 | + 'use strict'; | ||
| 3 | + const common = require('../common'); | ||
| 4 | + const assert = require('assert'); | ||
| 5 | + | ||
| 6 | + const { MessageChannel } = require('worker'); | ||
| 7 | + | ||
| 8 | + { | ||
| 9 | + const { port1: basePort1, port2: basePort2 } = new MessageChannel(); | ||
| 10 | + const { | ||
| 11 | + port1: transferredPort1, port2: transferredPort2 | ||
| 12 | + } = new MessageChannel(); | ||
| 13 | + | ||
| 14 | + basePort1.postMessage({ transferredPort1 }, [ transferredPort1 ]); | ||
| 15 | + basePort2.on('message', common.mustCall(({ transferredPort1 }) => { | ||
| 16 | + transferredPort1.postMessage('foobar'); | ||
| 17 | + transferredPort2.on('message', common.mustCall((msg) => { | ||
| 18 | + assert.strictEqual(msg, 'foobar'); | ||
| 19 | + transferredPort1.close(common.mustCall()); | ||
| 20 | + basePort1.close(common.mustCall()); | ||
| 21 | + })); | ||
| 22 | + })); | ||
| 23 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments