@@ -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