@@ -31,6 +31,7 @@ using v8::Object;
31
31
using v8::ObjectTemplate;
32
32
using v8::SharedArrayBuffer;
33
33
using v8::String;
34
+ using v8::Symbol;
34
35
using v8::Value;
35
36
using v8::ValueDeserializer;
36
37
using v8::ValueSerializer;
@@ -304,7 +305,7 @@ class SerializerDelegate : public ValueSerializer::Delegate {
304
305
Maybe<bool > Message::Serialize (Environment* env,
305
306
Local<Context> context,
306
307
Local<Value> input,
307
- Local<Value> transfer_list_v,
308
+ const TransferList& transfer_list_v,
308
309
Local<Object> source_port) {
309
310
HandleScope handle_scope (env->isolate ());
310
311
Context::Scope context_scope (context);
@@ -317,72 +318,66 @@ Maybe<bool> Message::Serialize(Environment* env,
317
318
delegate.serializer = &serializer;
318
319
319
320
std::vector<Local<ArrayBuffer>> array_buffers;
320
- if (transfer_list_v->IsArray ()) {
321
- Local<Array> transfer_list = transfer_list_v.As <Array>();
322
- uint32_t length = transfer_list->Length ();
323
- for (uint32_t i = 0 ; i < length; ++i) {
324
- Local<Value> entry;
325
- if (!transfer_list->Get (context, i).ToLocal (&entry))
326
- return Nothing<bool >();
327
- // Currently, we support ArrayBuffers and MessagePorts.
328
- if (entry->IsArrayBuffer ()) {
329
- Local<ArrayBuffer> ab = entry.As <ArrayBuffer>();
330
- // If we cannot render the ArrayBuffer unusable in this Isolate and
331
- // take ownership of its memory, copying the buffer will have to do.
332
- if (!ab->IsDetachable () || ab->IsExternal () ||
333
- !env->isolate_data ()->uses_node_allocator ()) {
334
- continue ;
335
- }
336
- if (std::find (array_buffers.begin (), array_buffers.end (), ab) !=
337
- array_buffers.end ()) {
338
- ThrowDataCloneException (
339
- context,
340
- FIXED_ONE_BYTE_STRING (
341
- env->isolate (),
342
- " Transfer list contains duplicate ArrayBuffer" ));
343
- return Nothing<bool >();
344
- }
345
- // We simply use the array index in the `array_buffers` list as the
346
- // ID that we write into the serialized buffer.
347
- uint32_t id = array_buffers.size ();
348
- array_buffers.push_back (ab);
349
- serializer.TransferArrayBuffer (id, ab);
350
- continue ;
351
- } else if (env->message_port_constructor_template ()
352
- ->HasInstance (entry)) {
353
- // Check if the source MessagePort is being transferred.
354
- if (!source_port.IsEmpty () && entry == source_port) {
355
- ThrowDataCloneException (
356
- context,
357
- FIXED_ONE_BYTE_STRING (env->isolate (),
358
- " Transfer list contains source port" ));
359
- return Nothing<bool >();
360
- }
361
- MessagePort* port = Unwrap<MessagePort>(entry.As <Object>());
362
- if (port == nullptr || port->IsDetached ()) {
363
- ThrowDataCloneException (
364
- context,
365
- FIXED_ONE_BYTE_STRING (
366
- env->isolate (),
367
- " MessagePort in transfer list is already detached" ));
368
- return Nothing<bool >();
369
- }
370
- if (std::find (delegate.ports_ .begin (), delegate.ports_ .end (), port) !=
371
- delegate.ports_ .end ()) {
372
- ThrowDataCloneException (
373
- context,
374
- FIXED_ONE_BYTE_STRING (
375
- env->isolate (),
376
- " Transfer list contains duplicate MessagePort" ));
377
- return Nothing<bool >();
378
- }
379
- delegate.ports_ .push_back (port);
321
+ for (uint32_t i = 0 ; i < transfer_list_v.length (); ++i) {
322
+ Local<Value> entry = transfer_list_v[i];
323
+ // Currently, we support ArrayBuffers and MessagePorts.
324
+ if (entry->IsArrayBuffer ()) {
325
+ Local<ArrayBuffer> ab = entry.As <ArrayBuffer>();
326
+ // If we cannot render the ArrayBuffer unusable in this Isolate and
327
+ // take ownership of its memory, copying the buffer will have to do.
328
+ if (!ab->IsDetachable () || ab->IsExternal () ||
329
+ !env->isolate_data ()->uses_node_allocator ()) {
380
330
continue ;
381
331
}
382
-
383
- THROW_ERR_INVALID_TRANSFER_OBJECT (env);
384
- return Nothing<bool >();
332
+ if (std::find (array_buffers.begin (), array_buffers.end (), ab) !=
333
+ array_buffers.end ()) {
334
+ ThrowDataCloneException (
335
+ context,
336
+ FIXED_ONE_BYTE_STRING (
337
+ env->isolate (),
338
+ " Transfer list contains duplicate ArrayBuffer" ));
339
+ return Nothing<bool >();
340
+ }
341
+ // We simply use the array index in the `array_buffers` list as the
342
+ // ID that we write into the serialized buffer.
343
+ uint32_t id = array_buffers.size ();
344
+ array_buffers.push_back (ab);
345
+ serializer.TransferArrayBuffer (id, ab);
346
+ continue ;
347
+ } else if (env->message_port_constructor_template ()
348
+ ->HasInstance (entry)) {
349
+ // Check if the source MessagePort is being transferred.
350
+ if (!source_port.IsEmpty () && entry == source_port) {
351
+ ThrowDataCloneException (
352
+ context,
353
+ FIXED_ONE_BYTE_STRING (env->isolate (),
354
+ " Transfer list contains source port" ));
355
+ return Nothing<bool >();
356
+ }
357
+ MessagePort* port = Unwrap<MessagePort>(entry.As <Object>());
358
+ if (port == nullptr || port->IsDetached ()) {
359
+ ThrowDataCloneException (
360
+ context,
361
+ FIXED_ONE_BYTE_STRING (
362
+ env->isolate (),
363
+ " MessagePort in transfer list is already detached" ));
364
+ return Nothing<bool >();
365
+ }
366
+ if (std::find (delegate.ports_ .begin (), delegate.ports_ .end (), port) !=
367
+ delegate.ports_ .end ()) {
368
+ ThrowDataCloneException (
369
+ context,
370
+ FIXED_ONE_BYTE_STRING (
371
+ env->isolate (),
372
+ " Transfer list contains duplicate MessagePort" ));
373
+ return Nothing<bool >();
374
+ }
375
+ delegate.ports_ .push_back (port);
376
+ continue ;
385
377
}
378
+
379
+ THROW_ERR_INVALID_TRANSFER_OBJECT (env);
380
+ return Nothing<bool >();
386
381
}
387
382
388
383
serializer.WriteHeader ();
@@ -664,7 +659,7 @@ std::unique_ptr<MessagePortData> MessagePort::Detach() {
664
659
665
660
Maybe<bool > MessagePort::PostMessage (Environment* env,
666
661
Local<Value> message_v,
667
- Local<Value> transfer_v) {
662
+ const TransferList& transfer_v) {
668
663
Isolate* isolate = env->isolate ();
669
664
Local<Object> obj = object (isolate);
670
665
Local<Context> context = obj->CreationContext ();
@@ -705,20 +700,98 @@ Maybe<bool> MessagePort::PostMessage(Environment* env,
705
700
return Just (true );
706
701
}
707
702
703
+ static Maybe<bool > ReadIterable (Environment* env,
704
+ Local<Context> context,
705
+ // NOLINTNEXTLINE(runtime/references)
706
+ TransferList& transfer_list,
707
+ Local<Value> object) {
708
+ if (!object->IsObject ()) return Just (false );
709
+
710
+ if (object->IsArray ()) {
711
+ Local<Array> arr = object.As <Array>();
712
+ size_t length = arr->Length ();
713
+ transfer_list.AllocateSufficientStorage (length);
714
+ for (size_t i = 0 ; i < length; i++) {
715
+ if (!arr->Get (context, i).ToLocal (&transfer_list[i]))
716
+ return Nothing<bool >();
717
+ }
718
+ return Just (true );
719
+ }
720
+
721
+ Isolate* isolate = env->isolate ();
722
+ Local<Value> iterator_method;
723
+ if (!object.As <Object>()->Get (context, Symbol::GetIterator (isolate))
724
+ .ToLocal (&iterator_method)) return Nothing<bool >();
725
+ if (!iterator_method->IsFunction ()) return Just (false );
726
+
727
+ Local<Value> iterator;
728
+ if (!iterator_method.As <Function>()->Call (context, object, 0 , nullptr )
729
+ .ToLocal (&iterator)) return Nothing<bool >();
730
+ if (!iterator->IsObject ()) return Just (false );
731
+
732
+ Local<Value> next;
733
+ if (!iterator.As <Object>()->Get (context, env->next_string ()).ToLocal (&next))
734
+ return Nothing<bool >();
735
+ if (!next->IsFunction ()) return Just (false );
736
+
737
+ std::vector<Local<Value>> entries;
738
+ while (env->can_call_into_js ()) {
739
+ Local<Value> result;
740
+ if (!next.As <Function>()->Call (context, iterator, 0 , nullptr )
741
+ .ToLocal (&result)) return Nothing<bool >();
742
+ if (!result->IsObject ()) return Just (false );
743
+
744
+ Local<Value> done;
745
+ if (!result.As <Object>()->Get (context, env->done_string ()).ToLocal (&done))
746
+ return Nothing<bool >();
747
+ if (done->BooleanValue (isolate)) break ;
748
+
749
+ Local<Value> val;
750
+ if (!result.As <Object>()->Get (context, env->value_string ()).ToLocal (&val))
751
+ return Nothing<bool >();
752
+ entries.push_back (val);
753
+ }
754
+
755
+ transfer_list.AllocateSufficientStorage (entries.size ());
756
+ std::copy (entries.begin (), entries.end (), &transfer_list[0 ]);
757
+ return Just (true );
758
+ }
759
+
708
760
void MessagePort::PostMessage (const FunctionCallbackInfo<Value>& args) {
709
761
Environment* env = Environment::GetCurrent (args);
762
+ Local<Object> obj = args.This ();
763
+ Local<Context> context = obj->CreationContext ();
764
+
710
765
if (args.Length () == 0 ) {
711
766
return THROW_ERR_MISSING_ARGS (env, " Not enough arguments to "
712
767
" MessagePort.postMessage" );
713
768
}
769
+
714
770
if (!args[1 ]->IsNullOrUndefined () && !args[1 ]->IsObject ()) {
715
771
// Browsers ignore null or undefined, and otherwise accept an array or an
716
772
// options object.
717
- // TODO(addaleax): Add support for an options object and generic sequence
718
- // support.
719
- // Refs: https://github.com/nodejs/node/pull/28033#discussion_r289964991
720
773
return THROW_ERR_INVALID_ARG_TYPE (env,
721
- " Optional transferList argument must be an array" );
774
+ " Optional transferList argument must be an iterable" );
775
+ }
776
+
777
+ TransferList transfer_list;
778
+ if (args[1 ]->IsObject ()) {
779
+ bool was_iterable;
780
+ if (!ReadIterable (env, context, transfer_list, args[1 ]).To (&was_iterable))
781
+ return ;
782
+ if (!was_iterable) {
783
+ Local<Value> transfer_option;
784
+ if (!args[1 ].As <Object>()->Get (context, env->transfer_string ())
785
+ .ToLocal (&transfer_option)) return ;
786
+ if (!transfer_option->IsUndefined ()) {
787
+ if (!ReadIterable (env, context, transfer_list, transfer_option)
788
+ .To (&was_iterable)) return ;
789
+ if (!was_iterable) {
790
+ return THROW_ERR_INVALID_ARG_TYPE (env,
791
+ " Optional options.transfer argument must be an iterable" );
792
+ }
793
+ }
794
+ }
722
795
}
723
796
724
797
MessagePort* port = Unwrap<MessagePort>(args.This ());
@@ -727,13 +800,11 @@ void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
727
800
// transfers.
728
801
if (port == nullptr ) {
729
802
Message msg;
730
- Local<Object> obj = args.This ();
731
- Local<Context> context = obj->CreationContext ();
732
- USE (msg.Serialize (env, context, args[0 ], args[1 ], obj));
803
+ USE (msg.Serialize (env, context, args[0 ], transfer_list, obj));
733
804
return ;
734
805
}
735
806
736
- port->PostMessage (env, args[0 ], args[ 1 ] );
807
+ port->PostMessage (env, args[0 ], transfer_list );
737
808
}
738
809
739
810
void MessagePort::Start () {
0 commit comments