1 |
|
|
#include "node_messaging.h" |
2 |
|
|
|
3 |
|
|
#include "async_wrap-inl.h" |
4 |
|
|
#include "debug_utils-inl.h" |
5 |
|
|
#include "memory_tracker-inl.h" |
6 |
|
|
#include "node_buffer.h" |
7 |
|
|
#include "node_contextify.h" |
8 |
|
|
#include "node_errors.h" |
9 |
|
|
#include "node_external_reference.h" |
10 |
|
|
#include "node_process.h" |
11 |
|
|
#include "util-inl.h" |
12 |
|
|
|
13 |
|
|
using node::contextify::ContextifyContext; |
14 |
|
|
using node::errors::TryCatchScope; |
15 |
|
|
using v8::Array; |
16 |
|
|
using v8::ArrayBuffer; |
17 |
|
|
using v8::BackingStore; |
18 |
|
|
using v8::CompiledWasmModule; |
19 |
|
|
using v8::Context; |
20 |
|
|
using v8::EscapableHandleScope; |
21 |
|
|
using v8::Function; |
22 |
|
|
using v8::FunctionCallbackInfo; |
23 |
|
|
using v8::FunctionTemplate; |
24 |
|
|
using v8::Global; |
25 |
|
|
using v8::HandleScope; |
26 |
|
|
using v8::Isolate; |
27 |
|
|
using v8::Just; |
28 |
|
|
using v8::Local; |
29 |
|
|
using v8::Maybe; |
30 |
|
|
using v8::MaybeLocal; |
31 |
|
|
using v8::Nothing; |
32 |
|
|
using v8::Object; |
33 |
|
|
using v8::SharedArrayBuffer; |
34 |
|
|
using v8::String; |
35 |
|
|
using v8::Symbol; |
36 |
|
|
using v8::Value; |
37 |
|
|
using v8::ValueDeserializer; |
38 |
|
|
using v8::ValueSerializer; |
39 |
|
|
using v8::WasmModuleObject; |
40 |
|
|
|
41 |
|
|
namespace node { |
42 |
|
|
|
43 |
|
|
using BaseObjectList = std::vector<BaseObjectPtr<BaseObject>>; |
44 |
|
|
|
45 |
|
|
BaseObject::TransferMode BaseObject::GetTransferMode() const { |
46 |
|
|
return BaseObject::TransferMode::kUntransferable; |
47 |
|
|
} |
48 |
|
|
|
49 |
|
|
std::unique_ptr<worker::TransferData> BaseObject::TransferForMessaging() { |
50 |
|
|
return CloneForMessaging(); |
51 |
|
|
} |
52 |
|
|
|
53 |
|
|
std::unique_ptr<worker::TransferData> BaseObject::CloneForMessaging() const { |
54 |
|
|
return {}; |
55 |
|
|
} |
56 |
|
|
|
57 |
|
|
Maybe<BaseObjectList> BaseObject::NestedTransferables() const { |
58 |
|
|
return Just(BaseObjectList {}); |
59 |
|
|
} |
60 |
|
|
|
61 |
|
|
Maybe<bool> BaseObject::FinalizeTransferRead( |
62 |
|
|
Local<Context> context, ValueDeserializer* deserializer) { |
63 |
|
|
return Just(true); |
64 |
|
|
} |
65 |
|
|
|
66 |
|
|
namespace worker { |
67 |
|
|
|
68 |
|
|
Maybe<bool> TransferData::FinalizeTransferWrite( |
69 |
|
|
Local<Context> context, ValueSerializer* serializer) { |
70 |
|
|
return Just(true); |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
Message::Message(MallocedBuffer<char>&& buffer) |
74 |
|
|
: main_message_buf_(std::move(buffer)) {} |
75 |
|
|
|
76 |
|
|
bool Message::IsCloseMessage() const { |
77 |
|
|
return main_message_buf_.data == nullptr; |
78 |
|
|
} |
79 |
|
|
|
80 |
|
|
namespace { |
81 |
|
|
|
82 |
|
|
// This is used to tell V8 how to read transferred host objects, like other |
83 |
|
|
// `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them. |
84 |
|
|
class DeserializerDelegate : public ValueDeserializer::Delegate { |
85 |
|
|
public: |
86 |
|
|
DeserializerDelegate( |
87 |
|
|
Message* m, |
88 |
|
|
Environment* env, |
89 |
|
|
const std::vector<BaseObjectPtr<BaseObject>>& host_objects, |
90 |
|
|
const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers, |
91 |
|
|
const std::vector<CompiledWasmModule>& wasm_modules) |
92 |
|
|
: host_objects_(host_objects), |
93 |
|
|
shared_array_buffers_(shared_array_buffers), |
94 |
|
|
wasm_modules_(wasm_modules) {} |
95 |
|
|
|
96 |
|
|
MaybeLocal<Object> ReadHostObject(Isolate* isolate) override { |
97 |
|
|
// Identifying the index in the message's BaseObject array is sufficient. |
98 |
|
|
uint32_t id; |
99 |
|
|
if (!deserializer->ReadUint32(&id)) |
100 |
|
|
return MaybeLocal<Object>(); |
101 |
|
|
CHECK_LE(id, host_objects_.size()); |
102 |
|
|
return host_objects_[id]->object(isolate); |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId( |
106 |
|
|
Isolate* isolate, uint32_t clone_id) override { |
107 |
|
|
CHECK_LE(clone_id, shared_array_buffers_.size()); |
108 |
|
|
return shared_array_buffers_[clone_id]; |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
MaybeLocal<WasmModuleObject> GetWasmModuleFromId( |
112 |
|
|
Isolate* isolate, uint32_t transfer_id) override { |
113 |
|
|
CHECK_LE(transfer_id, wasm_modules_.size()); |
114 |
|
|
return WasmModuleObject::FromCompiledModule( |
115 |
|
|
isolate, wasm_modules_[transfer_id]); |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
ValueDeserializer* deserializer = nullptr; |
119 |
|
|
|
120 |
|
|
private: |
121 |
|
|
const std::vector<BaseObjectPtr<BaseObject>>& host_objects_; |
122 |
|
|
const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers_; |
123 |
|
|
const std::vector<CompiledWasmModule>& wasm_modules_; |
124 |
|
|
}; |
125 |
|
|
|
126 |
|
|
} // anonymous namespace |
127 |
|
|
|
128 |
|
|
MaybeLocal<Value> Message::Deserialize(Environment* env, |
129 |
|
|
Local<Context> context) { |
130 |
|
|
CHECK(!IsCloseMessage()); |
131 |
|
|
|
132 |
|
|
EscapableHandleScope handle_scope(env->isolate()); |
133 |
|
|
Context::Scope context_scope(context); |
134 |
|
|
|
135 |
|
|
// Create all necessary objects for transferables, e.g. MessagePort handles. |
136 |
|
|
std::vector<BaseObjectPtr<BaseObject>> host_objects(transferables_.size()); |
137 |
|
|
auto cleanup = OnScopeLeave([&]() { |
138 |
|
|
for (BaseObjectPtr<BaseObject> object : host_objects) { |
139 |
|
|
if (!object) continue; |
140 |
|
|
|
141 |
|
|
// If the function did not finish successfully, host_objects will contain |
142 |
|
|
// a list of objects that will never be passed to JS. Therefore, we |
143 |
|
|
// destroy them here. |
144 |
|
|
object->Detach(); |
145 |
|
|
} |
146 |
|
|
}); |
147 |
|
|
|
148 |
|
|
for (uint32_t i = 0; i < transferables_.size(); ++i) { |
149 |
|
|
TransferData* data = transferables_[i].get(); |
150 |
|
|
host_objects[i] = data->Deserialize( |
151 |
|
|
env, context, std::move(transferables_[i])); |
152 |
|
|
if (!host_objects[i]) return {}; |
153 |
|
|
} |
154 |
|
|
transferables_.clear(); |
155 |
|
|
|
156 |
|
|
std::vector<Local<SharedArrayBuffer>> shared_array_buffers; |
157 |
|
|
// Attach all transferred SharedArrayBuffers to their new Isolate. |
158 |
|
|
for (uint32_t i = 0; i < shared_array_buffers_.size(); ++i) { |
159 |
|
|
Local<SharedArrayBuffer> sab = |
160 |
|
|
SharedArrayBuffer::New(env->isolate(), shared_array_buffers_[i]); |
161 |
|
|
shared_array_buffers.push_back(sab); |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
DeserializerDelegate delegate( |
165 |
|
|
this, env, host_objects, shared_array_buffers, wasm_modules_); |
166 |
|
|
ValueDeserializer deserializer( |
167 |
|
|
env->isolate(), |
168 |
|
|
reinterpret_cast<const uint8_t*>(main_message_buf_.data), |
169 |
|
|
main_message_buf_.size, |
170 |
|
|
&delegate); |
171 |
|
|
delegate.deserializer = &deserializer; |
172 |
|
|
|
173 |
|
|
// Attach all transferred ArrayBuffers to their new Isolate. |
174 |
|
|
for (uint32_t i = 0; i < array_buffers_.size(); ++i) { |
175 |
|
|
Local<ArrayBuffer> ab = |
176 |
|
|
ArrayBuffer::New(env->isolate(), std::move(array_buffers_[i])); |
177 |
|
|
deserializer.TransferArrayBuffer(i, ab); |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
if (deserializer.ReadHeader(context).IsNothing()) |
181 |
|
|
return {}; |
182 |
|
|
Local<Value> return_value; |
183 |
|
|
if (!deserializer.ReadValue(context).ToLocal(&return_value)) |
184 |
|
|
return {}; |
185 |
|
|
|
186 |
|
|
for (BaseObjectPtr<BaseObject> base_object : host_objects) { |
187 |
|
|
if (base_object->FinalizeTransferRead(context, &deserializer).IsNothing()) |
188 |
|
|
return {}; |
189 |
|
|
} |
190 |
|
|
|
191 |
|
|
host_objects.clear(); |
192 |
|
|
return handle_scope.Escape(return_value); |
193 |
|
|
} |
194 |
|
|
|
195 |
|
|
void Message::AddSharedArrayBuffer( |
196 |
|
|
std::shared_ptr<BackingStore> backing_store) { |
197 |
|
|
shared_array_buffers_.emplace_back(std::move(backing_store)); |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
void Message::AddTransferable(std::unique_ptr<TransferData>&& data) { |
201 |
|
|
transferables_.emplace_back(std::move(data)); |
202 |
|
|
} |
203 |
|
|
|
204 |
|
|
uint32_t Message::AddWASMModule(CompiledWasmModule&& mod) { |
205 |
|
|
wasm_modules_.emplace_back(std::move(mod)); |
206 |
|
|
return wasm_modules_.size() - 1; |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
namespace { |
210 |
|
|
|
211 |
|
|
MaybeLocal<Function> GetEmitMessageFunction(Local<Context> context) { |
212 |
|
|
Isolate* isolate = context->GetIsolate(); |
213 |
|
|
Local<Object> per_context_bindings; |
214 |
|
|
Local<Value> emit_message_val; |
215 |
|
|
if (!GetPerContextExports(context).ToLocal(&per_context_bindings) || |
216 |
|
|
!per_context_bindings->Get(context, |
217 |
|
|
FIXED_ONE_BYTE_STRING(isolate, "emitMessage")) |
218 |
|
|
.ToLocal(&emit_message_val)) { |
219 |
|
|
return MaybeLocal<Function>(); |
220 |
|
|
} |
221 |
|
|
CHECK(emit_message_val->IsFunction()); |
222 |
|
|
return emit_message_val.As<Function>(); |
223 |
|
|
} |
224 |
|
|
|
225 |
|
52 |
MaybeLocal<Function> GetDOMException(Local<Context> context) { |
226 |
|
52 |
Isolate* isolate = context->GetIsolate(); |
227 |
|
|
Local<Object> per_context_bindings; |
228 |
|
|
Local<Value> domexception_ctor_val; |
229 |
✓✗✗✓ ✗✓ |
208 |
if (!GetPerContextExports(context).ToLocal(&per_context_bindings) || |
230 |
|
156 |
!per_context_bindings->Get(context, |
231 |
|
156 |
FIXED_ONE_BYTE_STRING(isolate, "DOMException")) |
232 |
|
52 |
.ToLocal(&domexception_ctor_val)) { |
233 |
|
|
return MaybeLocal<Function>(); |
234 |
|
|
} |
235 |
✗✓ |
52 |
CHECK(domexception_ctor_val->IsFunction()); |
236 |
|
52 |
Local<Function> domexception_ctor = domexception_ctor_val.As<Function>(); |
237 |
|
52 |
return domexception_ctor; |
238 |
|
|
} |
239 |
|
|
|
240 |
|
|
void ThrowDataCloneException(Local<Context> context, Local<String> message) { |
241 |
|
|
Isolate* isolate = context->GetIsolate(); |
242 |
|
|
Local<Value> argv[] = {message, |
243 |
|
|
FIXED_ONE_BYTE_STRING(isolate, "DataCloneError")}; |
244 |
|
|
Local<Value> exception; |
245 |
|
|
Local<Function> domexception_ctor; |
246 |
|
|
if (!GetDOMException(context).ToLocal(&domexception_ctor) || |
247 |
|
|
!domexception_ctor->NewInstance(context, arraysize(argv), argv) |
248 |
|
|
.ToLocal(&exception)) { |
249 |
|
|
return; |
250 |
|
|
} |
251 |
|
|
isolate->ThrowException(exception); |
252 |
|
|
} |
253 |
|
|
|
254 |
|
|
// This tells V8 how to serialize objects that it does not understand |
255 |
|
|
// (e.g. C++ objects) into the output buffer, in a way that our own |
256 |
|
|
// DeserializerDelegate understands how to unpack. |
257 |
|
|
class SerializerDelegate : public ValueSerializer::Delegate { |
258 |
|
|
public: |
259 |
|
|
SerializerDelegate(Environment* env, Local<Context> context, Message* m) |
260 |
|
|
: env_(env), context_(context), msg_(m) {} |
261 |
|
|
|
262 |
|
|
void ThrowDataCloneError(Local<String> message) override { |
263 |
|
|
ThrowDataCloneException(context_, message); |
264 |
|
|
} |
265 |
|
|
|
266 |
|
|
Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override { |
267 |
|
|
if (env_->base_object_ctor_template()->HasInstance(object)) { |
268 |
|
|
return WriteHostObject( |
269 |
|
|
BaseObjectPtr<BaseObject> { Unwrap<BaseObject>(object) }); |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
ThrowDataCloneError(env_->clone_unsupported_type_str()); |
273 |
|
|
return Nothing<bool>(); |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
Maybe<uint32_t> GetSharedArrayBufferId( |
277 |
|
|
Isolate* isolate, |
278 |
|
|
Local<SharedArrayBuffer> shared_array_buffer) override { |
279 |
|
|
uint32_t i; |
280 |
|
|
for (i = 0; i < seen_shared_array_buffers_.size(); ++i) { |
281 |
|
|
if (PersistentToLocal::Strong(seen_shared_array_buffers_[i]) == |
282 |
|
|
shared_array_buffer) { |
283 |
|
|
return Just(i); |
284 |
|
|
} |
285 |
|
|
} |
286 |
|
|
|
287 |
|
|
seen_shared_array_buffers_.emplace_back( |
288 |
|
|
Global<SharedArrayBuffer> { isolate, shared_array_buffer }); |
289 |
|
|
msg_->AddSharedArrayBuffer(shared_array_buffer->GetBackingStore()); |
290 |
|
|
return Just(i); |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
Maybe<uint32_t> GetWasmModuleTransferId( |
294 |
|
|
Isolate* isolate, Local<WasmModuleObject> module) override { |
295 |
|
|
return Just(msg_->AddWASMModule(module->GetCompiledModule())); |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
Maybe<bool> Finish(Local<Context> context) { |
299 |
|
|
for (uint32_t i = 0; i < host_objects_.size(); i++) { |
300 |
|
|
BaseObjectPtr<BaseObject> host_object = std::move(host_objects_[i]); |
301 |
|
|
std::unique_ptr<TransferData> data; |
302 |
|
|
if (i < first_cloned_object_index_) |
303 |
|
|
data = host_object->TransferForMessaging(); |
304 |
|
|
if (!data) |
305 |
|
|
data = host_object->CloneForMessaging(); |
306 |
|
|
if (!data) return Nothing<bool>(); |
307 |
|
|
if (data->FinalizeTransferWrite(context, serializer).IsNothing()) |
308 |
|
|
return Nothing<bool>(); |
309 |
|
|
msg_->AddTransferable(std::move(data)); |
310 |
|
|
} |
311 |
|
|
return Just(true); |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
inline void AddHostObject(BaseObjectPtr<BaseObject> host_object) { |
315 |
|
|
// Make sure we have not started serializing the value itself yet. |
316 |
|
|
CHECK_EQ(first_cloned_object_index_, SIZE_MAX); |
317 |
|
|
host_objects_.emplace_back(std::move(host_object)); |
318 |
|
|
} |
319 |
|
|
|
320 |
|
|
// Some objects in the transfer list may register sub-objects that can be |
321 |
|
|
// transferred. This could e.g. be a public JS wrapper object, such as a |
322 |
|
|
// FileHandle, that is registering its C++ handle for transfer. |
323 |
|
|
inline Maybe<bool> AddNestedHostObjects() { |
324 |
|
|
for (size_t i = 0; i < host_objects_.size(); i++) { |
325 |
|
|
std::vector<BaseObjectPtr<BaseObject>> nested_transferables; |
326 |
|
|
if (!host_objects_[i]->NestedTransferables().To(&nested_transferables)) |
327 |
|
|
return Nothing<bool>(); |
328 |
|
|
for (auto nested_transferable : nested_transferables) { |
329 |
|
|
if (std::find(host_objects_.begin(), |
330 |
|
|
host_objects_.end(), |
331 |
|
|
nested_transferable) == host_objects_.end()) { |
332 |
|
|
AddHostObject(nested_transferable); |
333 |
|
|
} |
334 |
|
|
} |
335 |
|
|
} |
336 |
|
|
return Just(true); |
337 |
|
|
} |
338 |
|
|
|
339 |
|
|
ValueSerializer* serializer = nullptr; |
340 |
|
|
|
341 |
|
|
private: |
342 |
|
|
Maybe<bool> WriteHostObject(BaseObjectPtr<BaseObject> host_object) { |
343 |
|
|
BaseObject::TransferMode mode = host_object->GetTransferMode(); |
344 |
|
|
if (mode == BaseObject::TransferMode::kUntransferable) { |
345 |
|
|
ThrowDataCloneError(env_->clone_unsupported_type_str()); |
346 |
|
|
return Nothing<bool>(); |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
for (uint32_t i = 0; i < host_objects_.size(); i++) { |
350 |
|
|
if (host_objects_[i] == host_object) { |
351 |
|
|
serializer->WriteUint32(i); |
352 |
|
|
return Just(true); |
353 |
|
|
} |
354 |
|
|
} |
355 |
|
|
|
356 |
|
|
if (mode == BaseObject::TransferMode::kTransferable) { |
357 |
|
|
THROW_ERR_MISSING_TRANSFERABLE_IN_TRANSFER_LIST(env_); |
358 |
|
|
return Nothing<bool>(); |
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
CHECK_EQ(mode, BaseObject::TransferMode::kCloneable); |
362 |
|
|
uint32_t index = host_objects_.size(); |
363 |
|
|
if (first_cloned_object_index_ == SIZE_MAX) |
364 |
|
|
first_cloned_object_index_ = index; |
365 |
|
|
serializer->WriteUint32(index); |
366 |
|
|
host_objects_.push_back(host_object); |
367 |
|
|
return Just(true); |
368 |
|
|
} |
369 |
|
|
|
370 |
|
|
Environment* env_; |
371 |
|
|
Local<Context> context_; |
372 |
|
|
Message* msg_; |
373 |
|
|
std::vector<Global<SharedArrayBuffer>> seen_shared_array_buffers_; |
374 |
|
|
std::vector<BaseObjectPtr<BaseObject>> host_objects_; |
375 |
|
|
size_t first_cloned_object_index_ = SIZE_MAX; |
376 |
|
|
|
377 |
|
|
friend class worker::Message; |
378 |
|
|
}; |
379 |
|
|
|
380 |
|
|
} // anonymous namespace |
381 |
|
|
|
382 |
|
|
Maybe<bool> Message::Serialize(Environment* env, |
383 |
|
|
Local<Context> context, |
384 |
|
|
Local<Value> input, |
385 |
|
|
const TransferList& transfer_list_v, |
386 |
|
|
Local<Object> source_port) { |
387 |
|
|
HandleScope handle_scope(env->isolate()); |
388 |
|
|
Context::Scope context_scope(context); |
389 |
|
|
|
390 |
|
|
// Verify that we're not silently overwriting an existing message. |
391 |
|
|
CHECK(main_message_buf_.is_empty()); |
392 |
|
|
|
393 |
|
|
SerializerDelegate delegate(env, context, this); |
394 |
|
|
ValueSerializer serializer(env->isolate(), &delegate); |
395 |
|
|
delegate.serializer = &serializer; |
396 |
|
|
|
397 |
|
|
std::vector<Local<ArrayBuffer>> array_buffers; |
398 |
|
|
for (uint32_t i = 0; i < transfer_list_v.length(); ++i) { |
399 |
|
|
Local<Value> entry = transfer_list_v[i]; |
400 |
|
|
if (entry->IsObject()) { |
401 |
|
|
// See https://github.com/nodejs/node/pull/30339#issuecomment-552225353 |
402 |
|
|
// for details. |
403 |
|
|
bool untransferable; |
404 |
|
|
if (!entry.As<Object>()->HasPrivate( |
405 |
|
|
context, |
406 |
|
|
env->untransferable_object_private_symbol()) |
407 |
|
|
.To(&untransferable)) { |
408 |
|
|
return Nothing<bool>(); |
409 |
|
|
} |
410 |
|
|
if (untransferable) continue; |
411 |
|
|
} |
412 |
|
|
|
413 |
|
|
// Currently, we support ArrayBuffers and BaseObjects for which |
414 |
|
|
// GetTransferMode() does not return kUntransferable. |
415 |
|
|
if (entry->IsArrayBuffer()) { |
416 |
|
|
Local<ArrayBuffer> ab = entry.As<ArrayBuffer>(); |
417 |
|
|
// If we cannot render the ArrayBuffer unusable in this Isolate, |
418 |
|
|
// copying the buffer will have to do. |
419 |
|
|
// Note that we can currently transfer ArrayBuffers even if they were |
420 |
|
|
// not allocated by Node’s ArrayBufferAllocator in the first place, |
421 |
|
|
// because we pass the underlying v8::BackingStore around rather than |
422 |
|
|
// raw data *and* an Isolate with a non-default ArrayBuffer allocator |
423 |
|
|
// is always going to outlive any Workers it creates, and so will its |
424 |
|
|
// allocator along with it. |
425 |
|
|
if (!ab->IsDetachable()) continue; |
426 |
|
|
if (std::find(array_buffers.begin(), array_buffers.end(), ab) != |
427 |
|
|
array_buffers.end()) { |
428 |
|
|
ThrowDataCloneException( |
429 |
|
|
context, |
430 |
|
|
FIXED_ONE_BYTE_STRING( |
431 |
|
|
env->isolate(), |
432 |
|
|
"Transfer list contains duplicate ArrayBuffer")); |
433 |
|
|
return Nothing<bool>(); |
434 |
|
|
} |
435 |
|
|
// We simply use the array index in the `array_buffers` list as the |
436 |
|
|
// ID that we write into the serialized buffer. |
437 |
|
|
uint32_t id = array_buffers.size(); |
438 |
|
|
array_buffers.push_back(ab); |
439 |
|
|
serializer.TransferArrayBuffer(id, ab); |
440 |
|
|
continue; |
441 |
|
|
} else if (env->base_object_ctor_template()->HasInstance(entry)) { |
442 |
|
|
// Check if the source MessagePort is being transferred. |
443 |
|
|
if (!source_port.IsEmpty() && entry == source_port) { |
444 |
|
|
ThrowDataCloneException( |
445 |
|
|
context, |
446 |
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), |
447 |
|
|
"Transfer list contains source port")); |
448 |
|
|
return Nothing<bool>(); |
449 |
|
|
} |
450 |
|
|
BaseObjectPtr<BaseObject> host_object { |
451 |
|
|
Unwrap<BaseObject>(entry.As<Object>()) }; |
452 |
|
|
if (env->message_port_constructor_template()->HasInstance(entry) && |
453 |
|
|
(!host_object || |
454 |
|
|
static_cast<MessagePort*>(host_object.get())->IsDetached())) { |
455 |
|
|
ThrowDataCloneException( |
456 |
|
|
context, |
457 |
|
|
FIXED_ONE_BYTE_STRING( |
458 |
|
|
env->isolate(), |
459 |
|
|
"MessagePort in transfer list is already detached")); |
460 |
|
|
return Nothing<bool>(); |
461 |
|
|
} |
462 |
|
|
if (std::find(delegate.host_objects_.begin(), |
463 |
|
|
delegate.host_objects_.end(), |
464 |
|
|
host_object) != delegate.host_objects_.end()) { |
465 |
|
|
ThrowDataCloneException( |
466 |
|
|
context, |
467 |
|
|
String::Concat(env->isolate(), |
468 |
|
|
FIXED_ONE_BYTE_STRING( |
469 |
|
|
env->isolate(), |
470 |
|
|
"Transfer list contains duplicate "), |
471 |
|
|
entry.As<Object>()->GetConstructorName())); |
472 |
|
|
return Nothing<bool>(); |
473 |
|
|
} |
474 |
|
|
if (host_object && host_object->GetTransferMode() != |
475 |
|
|
BaseObject::TransferMode::kUntransferable) { |
476 |
|
|
delegate.AddHostObject(host_object); |
477 |
|
|
continue; |
478 |
|
|
} |
479 |
|
|
} |
480 |
|
|
|
481 |
|
|
THROW_ERR_INVALID_TRANSFER_OBJECT(env); |
482 |
|
|
return Nothing<bool>(); |
483 |
|
|
} |
484 |
|
|
if (delegate.AddNestedHostObjects().IsNothing()) |
485 |
|
|
return Nothing<bool>(); |
486 |
|
|
|
487 |
|
|
serializer.WriteHeader(); |
488 |
|
|
if (serializer.WriteValue(context, input).IsNothing()) { |
489 |
|
|
return Nothing<bool>(); |
490 |
|
|
} |
491 |
|
|
|
492 |
|
|
for (Local<ArrayBuffer> ab : array_buffers) { |
493 |
|
|
// If serialization succeeded, we render it inaccessible in this Isolate. |
494 |
|
|
std::shared_ptr<BackingStore> backing_store = ab->GetBackingStore(); |
495 |
|
|
ab->Detach(); |
496 |
|
|
|
497 |
|
|
array_buffers_.emplace_back(std::move(backing_store)); |
498 |
|
|
} |
499 |
|
|
|
500 |
|
|
if (delegate.Finish(context).IsNothing()) |
501 |
|
|
return Nothing<bool>(); |
502 |
|
|
|
503 |
|
|
// The serializer gave us a buffer allocated using `malloc()`. |
504 |
|
|
std::pair<uint8_t*, size_t> data = serializer.Release(); |
505 |
|
|
CHECK_NOT_NULL(data.first); |
506 |
|
|
main_message_buf_ = |
507 |
|
|
MallocedBuffer<char>(reinterpret_cast<char*>(data.first), data.second); |
508 |
|
|
return Just(true); |
509 |
|
|
} |
510 |
|
|
|
511 |
|
|
void Message::MemoryInfo(MemoryTracker* tracker) const { |
512 |
|
|
tracker->TrackField("array_buffers_", array_buffers_); |
513 |
|
|
tracker->TrackField("shared_array_buffers", shared_array_buffers_); |
514 |
|
|
tracker->TrackField("transferables", transferables_); |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
MessagePortData::MessagePortData(MessagePort* owner) |
518 |
|
|
: owner_(owner) { |
519 |
|
|
} |
520 |
|
|
|
521 |
|
|
MessagePortData::~MessagePortData() { |
522 |
|
|
CHECK_NULL(owner_); |
523 |
|
|
Disentangle(); |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
void MessagePortData::MemoryInfo(MemoryTracker* tracker) const { |
527 |
|
|
Mutex::ScopedLock lock(mutex_); |
528 |
|
|
tracker->TrackField("incoming_messages", incoming_messages_); |
529 |
|
|
} |
530 |
|
|
|
531 |
|
|
void MessagePortData::AddToIncomingQueue(std::shared_ptr<Message> message) { |
532 |
|
|
// This function will be called by other threads. |
533 |
|
|
Mutex::ScopedLock lock(mutex_); |
534 |
|
|
incoming_messages_.emplace_back(std::move(message)); |
535 |
|
|
|
536 |
|
|
if (owner_ != nullptr) { |
537 |
|
|
Debug(owner_, "Adding message to incoming queue"); |
538 |
|
|
owner_->TriggerAsync(); |
539 |
|
|
} |
540 |
|
|
} |
541 |
|
|
|
542 |
|
|
void MessagePortData::Entangle(MessagePortData* a, MessagePortData* b) { |
543 |
|
|
auto group = std::make_shared<SiblingGroup>(); |
544 |
|
|
group->Entangle({a, b}); |
545 |
|
|
} |
546 |
|
|
|
547 |
|
|
void MessagePortData::Disentangle() { |
548 |
|
|
if (group_) { |
549 |
|
|
group_->Disentangle(this); |
550 |
|
|
} |
551 |
|
|
} |
552 |
|
|
|
553 |
|
|
MessagePort::~MessagePort() { |
554 |
|
|
if (data_) Detach(); |
555 |
|
|
} |
556 |
|
|
|
557 |
|
|
MessagePort::MessagePort(Environment* env, |
558 |
|
|
Local<Context> context, |
559 |
|
|
Local<Object> wrap) |
560 |
|
|
: HandleWrap(env, |
561 |
|
|
wrap, |
562 |
|
|
reinterpret_cast<uv_handle_t*>(&async_), |
563 |
|
|
AsyncWrap::PROVIDER_MESSAGEPORT), |
564 |
|
|
data_(new MessagePortData(this)) { |
565 |
|
|
auto onmessage = [](uv_async_t* handle) { |
566 |
|
|
// Called when data has been put into the queue. |
567 |
|
|
MessagePort* channel = ContainerOf(&MessagePort::async_, handle); |
568 |
|
|
channel->OnMessage(); |
569 |
|
|
}; |
570 |
|
|
|
571 |
|
|
CHECK_EQ(uv_async_init(env->event_loop(), |
572 |
|
|
&async_, |
573 |
|
|
onmessage), 0); |
574 |
|
|
// Reset later to indicate success of the constructor. |
575 |
|
|
bool succeeded = false; |
576 |
|
|
auto cleanup = OnScopeLeave([&]() { if (!succeeded) Close(); }); |
577 |
|
|
|
578 |
|
|
Local<Value> fn; |
579 |
|
|
if (!wrap->Get(context, env->oninit_symbol()).ToLocal(&fn)) |
580 |
|
|
return; |
581 |
|
|
|
582 |
|
|
if (fn->IsFunction()) { |
583 |
|
|
Local<Function> init = fn.As<Function>(); |
584 |
|
|
if (init->Call(context, wrap, 0, nullptr).IsEmpty()) |
585 |
|
|
return; |
586 |
|
|
} |
587 |
|
|
|
588 |
|
|
Local<Function> emit_message_fn; |
589 |
|
|
if (!GetEmitMessageFunction(context).ToLocal(&emit_message_fn)) |
590 |
|
|
return; |
591 |
|
|
emit_message_fn_.Reset(env->isolate(), emit_message_fn); |
592 |
|
|
|
593 |
|
|
succeeded = true; |
594 |
|
|
Debug(this, "Created message port"); |
595 |
|
|
} |
596 |
|
|
|
597 |
|
|
bool MessagePort::IsDetached() const { |
598 |
|
|
return data_ == nullptr || IsHandleClosing(); |
599 |
|
|
} |
600 |
|
|
|
601 |
|
|
void MessagePort::TriggerAsync() { |
602 |
|
|
if (IsHandleClosing()) return; |
603 |
|
|
CHECK_EQ(uv_async_send(&async_), 0); |
604 |
|
|
} |
605 |
|
|
|
606 |
|
|
void MessagePort::Close(v8::Local<v8::Value> close_callback) { |
607 |
|
|
Debug(this, "Closing message port, data set = %d", static_cast<int>(!!data_)); |
608 |
|
|
|
609 |
|
|
if (data_) { |
610 |
|
|
// Wrap this call with accessing the mutex, so that TriggerAsync() |
611 |
|
|
// can check IsHandleClosing() without race conditions. |
612 |
|
|
Mutex::ScopedLock sibling_lock(data_->mutex_); |
613 |
|
|
HandleWrap::Close(close_callback); |
614 |
|
|
} else { |
615 |
|
|
HandleWrap::Close(close_callback); |
616 |
|
|
} |
617 |
|
|
} |
618 |
|
|
|
619 |
|
|
void MessagePort::New(const FunctionCallbackInfo<Value>& args) { |
620 |
|
|
// This constructor just throws an error. Unfortunately, we can’t use V8’s |
621 |
|
|
// ConstructorBehavior::kThrow, as that also removes the prototype from the |
622 |
|
|
// class (i.e. makes it behave like an arrow function). |
623 |
|
|
Environment* env = Environment::GetCurrent(args); |
624 |
|
|
THROW_ERR_CONSTRUCT_CALL_INVALID(env); |
625 |
|
|
} |
626 |
|
|
|
627 |
|
|
MessagePort* MessagePort::New( |
628 |
|
|
Environment* env, |
629 |
|
|
Local<Context> context, |
630 |
|
|
std::unique_ptr<MessagePortData> data, |
631 |
|
|
std::shared_ptr<SiblingGroup> sibling_group) { |
632 |
|
|
Context::Scope context_scope(context); |
633 |
|
|
Local<FunctionTemplate> ctor_templ = GetMessagePortConstructorTemplate(env); |
634 |
|
|
|
635 |
|
|
// Construct a new instance, then assign the listener instance and possibly |
636 |
|
|
// the MessagePortData to it. |
637 |
|
|
Local<Object> instance; |
638 |
|
|
if (!ctor_templ->InstanceTemplate()->NewInstance(context).ToLocal(&instance)) |
639 |
|
|
return nullptr; |
640 |
|
|
MessagePort* port = new MessagePort(env, context, instance); |
641 |
|
|
CHECK_NOT_NULL(port); |
642 |
|
|
if (port->IsHandleClosing()) { |
643 |
|
|
// Construction failed with an exception. |
644 |
|
|
return nullptr; |
645 |
|
|
} |
646 |
|
|
|
647 |
|
|
if (data) { |
648 |
|
|
CHECK(!sibling_group); |
649 |
|
|
port->Detach(); |
650 |
|
|
port->data_ = std::move(data); |
651 |
|
|
|
652 |
|
|
// This lock is here to avoid race conditions with the `owner_` read |
653 |
|
|
// in AddToIncomingQueue(). (This would likely be unproblematic without it, |
654 |
|
|
// but it's better to be safe than sorry.) |
655 |
|
|
Mutex::ScopedLock lock(port->data_->mutex_); |
656 |
|
|
port->data_->owner_ = port; |
657 |
|
|
// If the existing MessagePortData object had pending messages, this is |
658 |
|
|
// the easiest way to run that queue. |
659 |
|
|
port->TriggerAsync(); |
660 |
|
|
} else if (sibling_group) { |
661 |
|
|
sibling_group->Entangle(port->data_.get()); |
662 |
|
|
} |
663 |
|
|
return port; |
664 |
|
|
} |
665 |
|
|
|
666 |
|
|
MaybeLocal<Value> MessagePort::ReceiveMessage(Local<Context> context, |
667 |
|
|
bool only_if_receiving) { |
668 |
|
|
std::shared_ptr<Message> received; |
669 |
|
|
{ |
670 |
|
|
// Get the head of the message queue. |
671 |
|
|
Mutex::ScopedLock lock(data_->mutex_); |
672 |
|
|
|
673 |
|
|
Debug(this, "MessagePort has message"); |
674 |
|
|
|
675 |
|
|
bool wants_message = receiving_messages_ || !only_if_receiving; |
676 |
|
|
// We have nothing to do if: |
677 |
|
|
// - There are no pending messages |
678 |
|
|
// - We are not intending to receive messages, and the message we would |
679 |
|
|
// receive is not the final "close" message. |
680 |
|
|
if (data_->incoming_messages_.empty() || |
681 |
|
|
(!wants_message && |
682 |
|
|
!data_->incoming_messages_.front()->IsCloseMessage())) { |
683 |
|
|
return env()->no_message_symbol(); |
684 |
|
|
} |
685 |
|
|
|
686 |
|
|
received = data_->incoming_messages_.front(); |
687 |
|
|
data_->incoming_messages_.pop_front(); |
688 |
|
|
} |
689 |
|
|
|
690 |
|
|
if (received->IsCloseMessage()) { |
691 |
|
|
Close(); |
692 |
|
|
return env()->no_message_symbol(); |
693 |
|
|
} |
694 |
|
|
|
695 |
|
|
if (!env()->can_call_into_js()) return MaybeLocal<Value>(); |
696 |
|
|
|
697 |
|
|
return received->Deserialize(env(), context); |
698 |
|
|
} |
699 |
|
|
|
700 |
|
|
void MessagePort::OnMessage() { |
701 |
|
|
Debug(this, "Running MessagePort::OnMessage()"); |
702 |
|
|
HandleScope handle_scope(env()->isolate()); |
703 |
|
|
Local<Context> context = object(env()->isolate())->CreationContext(); |
704 |
|
|
|
705 |
|
|
size_t processing_limit; |
706 |
|
|
{ |
707 |
|
|
Mutex::ScopedLock(data_->mutex_); |
708 |
|
|
processing_limit = std::max(data_->incoming_messages_.size(), |
709 |
|
|
static_cast<size_t>(1000)); |
710 |
|
|
} |
711 |
|
|
|
712 |
|
|
// data_ can only ever be modified by the owner thread, so no need to lock. |
713 |
|
|
// However, the message port may be transferred while it is processing |
714 |
|
|
// messages, so we need to check that this handle still owns its `data_` field |
715 |
|
|
// on every iteration. |
716 |
|
|
while (data_) { |
717 |
|
|
if (processing_limit-- == 0) { |
718 |
|
|
// Prevent event loop starvation by only processing those messages without |
719 |
|
|
// interruption that were already present when the OnMessage() call was |
720 |
|
|
// first triggered, but at least 1000 messages because otherwise the |
721 |
|
|
// overhead of repeatedly triggering the uv_async_t instance becomes |
722 |
|
|
// noticable, at least on Windows. |
723 |
|
|
// (That might require more investigation by somebody more familiar with |
724 |
|
|
// Windows.) |
725 |
|
|
TriggerAsync(); |
726 |
|
|
return; |
727 |
|
|
} |
728 |
|
|
|
729 |
|
|
HandleScope handle_scope(env()->isolate()); |
730 |
|
|
Context::Scope context_scope(context); |
731 |
|
|
Local<Function> emit_message = PersistentToLocal::Strong(emit_message_fn_); |
732 |
|
|
|
733 |
|
|
Local<Value> payload; |
734 |
|
|
Local<Value> message_error; |
735 |
|
|
Local<Value> argv[2]; |
736 |
|
|
|
737 |
|
|
{ |
738 |
|
|
// Catch any exceptions from parsing the message itself (not from |
739 |
|
|
// emitting it) as 'messageeror' events. |
740 |
|
|
TryCatchScope try_catch(env()); |
741 |
|
|
if (!ReceiveMessage(context, true).ToLocal(&payload)) { |
742 |
|
|
if (try_catch.HasCaught() && !try_catch.HasTerminated()) |
743 |
|
|
message_error = try_catch.Exception(); |
744 |
|
|
goto reschedule; |
745 |
|
|
} |
746 |
|
|
} |
747 |
|
|
if (payload == env()->no_message_symbol()) break; |
748 |
|
|
|
749 |
|
|
if (!env()->can_call_into_js()) { |
750 |
|
|
Debug(this, "MessagePort drains queue because !can_call_into_js()"); |
751 |
|
|
// In this case there is nothing to do but to drain the current queue. |
752 |
|
|
continue; |
753 |
|
|
} |
754 |
|
|
|
755 |
|
|
argv[0] = payload; |
756 |
|
|
argv[1] = env()->message_string(); |
757 |
|
|
|
758 |
|
|
if (MakeCallback(emit_message, arraysize(argv), argv).IsEmpty()) { |
759 |
|
|
reschedule: |
760 |
|
|
if (!message_error.IsEmpty()) { |
761 |
|
|
argv[0] = message_error; |
762 |
|
|
argv[1] = env()->messageerror_string(); |
763 |
|
|
USE(MakeCallback(emit_message, arraysize(argv), argv)); |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
// Re-schedule OnMessage() execution in case of failure. |
767 |
|
|
if (data_) |
768 |
|
|
TriggerAsync(); |
769 |
|
|
return; |
770 |
|
|
} |
771 |
|
|
} |
772 |
|
|
} |
773 |
|
|
|
774 |
|
|
void MessagePort::OnClose() { |
775 |
|
|
Debug(this, "MessagePort::OnClose()"); |
776 |
|
|
if (data_) { |
777 |
|
|
// Detach() returns move(data_). |
778 |
|
|
Detach()->Disentangle(); |
779 |
|
|
} |
780 |
|
|
} |
781 |
|
|
|
782 |
|
|
std::unique_ptr<MessagePortData> MessagePort::Detach() { |
783 |
|
|
CHECK(data_); |
784 |
|
|
Mutex::ScopedLock lock(data_->mutex_); |
785 |
|
|
data_->owner_ = nullptr; |
786 |
|
|
return std::move(data_); |
787 |
|
|
} |
788 |
|
|
|
789 |
|
|
BaseObject::TransferMode MessagePort::GetTransferMode() const { |
790 |
|
|
if (IsDetached()) |
791 |
|
|
return BaseObject::TransferMode::kUntransferable; |
792 |
|
|
return BaseObject::TransferMode::kTransferable; |
793 |
|
|
} |
794 |
|
|
|
795 |
|
|
std::unique_ptr<TransferData> MessagePort::TransferForMessaging() { |
796 |
|
|
Close(); |
797 |
|
|
return Detach(); |
798 |
|
|
} |
799 |
|
|
|
800 |
|
|
BaseObjectPtr<BaseObject> MessagePortData::Deserialize( |
801 |
|
|
Environment* env, |
802 |
|
|
Local<Context> context, |
803 |
|
|
std::unique_ptr<TransferData> self) { |
804 |
|
|
return BaseObjectPtr<MessagePort> { MessagePort::New( |
805 |
|
|
env, context, |
806 |
|
|
static_unique_pointer_cast<MessagePortData>(std::move(self))) }; |
807 |
|
|
} |
808 |
|
|
|
809 |
|
|
Maybe<bool> MessagePort::PostMessage(Environment* env, |
810 |
|
|
Local<Value> message_v, |
811 |
|
|
const TransferList& transfer_v) { |
812 |
|
|
Isolate* isolate = env->isolate(); |
813 |
|
|
Local<Object> obj = object(isolate); |
814 |
|
|
Local<Context> context = obj->CreationContext(); |
815 |
|
|
|
816 |
|
|
std::shared_ptr<Message> msg = std::make_shared<Message>(); |
817 |
|
|
|
818 |
|
|
// Per spec, we need to both check if transfer list has the source port, and |
819 |
|
|
// serialize the input message, even if the MessagePort is closed or detached. |
820 |
|
|
|
821 |
|
|
Maybe<bool> serialization_maybe = |
822 |
|
|
msg->Serialize(env, context, message_v, transfer_v, obj); |
823 |
|
|
if (data_ == nullptr) { |
824 |
|
|
return serialization_maybe; |
825 |
|
|
} |
826 |
|
|
if (serialization_maybe.IsNothing()) { |
827 |
|
|
return Nothing<bool>(); |
828 |
|
|
} |
829 |
|
|
|
830 |
|
|
std::string error; |
831 |
|
|
Maybe<bool> res = data_->Dispatch(msg, &error); |
832 |
|
|
if (res.IsNothing()) |
833 |
|
|
return res; |
834 |
|
|
|
835 |
|
|
if (!error.empty()) |
836 |
|
|
ProcessEmitWarning(env, error.c_str()); |
837 |
|
|
|
838 |
|
|
return res; |
839 |
|
|
} |
840 |
|
|
|
841 |
|
|
Maybe<bool> MessagePortData::Dispatch( |
842 |
|
|
std::shared_ptr<Message> message, |
843 |
|
|
std::string* error) { |
844 |
|
|
if (!group_) { |
845 |
|
|
if (error != nullptr) |
846 |
|
|
*error = "MessagePortData is not entangled."; |
847 |
|
|
return Nothing<bool>(); |
848 |
|
|
} |
849 |
|
|
return group_->Dispatch(this, message, error); |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
static Maybe<bool> ReadIterable(Environment* env, |
853 |
|
|
Local<Context> context, |
854 |
|
|
// NOLINTNEXTLINE(runtime/references) |
855 |
|
|
TransferList& transfer_list, |
856 |
|
|
Local<Value> object) { |
857 |
|
|
if (!object->IsObject()) return Just(false); |
858 |
|
|
|
859 |
|
|
if (object->IsArray()) { |
860 |
|
|
Local<Array> arr = object.As<Array>(); |
861 |
|
|
size_t length = arr->Length(); |
862 |
|
|
transfer_list.AllocateSufficientStorage(length); |
863 |
|
|
for (size_t i = 0; i < length; i++) { |
864 |
|
|
if (!arr->Get(context, i).ToLocal(&transfer_list[i])) |
865 |
|
|
return Nothing<bool>(); |
866 |
|
|
} |
867 |
|
|
return Just(true); |
868 |
|
|
} |
869 |
|
|
|
870 |
|
|
Isolate* isolate = env->isolate(); |
871 |
|
|
Local<Value> iterator_method; |
872 |
|
|
if (!object.As<Object>()->Get(context, Symbol::GetIterator(isolate)) |
873 |
|
|
.ToLocal(&iterator_method)) return Nothing<bool>(); |
874 |
|
|
if (!iterator_method->IsFunction()) return Just(false); |
875 |
|
|
|
876 |
|
|
Local<Value> iterator; |
877 |
|
|
if (!iterator_method.As<Function>()->Call(context, object, 0, nullptr) |
878 |
|
|
.ToLocal(&iterator)) return Nothing<bool>(); |
879 |
|
|
if (!iterator->IsObject()) return Just(false); |
880 |
|
|
|
881 |
|
|
Local<Value> next; |
882 |
|
|
if (!iterator.As<Object>()->Get(context, env->next_string()).ToLocal(&next)) |
883 |
|
|
return Nothing<bool>(); |
884 |
|
|
if (!next->IsFunction()) return Just(false); |
885 |
|
|
|
886 |
|
|
std::vector<Local<Value>> entries; |
887 |
|
|
while (env->can_call_into_js()) { |
888 |
|
|
Local<Value> result; |
889 |
|
|
if (!next.As<Function>()->Call(context, iterator, 0, nullptr) |
890 |
|
|
.ToLocal(&result)) return Nothing<bool>(); |
891 |
|
|
if (!result->IsObject()) return Just(false); |
892 |
|
|
|
893 |
|
|
Local<Value> done; |
894 |
|
|
if (!result.As<Object>()->Get(context, env->done_string()).ToLocal(&done)) |
895 |
|
|
return Nothing<bool>(); |
896 |
|
|
if (done->BooleanValue(isolate)) break; |
897 |
|
|
|
898 |
|
|
Local<Value> val; |
899 |
|
|
if (!result.As<Object>()->Get(context, env->value_string()).ToLocal(&val)) |
900 |
|
|
return Nothing<bool>(); |
901 |
|
|
entries.push_back(val); |
902 |
|
|
} |
903 |
|
|
|
904 |
|
|
transfer_list.AllocateSufficientStorage(entries.size()); |
905 |
|
|
std::copy(entries.begin(), entries.end(), &transfer_list[0]); |
906 |
|
|
return Just(true); |
907 |
|
|
} |
908 |
|
|
|
909 |
|
|
void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) { |
910 |
|
|
Environment* env = Environment::GetCurrent(args); |
911 |
|
|
Local<Object> obj = args.This(); |
912 |
|
|
Local<Context> context = obj->CreationContext(); |
913 |
|
|
|
914 |
|
|
if (args.Length() == 0) { |
915 |
|
|
return THROW_ERR_MISSING_ARGS(env, "Not enough arguments to " |
916 |
|
|
"MessagePort.postMessage"); |
917 |
|
|
} |
918 |
|
|
|
919 |
|
|
if (!args[1]->IsNullOrUndefined() && !args[1]->IsObject()) { |
920 |
|
|
// Browsers ignore null or undefined, and otherwise accept an array or an |
921 |
|
|
// options object. |
922 |
|
|
return THROW_ERR_INVALID_ARG_TYPE(env, |
923 |
|
|
"Optional transferList argument must be an iterable"); |
924 |
|
|
} |
925 |
|
|
|
926 |
|
|
TransferList transfer_list; |
927 |
|
|
if (args[1]->IsObject()) { |
928 |
|
|
bool was_iterable; |
929 |
|
|
if (!ReadIterable(env, context, transfer_list, args[1]).To(&was_iterable)) |
930 |
|
|
return; |
931 |
|
|
if (!was_iterable) { |
932 |
|
|
Local<Value> transfer_option; |
933 |
|
|
if (!args[1].As<Object>()->Get(context, env->transfer_string()) |
934 |
|
|
.ToLocal(&transfer_option)) return; |
935 |
|
|
if (!transfer_option->IsUndefined()) { |
936 |
|
|
if (!ReadIterable(env, context, transfer_list, transfer_option) |
937 |
|
|
.To(&was_iterable)) return; |
938 |
|
|
if (!was_iterable) { |
939 |
|
|
return THROW_ERR_INVALID_ARG_TYPE(env, |
940 |
|
|
"Optional options.transfer argument must be an iterable"); |
941 |
|
|
} |
942 |
|
|
} |
943 |
|
|
} |
944 |
|
|
} |
945 |
|
|
|
946 |
|
|
MessagePort* port = Unwrap<MessagePort>(args.This()); |
947 |
|
|
// Even if the backing MessagePort object has already been deleted, we still |
948 |
|
|
// want to serialize the message to ensure spec-compliant behavior w.r.t. |
949 |
|
|
// transfers. |
950 |
|
|
if (port == nullptr) { |
951 |
|
|
Message msg; |
952 |
|
|
USE(msg.Serialize(env, context, args[0], transfer_list, obj)); |
953 |
|
|
return; |
954 |
|
|
} |
955 |
|
|
|
956 |
|
|
Maybe<bool> res = port->PostMessage(env, args[0], transfer_list); |
957 |
|
|
if (res.IsJust()) |
958 |
|
|
args.GetReturnValue().Set(res.FromJust()); |
959 |
|
|
} |
960 |
|
|
|
961 |
|
|
void MessagePort::Start() { |
962 |
|
|
Debug(this, "Start receiving messages"); |
963 |
|
|
receiving_messages_ = true; |
964 |
|
|
Mutex::ScopedLock lock(data_->mutex_); |
965 |
|
|
if (!data_->incoming_messages_.empty()) |
966 |
|
|
TriggerAsync(); |
967 |
|
|
} |
968 |
|
|
|
969 |
|
|
void MessagePort::Stop() { |
970 |
|
|
Debug(this, "Stop receiving messages"); |
971 |
|
|
receiving_messages_ = false; |
972 |
|
|
} |
973 |
|
|
|
974 |
|
|
void MessagePort::Start(const FunctionCallbackInfo<Value>& args) { |
975 |
|
|
MessagePort* port; |
976 |
|
|
ASSIGN_OR_RETURN_UNWRAP(&port, args.This()); |
977 |
|
|
if (!port->data_) { |
978 |
|
|
return; |
979 |
|
|
} |
980 |
|
|
port->Start(); |
981 |
|
|
} |
982 |
|
|
|
983 |
|
|
void MessagePort::Stop(const FunctionCallbackInfo<Value>& args) { |
984 |
|
|
MessagePort* port; |
985 |
|
|
CHECK(args[0]->IsObject()); |
986 |
|
|
ASSIGN_OR_RETURN_UNWRAP(&port, args[0].As<Object>()); |
987 |
|
|
if (!port->data_) { |
988 |
|
|
return; |
989 |
|
|
} |
990 |
|
|
port->Stop(); |
991 |
|
|
} |
992 |
|
|
|
993 |
|
|
void MessagePort::CheckType(const FunctionCallbackInfo<Value>& args) { |
994 |
|
|
Environment* env = Environment::GetCurrent(args); |
995 |
|
|
args.GetReturnValue().Set( |
996 |
|
|
GetMessagePortConstructorTemplate(env)->HasInstance(args[0])); |
997 |
|
|
} |
998 |
|
|
|
999 |
|
|
void MessagePort::Drain(const FunctionCallbackInfo<Value>& args) { |
1000 |
|
|
MessagePort* port; |
1001 |
|
|
ASSIGN_OR_RETURN_UNWRAP(&port, args[0].As<Object>()); |
1002 |
|
|
port->OnMessage(); |
1003 |
|
|
} |
1004 |
|
|
|
1005 |
|
|
void MessagePort::ReceiveMessage(const FunctionCallbackInfo<Value>& args) { |
1006 |
|
|
Environment* env = Environment::GetCurrent(args); |
1007 |
|
|
if (!args[0]->IsObject() || |
1008 |
|
|
!env->message_port_constructor_template()->HasInstance(args[0])) { |
1009 |
|
|
return THROW_ERR_INVALID_ARG_TYPE(env, |
1010 |
|
|
"The \"port\" argument must be a MessagePort instance"); |
1011 |
|
|
} |
1012 |
|
|
MessagePort* port = Unwrap<MessagePort>(args[0].As<Object>()); |
1013 |
|
|
if (port == nullptr) { |
1014 |
|
|
// Return 'no messages' for a closed port. |
1015 |
|
|
args.GetReturnValue().Set( |
1016 |
|
|
Environment::GetCurrent(args)->no_message_symbol()); |
1017 |
|
|
return; |
1018 |
|
|
} |
1019 |
|
|
|
1020 |
|
|
MaybeLocal<Value> payload = |
1021 |
|
|
port->ReceiveMessage(port->object()->CreationContext(), false); |
1022 |
|
|
if (!payload.IsEmpty()) |
1023 |
|
|
args.GetReturnValue().Set(payload.ToLocalChecked()); |
1024 |
|
|
} |
1025 |
|
|
|
1026 |
|
|
void MessagePort::MoveToContext(const FunctionCallbackInfo<Value>& args) { |
1027 |
|
|
Environment* env = Environment::GetCurrent(args); |
1028 |
|
|
if (!args[0]->IsObject() || |
1029 |
|
|
!env->message_port_constructor_template()->HasInstance(args[0])) { |
1030 |
|
|
return THROW_ERR_INVALID_ARG_TYPE(env, |
1031 |
|
|
"The \"port\" argument must be a MessagePort instance"); |
1032 |
|
|
} |
1033 |
|
|
MessagePort* port = Unwrap<MessagePort>(args[0].As<Object>()); |
1034 |
|
|
CHECK_NOT_NULL(port); |
1035 |
|
|
|
1036 |
|
|
Local<Value> context_arg = args[1]; |
1037 |
|
|
ContextifyContext* context_wrapper; |
1038 |
|
|
if (!context_arg->IsObject() || |
1039 |
|
|
(context_wrapper = ContextifyContext::ContextFromContextifiedSandbox( |
1040 |
|
|
env, context_arg.As<Object>())) == nullptr) { |
1041 |
|
|
return THROW_ERR_INVALID_ARG_TYPE(env, "Invalid context argument"); |
1042 |
|
|
} |
1043 |
|
|
|
1044 |
|
|
std::unique_ptr<MessagePortData> data; |
1045 |
|
|
if (!port->IsDetached()) |
1046 |
|
|
data = port->Detach(); |
1047 |
|
|
|
1048 |
|
|
Context::Scope context_scope(context_wrapper->context()); |
1049 |
|
|
MessagePort* target = |
1050 |
|
|
MessagePort::New(env, context_wrapper->context(), std::move(data)); |
1051 |
|
|
if (target != nullptr) |
1052 |
|
|
args.GetReturnValue().Set(target->object()); |
1053 |
|
|
} |
1054 |
|
|
|
1055 |
|
|
void MessagePort::Entangle(MessagePort* a, MessagePort* b) { |
1056 |
|
|
MessagePortData::Entangle(a->data_.get(), b->data_.get()); |
1057 |
|
|
} |
1058 |
|
|
|
1059 |
|
|
void MessagePort::Entangle(MessagePort* a, MessagePortData* b) { |
1060 |
|
|
MessagePortData::Entangle(a->data_.get(), b); |
1061 |
|
|
} |
1062 |
|
|
|
1063 |
|
|
void MessagePort::MemoryInfo(MemoryTracker* tracker) const { |
1064 |
|
|
tracker->TrackField("data", data_); |
1065 |
|
|
tracker->TrackField("emit_message_fn", emit_message_fn_); |
1066 |
|
|
} |
1067 |
|
|
|
1068 |
|
104 |
Local<FunctionTemplate> GetMessagePortConstructorTemplate(Environment* env) { |
1069 |
|
|
// Factor generating the MessagePort JS constructor into its own piece |
1070 |
|
|
// of code, because it is needed early on in the child environment setup. |
1071 |
|
104 |
Local<FunctionTemplate> templ = env->message_port_constructor_template(); |
1072 |
✓✓ |
104 |
if (!templ.IsEmpty()) |
1073 |
|
52 |
return templ; |
1074 |
|
|
|
1075 |
|
|
{ |
1076 |
|
52 |
Local<FunctionTemplate> m = env->NewFunctionTemplate(MessagePort::New); |
1077 |
|
104 |
m->SetClassName(env->message_port_constructor_string()); |
1078 |
|
156 |
m->InstanceTemplate()->SetInternalFieldCount( |
1079 |
|
52 |
MessagePort::kInternalFieldCount); |
1080 |
|
104 |
m->Inherit(HandleWrap::GetConstructorTemplate(env)); |
1081 |
|
|
|
1082 |
|
52 |
env->SetProtoMethod(m, "postMessage", MessagePort::PostMessage); |
1083 |
|
52 |
env->SetProtoMethod(m, "start", MessagePort::Start); |
1084 |
|
|
|
1085 |
|
52 |
env->set_message_port_constructor_template(m); |
1086 |
|
|
} |
1087 |
|
|
|
1088 |
|
52 |
return GetMessagePortConstructorTemplate(env); |
1089 |
|
|
} |
1090 |
|
|
|
1091 |
|
115 |
JSTransferable::JSTransferable(Environment* env, Local<Object> obj) |
1092 |
|
115 |
: BaseObject(env, obj) { |
1093 |
|
115 |
MakeWeak(); |
1094 |
|
115 |
} |
1095 |
|
|
|
1096 |
|
115 |
void JSTransferable::New(const FunctionCallbackInfo<Value>& args) { |
1097 |
✗✓ |
115 |
CHECK(args.IsConstructCall()); |
1098 |
|
230 |
new JSTransferable(Environment::GetCurrent(args), args.This()); |
1099 |
|
115 |
} |
1100 |
|
|
|
1101 |
|
|
JSTransferable::TransferMode JSTransferable::GetTransferMode() const { |
1102 |
|
|
// Implement `kClone in this ? kCloneable : kTransferable`. |
1103 |
|
|
HandleScope handle_scope(env()->isolate()); |
1104 |
|
|
errors::TryCatchScope ignore_exceptions(env()); |
1105 |
|
|
|
1106 |
|
|
bool has_clone; |
1107 |
|
|
if (!object()->Has(env()->context(), |
1108 |
|
|
env()->messaging_clone_symbol()).To(&has_clone)) { |
1109 |
|
|
return TransferMode::kUntransferable; |
1110 |
|
|
} |
1111 |
|
|
|
1112 |
|
|
return has_clone ? TransferMode::kCloneable : TransferMode::kTransferable; |
1113 |
|
|
} |
1114 |
|
|
|
1115 |
|
|
std::unique_ptr<TransferData> JSTransferable::TransferForMessaging() { |
1116 |
|
|
return TransferOrClone(TransferMode::kTransferable); |
1117 |
|
|
} |
1118 |
|
|
|
1119 |
|
|
std::unique_ptr<TransferData> JSTransferable::CloneForMessaging() const { |
1120 |
|
|
return TransferOrClone(TransferMode::kCloneable); |
1121 |
|
|
} |
1122 |
|
|
|
1123 |
|
|
std::unique_ptr<TransferData> JSTransferable::TransferOrClone( |
1124 |
|
|
TransferMode mode) const { |
1125 |
|
|
// Call `this[symbol]()` where `symbol` is `kClone` or `kTransfer`, |
1126 |
|
|
// which should return an object with `data` and `deserializeInfo` properties; |
1127 |
|
|
// `data` is written to the serializer later, and `deserializeInfo` is stored |
1128 |
|
|
// on the `TransferData` instance as a string. |
1129 |
|
|
HandleScope handle_scope(env()->isolate()); |
1130 |
|
|
Local<Context> context = env()->isolate()->GetCurrentContext(); |
1131 |
|
|
Local<Symbol> method_name = mode == TransferMode::kCloneable ? |
1132 |
|
|
env()->messaging_clone_symbol() : env()->messaging_transfer_symbol(); |
1133 |
|
|
|
1134 |
|
|
Local<Value> method; |
1135 |
|
|
if (!object()->Get(context, method_name).ToLocal(&method)) { |
1136 |
|
|
return {}; |
1137 |
|
|
} |
1138 |
|
|
if (method->IsFunction()) { |
1139 |
|
|
Local<Value> result_v; |
1140 |
|
|
if (!method.As<Function>()->Call( |
1141 |
|
|
context, object(), 0, nullptr).ToLocal(&result_v)) { |
1142 |
|
|
return {}; |
1143 |
|
|
} |
1144 |
|
|
|
1145 |
|
|
if (result_v->IsObject()) { |
1146 |
|
|
Local<Object> result = result_v.As<Object>(); |
1147 |
|
|
Local<Value> data; |
1148 |
|
|
Local<Value> deserialize_info; |
1149 |
|
|
if (!result->Get(context, env()->data_string()).ToLocal(&data) || |
1150 |
|
|
!result->Get(context, env()->deserialize_info_string()) |
1151 |
|
|
.ToLocal(&deserialize_info)) { |
1152 |
|
|
return {}; |
1153 |
|
|
} |
1154 |
|
|
Utf8Value deserialize_info_str(env()->isolate(), deserialize_info); |
1155 |
|
|
if (*deserialize_info_str == nullptr) return {}; |
1156 |
|
|
return std::make_unique<Data>( |
1157 |
|
|
*deserialize_info_str, Global<Value>(env()->isolate(), data)); |
1158 |
|
|
} |
1159 |
|
|
} |
1160 |
|
|
|
1161 |
|
|
if (mode == TransferMode::kTransferable) |
1162 |
|
|
return TransferOrClone(TransferMode::kCloneable); |
1163 |
|
|
else |
1164 |
|
|
return {}; |
1165 |
|
|
} |
1166 |
|
|
|
1167 |
|
|
Maybe<BaseObjectList> |
1168 |
|
|
JSTransferable::NestedTransferables() const { |
1169 |
|
|
// Call `this[kTransferList]()` and return the resulting list of BaseObjects. |
1170 |
|
|
HandleScope handle_scope(env()->isolate()); |
1171 |
|
|
Local<Context> context = env()->isolate()->GetCurrentContext(); |
1172 |
|
|
Local<Symbol> method_name = env()->messaging_transfer_list_symbol(); |
1173 |
|
|
|
1174 |
|
|
Local<Value> method; |
1175 |
|
|
if (!object()->Get(context, method_name).ToLocal(&method)) { |
1176 |
|
|
return Nothing<BaseObjectList>(); |
1177 |
|
|
} |
1178 |
|
|
if (!method->IsFunction()) return Just(BaseObjectList {}); |
1179 |
|
|
|
1180 |
|
|
Local<Value> list_v; |
1181 |
|
|
if (!method.As<Function>()->Call( |
1182 |
|
|
context, object(), 0, nullptr).ToLocal(&list_v)) { |
1183 |
|
|
return Nothing<BaseObjectList>(); |
1184 |
|
|
} |
1185 |
|
|
if (!list_v->IsArray()) return Just(BaseObjectList {}); |
1186 |
|
|
Local<Array> list = list_v.As<Array>(); |
1187 |
|
|
|
1188 |
|
|
BaseObjectList ret; |
1189 |
|
|
for (size_t i = 0; i < list->Length(); i++) { |
1190 |
|
|
Local<Value> value; |
1191 |
|
|
if (!list->Get(context, i).ToLocal(&value)) |
1192 |
|
|
return Nothing<BaseObjectList>(); |
1193 |
|
|
if (env()->base_object_ctor_template()->HasInstance(value)) |
1194 |
|
|
ret.emplace_back(Unwrap<BaseObject>(value)); |
1195 |
|
|
} |
1196 |
|
|
return Just(ret); |
1197 |
|
|
} |
1198 |
|
|
|
1199 |
|
|
Maybe<bool> JSTransferable::FinalizeTransferRead( |
1200 |
|
|
Local<Context> context, ValueDeserializer* deserializer) { |
1201 |
|
|
// Call `this[kDeserialize](data)` where `data` comes from the return value |
1202 |
|
|
// of `this[kTransfer]()` or `this[kClone]()`. |
1203 |
|
|
HandleScope handle_scope(env()->isolate()); |
1204 |
|
|
Local<Value> data; |
1205 |
|
|
if (!deserializer->ReadValue(context).ToLocal(&data)) return Nothing<bool>(); |
1206 |
|
|
|
1207 |
|
|
Local<Symbol> method_name = env()->messaging_deserialize_symbol(); |
1208 |
|
|
Local<Value> method; |
1209 |
|
|
if (!object()->Get(context, method_name).ToLocal(&method)) { |
1210 |
|
|
return Nothing<bool>(); |
1211 |
|
|
} |
1212 |
|
|
if (!method->IsFunction()) return Just(true); |
1213 |
|
|
|
1214 |
|
|
if (method.As<Function>()->Call(context, object(), 1, &data).IsEmpty()) { |
1215 |
|
|
return Nothing<bool>(); |
1216 |
|
|
} |
1217 |
|
|
return Just(true); |
1218 |
|
|
} |
1219 |
|
|
|
1220 |
|
|
JSTransferable::Data::Data(std::string&& deserialize_info, |
1221 |
|
|
v8::Global<v8::Value>&& data) |
1222 |
|
|
: deserialize_info_(std::move(deserialize_info)), |
1223 |
|
|
data_(std::move(data)) {} |
1224 |
|
|
|
1225 |
|
|
BaseObjectPtr<BaseObject> JSTransferable::Data::Deserialize( |
1226 |
|
|
Environment* env, |
1227 |
|
|
Local<Context> context, |
1228 |
|
|
std::unique_ptr<TransferData> self) { |
1229 |
|
|
// Create the JS wrapper object that will later be filled with data passed to |
1230 |
|
|
// the `[kDeserialize]()` method on it. This split is necessary, because here |
1231 |
|
|
// we need to create an object with the right prototype and internal fields, |
1232 |
|
|
// but the actual JS data stored in the serialized data can only be read at |
1233 |
|
|
// the end of the stream, after the main message has been read. |
1234 |
|
|
|
1235 |
|
|
if (context != env->context()) { |
1236 |
|
|
THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env); |
1237 |
|
|
return {}; |
1238 |
|
|
} |
1239 |
|
|
HandleScope handle_scope(env->isolate()); |
1240 |
|
|
Local<Value> info; |
1241 |
|
|
if (!ToV8Value(context, deserialize_info_).ToLocal(&info)) return {}; |
1242 |
|
|
|
1243 |
|
|
Local<Value> ret; |
1244 |
|
|
CHECK(!env->messaging_deserialize_create_object().IsEmpty()); |
1245 |
|
|
if (!env->messaging_deserialize_create_object()->Call( |
1246 |
|
|
context, Null(env->isolate()), 1, &info).ToLocal(&ret) || |
1247 |
|
|
!env->base_object_ctor_template()->HasInstance(ret)) { |
1248 |
|
|
return {}; |
1249 |
|
|
} |
1250 |
|
|
|
1251 |
|
|
return BaseObjectPtr<BaseObject> { Unwrap<BaseObject>(ret) }; |
1252 |
|
|
} |
1253 |
|
|
|
1254 |
|
|
Maybe<bool> JSTransferable::Data::FinalizeTransferWrite( |
1255 |
|
|
Local<Context> context, ValueSerializer* serializer) { |
1256 |
|
|
HandleScope handle_scope(context->GetIsolate()); |
1257 |
|
|
auto ret = serializer->WriteValue(context, PersistentToLocal::Strong(data_)); |
1258 |
|
|
data_.Reset(); |
1259 |
|
|
return ret; |
1260 |
|
|
} |
1261 |
|
|
|
1262 |
|
|
std::shared_ptr<SiblingGroup> SiblingGroup::Get(const std::string& name) { |
1263 |
|
|
Mutex::ScopedLock lock(SiblingGroup::groups_mutex_); |
1264 |
|
|
std::shared_ptr<SiblingGroup> group; |
1265 |
|
|
auto i = groups_.find(name); |
1266 |
|
|
if (i == groups_.end() || i->second.expired()) { |
1267 |
|
|
group = std::make_shared<SiblingGroup>(name); |
1268 |
|
|
groups_[name] = group; |
1269 |
|
|
} else { |
1270 |
|
|
group = i->second.lock(); |
1271 |
|
|
} |
1272 |
|
|
return group; |
1273 |
|
|
} |
1274 |
|
|
|
1275 |
|
|
void SiblingGroup::CheckSiblingGroup(const std::string& name) { |
1276 |
|
|
Mutex::ScopedLock lock(SiblingGroup::groups_mutex_); |
1277 |
|
|
auto i = groups_.find(name); |
1278 |
|
|
if (i != groups_.end() && i->second.expired()) |
1279 |
|
|
groups_.erase(name); |
1280 |
|
|
} |
1281 |
|
|
|
1282 |
|
|
SiblingGroup::SiblingGroup(const std::string& name) |
1283 |
|
|
: name_(name) { } |
1284 |
|
|
|
1285 |
|
|
SiblingGroup::~SiblingGroup() { |
1286 |
|
|
// If this is a named group, check to see if we can remove the group |
1287 |
|
|
if (!name_.empty()) |
1288 |
|
|
CheckSiblingGroup(name_); |
1289 |
|
|
} |
1290 |
|
|
|
1291 |
|
|
Maybe<bool> SiblingGroup::Dispatch( |
1292 |
|
|
MessagePortData* source, |
1293 |
|
|
std::shared_ptr<Message> message, |
1294 |
|
|
std::string* error) { |
1295 |
|
|
|
1296 |
|
|
Mutex::ScopedLock lock(group_mutex_); |
1297 |
|
|
|
1298 |
|
|
// The source MessagePortData is not part of this group. |
1299 |
|
|
if (ports_.find(source) == ports_.end()) { |
1300 |
|
|
if (error != nullptr) |
1301 |
|
|
*error = "Source MessagePort is not entangled with this group."; |
1302 |
|
|
return Nothing<bool>(); |
1303 |
|
|
} |
1304 |
|
|
|
1305 |
|
|
// There are no destination ports. |
1306 |
|
|
if (size() <= 1) |
1307 |
|
|
return Just(false); |
1308 |
|
|
|
1309 |
|
|
// Transferables cannot be used when there is more |
1310 |
|
|
// than a single destination. |
1311 |
|
|
if (size() > 2 && message->has_transferables()) { |
1312 |
|
|
if (error != nullptr) |
1313 |
|
|
*error = "Transferables cannot be used with multiple destinations."; |
1314 |
|
|
return Nothing<bool>(); |
1315 |
|
|
} |
1316 |
|
|
|
1317 |
|
|
for (MessagePortData* port : ports_) { |
1318 |
|
|
if (port == source) |
1319 |
|
|
continue; |
1320 |
|
|
// This loop should only be entered if there's only a single destination |
1321 |
|
|
for (const auto& transferable : message->transferables()) { |
1322 |
|
|
if (port == transferable.get()) { |
1323 |
|
|
if (error != nullptr) { |
1324 |
|
|
*error = "The target port was posted to itself, and the " |
1325 |
|
|
"communication channel was lost"; |
1326 |
|
|
} |
1327 |
|
|
return Just(true); |
1328 |
|
|
} |
1329 |
|
|
} |
1330 |
|
|
port->AddToIncomingQueue(message); |
1331 |
|
|
} |
1332 |
|
|
|
1333 |
|
|
return Just(true); |
1334 |
|
|
} |
1335 |
|
|
|
1336 |
|
|
void SiblingGroup::Entangle(MessagePortData* port) { |
1337 |
|
|
Entangle({ port }); |
1338 |
|
|
} |
1339 |
|
|
|
1340 |
|
|
void SiblingGroup::Entangle(std::initializer_list<MessagePortData*> ports) { |
1341 |
|
|
Mutex::ScopedLock lock(group_mutex_); |
1342 |
|
|
for (MessagePortData* data : ports) { |
1343 |
|
|
ports_.insert(data); |
1344 |
|
|
CHECK(!data->group_); |
1345 |
|
|
data->group_ = shared_from_this(); |
1346 |
|
|
} |
1347 |
|
|
} |
1348 |
|
|
|
1349 |
|
|
void SiblingGroup::Disentangle(MessagePortData* data) { |
1350 |
|
|
auto self = shared_from_this(); // Keep alive until end of function. |
1351 |
|
|
Mutex::ScopedLock lock(group_mutex_); |
1352 |
|
|
ports_.erase(data); |
1353 |
|
|
data->group_.reset(); |
1354 |
|
|
|
1355 |
|
|
data->AddToIncomingQueue(std::make_shared<Message>()); |
1356 |
|
|
// If this is an anonymous group and there's another port, close it. |
1357 |
|
|
if (size() == 1 && name_.empty()) |
1358 |
|
|
(*(ports_.begin()))->AddToIncomingQueue(std::make_shared<Message>()); |
1359 |
|
|
} |
1360 |
|
|
|
1361 |
|
122 |
SiblingGroup::Map SiblingGroup::groups_; |
1362 |
|
122 |
Mutex SiblingGroup::groups_mutex_; |
1363 |
|
|
|
1364 |
|
|
namespace { |
1365 |
|
|
|
1366 |
|
52 |
static void SetDeserializerCreateObjectFunction( |
1367 |
|
|
const FunctionCallbackInfo<Value>& args) { |
1368 |
|
52 |
Environment* env = Environment::GetCurrent(args); |
1369 |
✗✓ |
104 |
CHECK(args[0]->IsFunction()); |
1370 |
|
104 |
env->set_messaging_deserialize_create_object(args[0].As<Function>()); |
1371 |
|
52 |
} |
1372 |
|
|
|
1373 |
|
|
static void MessageChannel(const FunctionCallbackInfo<Value>& args) { |
1374 |
|
|
Environment* env = Environment::GetCurrent(args); |
1375 |
|
|
if (!args.IsConstructCall()) { |
1376 |
|
|
THROW_ERR_CONSTRUCT_CALL_REQUIRED(env); |
1377 |
|
|
return; |
1378 |
|
|
} |
1379 |
|
|
|
1380 |
|
|
Local<Context> context = args.This()->CreationContext(); |
1381 |
|
|
Context::Scope context_scope(context); |
1382 |
|
|
|
1383 |
|
|
MessagePort* port1 = MessagePort::New(env, context); |
1384 |
|
|
if (port1 == nullptr) return; |
1385 |
|
|
MessagePort* port2 = MessagePort::New(env, context); |
1386 |
|
|
if (port2 == nullptr) { |
1387 |
|
|
port1->Close(); |
1388 |
|
|
return; |
1389 |
|
|
} |
1390 |
|
|
|
1391 |
|
|
MessagePort::Entangle(port1, port2); |
1392 |
|
|
|
1393 |
|
|
args.This()->Set(context, env->port1_string(), port1->object()) |
1394 |
|
|
.Check(); |
1395 |
|
|
args.This()->Set(context, env->port2_string(), port2->object()) |
1396 |
|
|
.Check(); |
1397 |
|
|
} |
1398 |
|
|
|
1399 |
|
|
static void BroadcastChannel(const FunctionCallbackInfo<Value>& args) { |
1400 |
|
|
CHECK(args[0]->IsString()); |
1401 |
|
|
Environment* env = Environment::GetCurrent(args); |
1402 |
|
|
Context::Scope context_scope(env->context()); |
1403 |
|
|
Utf8Value name(env->isolate(), args[0]); |
1404 |
|
|
MessagePort* port = |
1405 |
|
|
MessagePort::New(env, env->context(), {}, SiblingGroup::Get(*name)); |
1406 |
|
|
if (port != nullptr) { |
1407 |
|
|
args.GetReturnValue().Set(port->object()); |
1408 |
|
|
} |
1409 |
|
|
} |
1410 |
|
|
|
1411 |
|
52 |
static void InitMessaging(Local<Object> target, |
1412 |
|
|
Local<Value> unused, |
1413 |
|
|
Local<Context> context, |
1414 |
|
|
void* priv) { |
1415 |
|
52 |
Environment* env = Environment::GetCurrent(context); |
1416 |
|
|
|
1417 |
|
|
{ |
1418 |
|
104 |
env->SetConstructorFunction( |
1419 |
|
|
target, |
1420 |
|
|
"MessageChannel", |
1421 |
|
52 |
env->NewFunctionTemplate(MessageChannel)); |
1422 |
|
|
} |
1423 |
|
|
|
1424 |
|
|
{ |
1425 |
|
52 |
Local<FunctionTemplate> t = env->NewFunctionTemplate(JSTransferable::New); |
1426 |
|
104 |
t->Inherit(BaseObject::GetConstructorTemplate(env)); |
1427 |
|
156 |
t->InstanceTemplate()->SetInternalFieldCount( |
1428 |
|
52 |
JSTransferable::kInternalFieldCount); |
1429 |
|
52 |
env->SetConstructorFunction(target, "JSTransferable", t); |
1430 |
|
|
} |
1431 |
|
|
|
1432 |
|
52 |
env->SetConstructorFunction( |
1433 |
|
|
target, |
1434 |
|
|
env->message_port_constructor_string(), |
1435 |
|
52 |
GetMessagePortConstructorTemplate(env)); |
1436 |
|
|
|
1437 |
|
|
// These are not methods on the MessagePort prototype, because |
1438 |
|
|
// the browser equivalents do not provide them. |
1439 |
|
52 |
env->SetMethod(target, "stopMessagePort", MessagePort::Stop); |
1440 |
|
52 |
env->SetMethod(target, "checkMessagePort", MessagePort::CheckType); |
1441 |
|
52 |
env->SetMethod(target, "drainMessagePort", MessagePort::Drain); |
1442 |
|
52 |
env->SetMethod(target, "receiveMessageOnPort", MessagePort::ReceiveMessage); |
1443 |
|
|
env->SetMethod(target, "moveMessagePortToContext", |
1444 |
|
52 |
MessagePort::MoveToContext); |
1445 |
|
|
env->SetMethod(target, "setDeserializerCreateObjectFunction", |
1446 |
|
52 |
SetDeserializerCreateObjectFunction); |
1447 |
|
52 |
env->SetMethod(target, "broadcastChannel", BroadcastChannel); |
1448 |
|
|
|
1449 |
|
|
{ |
1450 |
|
104 |
Local<Function> domexception = GetDOMException(context).ToLocalChecked(); |
1451 |
|
|
target |
1452 |
|
104 |
->Set(context, |
1453 |
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "DOMException"), |
1454 |
|
156 |
domexception) |
1455 |
|
|
.Check(); |
1456 |
|
|
} |
1457 |
|
52 |
} |
1458 |
|
|
|
1459 |
|
107 |
static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
1460 |
|
107 |
registry->Register(MessageChannel); |
1461 |
|
107 |
registry->Register(BroadcastChannel); |
1462 |
|
107 |
registry->Register(JSTransferable::New); |
1463 |
|
107 |
registry->Register(MessagePort::New); |
1464 |
|
107 |
registry->Register(MessagePort::PostMessage); |
1465 |
|
107 |
registry->Register(MessagePort::Start); |
1466 |
|
107 |
registry->Register(MessagePort::Stop); |
1467 |
|
107 |
registry->Register(MessagePort::CheckType); |
1468 |
|
107 |
registry->Register(MessagePort::Drain); |
1469 |
|
107 |
registry->Register(MessagePort::ReceiveMessage); |
1470 |
|
107 |
registry->Register(MessagePort::MoveToContext); |
1471 |
|
107 |
registry->Register(SetDeserializerCreateObjectFunction); |
1472 |
|
107 |
} |
1473 |
|
|
|
1474 |
|
|
} // anonymous namespace |
1475 |
|
|
|
1476 |
|
|
} // namespace worker |
1477 |
|
|
} // namespace node |
1478 |
|
|
|
1479 |
|
115 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(messaging, node::worker::InitMessaging) |
1480 |
✓✗✓✗
|
473 |
NODE_MODULE_EXTERNAL_REFERENCE(messaging, |
1481 |
|
|
node::worker::RegisterExternalReferences) |