@@ -144,7 +144,7 @@ void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
144144
145145namespace  {
146146
147- void  ThrowDataCloneError (Environment* env, Local<String> message) {
147+ void  ThrowDataCloneException (Environment* env, Local<String> message) {
148148  Local<Value> argv[] = {
149149    message,
150150    FIXED_ONE_BYTE_STRING (env->isolate (), " DataCloneError" 
@@ -168,7 +168,7 @@ class SerializerDelegate : public ValueSerializer::Delegate {
168168      : env_(env), context_(context), msg_(m) {}
169169
170170  void  ThrowDataCloneError (Local<String> message) override  {
171-     ThrowDataCloneError (env_, message);
171+     ThrowDataCloneException (env_, message);
172172  }
173173
174174  Maybe<bool > WriteHostObject (Isolate* isolate, Local<Object> object) override  {
@@ -239,7 +239,8 @@ class SerializerDelegate : public ValueSerializer::Delegate {
239239Maybe<bool > Message::Serialize (Environment* env,
240240                               Local<Context> context,
241241                               Local<Value> input,
242-                                Local<Value> transfer_list_v) {
242+                                Local<Value> transfer_list_v,
243+                                Local<Object> source_port) {
243244  HandleScope handle_scope (env->isolate ());
244245  Context::Scope context_scope (context);
245246
@@ -273,8 +274,23 @@ Maybe<bool> Message::Serialize(Environment* env,
273274        continue ;
274275      } else  if  (env->message_port_constructor_template ()
275276                    ->HasInstance (entry)) {
277+         //  Check if the source MessagePort is being transferred.
278+         if  (!source_port.IsEmpty () && entry == source_port) {
279+           ThrowDataCloneException (
280+               env,
281+               FIXED_ONE_BYTE_STRING (env->isolate (),
282+                                     " Transfer list contains source port" 
283+           return  Nothing<bool >();
284+         }
276285        MessagePort* port = Unwrap<MessagePort>(entry.As <Object>());
277-         CHECK_NE (port, nullptr );
286+         if  (port == nullptr  || port->IsDetached ()) {
287+           ThrowDataCloneException (
288+               env,
289+               FIXED_ONE_BYTE_STRING (
290+                   env->isolate (),
291+                   " MessagePort in transfer list is already detached" 
292+           return  Nothing<bool >();
293+         }
278294        delegate.ports_ .push_back (port);
279295        continue ;
280296      }
@@ -410,6 +426,10 @@ uv_async_t* MessagePort::async() {
410426  return  reinterpret_cast <uv_async_t *>(GetHandle ());
411427}
412428
429+ bool  MessagePort::IsDetached () const  {
430+   return  data_ == nullptr  || IsHandleClosing ();
431+ }
432+ 
413433void  MessagePort::TriggerAsync () {
414434  if  (IsHandleClosing ()) return ;
415435  CHECK_EQ (uv_async_send (async ()), 0 );
@@ -552,36 +572,69 @@ std::unique_ptr<MessagePortData> MessagePort::Detach() {
552572}
553573
554574
555- void  MessagePort::Send  (Message&& message) { 
556-   Mutex::ScopedLock  lock (*data_-> sibling_mutex_ ); 
557-   if  (data_-> sibling_  ==  nullptr ) 
558-      return ;
559-   data_-> sibling_ -> AddToIncomingQueue ( std::move (message) );
560- } 
575+ Maybe< bool >  MessagePort::PostMessage  (Environment* env, 
576+                                      Local<Value> message_v, 
577+                                      Local<Value> transfer_v) { 
578+   Isolate* isolate = env-> isolate () ;
579+   Local<Object> obj =  object (isolate );
580+   Local<Context> context = obj-> CreationContext (); 
561581
562- void  MessagePort::Send (const  FunctionCallbackInfo<Value>& args) {
563-   Environment* env = Environment::GetCurrent (args);
564-   Local<Context> context = object (env->isolate ())->CreationContext ();
565582  Message msg;
566-   if  (msg.Serialize (env, context, args[0 ], args[1 ])
567-           .IsNothing ()) {
568-     return ;
583+ 
584+   //  Per spec, we need to both check if transfer list has the source port, and
585+   //  serialize the input message, even if the MessagePort is closed or detached.
586+ 
587+   Maybe<bool > serialization_maybe =
588+       msg.Serialize (env, context, message_v, transfer_v, obj);
589+   if  (data_ == nullptr ) {
590+     return  serialization_maybe;
591+   }
592+   if  (serialization_maybe.IsNothing ()) {
593+     return  Nothing<bool >();
594+   }
595+ 
596+   Mutex::ScopedLock lock (*data_->sibling_mutex_ );
597+   bool  doomed = false ;
598+ 
599+   //  Check if the target port is posted to itself.
600+   if  (data_->sibling_  != nullptr ) {
601+     for  (const  auto & port_data : msg.message_ports ()) {
602+       if  (data_->sibling_  == port_data.get ()) {
603+         doomed = true ;
604+         ProcessEmitWarning (env, " The target port was posted to itself, and " 
605+                                 " the communication channel was lost" 
606+         break ;
607+       }
608+     }
569609  }
570-   Send (std::move (msg));
610+ 
611+   if  (data_->sibling_  == nullptr  || doomed)
612+     return  Just (true );
613+ 
614+   data_->sibling_ ->AddToIncomingQueue (std::move (msg));
615+   return  Just (true );
571616}
572617
573618void  MessagePort::PostMessage (const  FunctionCallbackInfo<Value>& args) {
574619  Environment* env = Environment::GetCurrent (args);
575-   MessagePort* port;
576-   ASSIGN_OR_RETURN_UNWRAP (&port, args.This ());
577-   if  (!port->data_ ) {
578-     return  THROW_ERR_CLOSED_MESSAGE_PORT (env);
579-   }
580620  if  (args.Length () == 0 ) {
581621    return  THROW_ERR_MISSING_ARGS (env, " Not enough arguments to " 
582622                                       " MessagePort.postMessage" 
583623  }
584-   port->Send (args);
624+ 
625+   MessagePort* port = Unwrap<MessagePort>(args.This ());
626+   //  Even if the backing MessagePort object has already been deleted, we still
627+   //  want to serialize the message to ensure spec-compliant behavior w.r.t.
628+   //  transfers.
629+   if  (port == nullptr ) {
630+     Message msg;
631+     Local<Object> obj = args.This ();
632+     Local<Context> context = obj->CreationContext ();
633+     USE (msg.Serialize (env, context, args[0 ], args[1 ], obj));
634+     return ;
635+   }
636+ 
637+   port->PostMessage (env, args[0 ], args[1 ]);
585638}
586639
587640void  MessagePort::Start () {
0 commit comments