From 7e54b9adf9b6b32a8c047d1ea05cbc4e94e13fa2 Mon Sep 17 00:00:00 2001 From: Santusht kotai <115890693+santusht06@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:41:00 +0530 Subject: [PATCH] worker: define Symbol.toStringTag on messaging prototypes Ensure compliance with the HTML and WebIDL specifications by defining Symbol.toStringTag on MessageChannel.prototype, MessagePort.prototype, and BroadcastChannel.prototype. Fixes: https://github.com/nodejs/node/issues/65527 Signed-off-by: Santusht kotai <115890693+santusht06@users.noreply.github.com> --- lib/internal/worker/io.js | 19 ++++++++++ .../test-eventtarget-memoryleakwarning.js | 4 +- ...r-message-port-inspect-during-init-hook.js | 2 +- .../test-worker-messaging-string-tag.js | 38 +++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-worker-messaging-string-tag.js diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index 011c00b0149c..dab6196026ef 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -15,6 +15,7 @@ const { ReflectApply, Symbol, SymbolFor, + SymbolToStringTag, } = primordials; const { @@ -208,6 +209,19 @@ ObjectDefineProperty(MessagePort.prototype, inspect.custom, { }, }); +ObjectDefineProperty(MessagePort.prototype, SymbolToStringTag, { + __proto__: null, + configurable: true, + value: 'MessagePort', +}); + +ObjectDefineProperty(MessageChannel.prototype, SymbolToStringTag, { + __proto__: null, + configurable: true, + value: 'MessageChannel', +}); + + function setupPortReferencing(port, eventEmitter, eventName) { // Keep track of whether there are any workerMessage listeners: // If there are some, ref() the channel so it keeps the event loop alive. @@ -464,6 +478,11 @@ ObjectDefineProperties(BroadcastChannel.prototype, { name: kEnumerableProperty, close: kEnumerableProperty, postMessage: kEnumerableProperty, + [SymbolToStringTag]: { + __proto__: null, + configurable: true, + value: 'BroadcastChannel', + }, }); defineEventHandler(BroadcastChannel.prototype, 'message'); diff --git a/test/parallel/test-eventtarget-memoryleakwarning.js b/test/parallel/test-eventtarget-memoryleakwarning.js index 38fc5efc9bd2..c8c1faa2175b 100644 --- a/test/parallel/test-eventtarget-memoryleakwarning.js +++ b/test/parallel/test-eventtarget-memoryleakwarning.js @@ -15,12 +15,12 @@ common.expectWarning({ 'EventTarget. MaxListeners is 2. Use events.setMaxListeners() ' + 'to increase limit'], ['Possible EventTarget memory leak detected. 3 foo listeners added to ' + - '[MessagePort [EventTarget]]. ' + + '[MessagePort]. ' + 'MaxListeners is 2. ' + 'Use events.setMaxListeners() to increase ' + 'limit'], ['Possible EventTarget memory leak detected. 3 foo listeners added to ' + - '[MessagePort [EventTarget]]. ' + + '[MessagePort]. ' + 'MaxListeners is 2. ' + 'Use events.setMaxListeners() to increase ' + 'limit'], diff --git a/test/parallel/test-worker-message-port-inspect-during-init-hook.js b/test/parallel/test-worker-message-port-inspect-during-init-hook.js index 8f9678de1e97..d5784dceb71d 100644 --- a/test/parallel/test-worker-message-port-inspect-during-init-hook.js +++ b/test/parallel/test-worker-message-port-inspect-during-init-hook.js @@ -12,7 +12,7 @@ async_hooks.createHook({ init: common.mustCall((id, type, triggerId, resource) => { assert.strictEqual( util.inspect(resource), - 'MessagePort [EventTarget] { active: true, refed: false }'); + 'MessagePort { active: true, refed: false }'); }, 2) }).enable(); diff --git a/test/parallel/test-worker-messaging-string-tag.js b/test/parallel/test-worker-messaging-string-tag.js new file mode 100644 index 000000000000..19f67d9a38cb --- /dev/null +++ b/test/parallel/test-worker-messaging-string-tag.js @@ -0,0 +1,38 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { MessageChannel, MessagePort, BroadcastChannel } = require('worker_threads'); + +const classesToBeTested = [ + MessageChannel, + MessagePort, + BroadcastChannel, +]; + +for (const cls of classesToBeTested) { + assert.strictEqual(cls.prototype[Symbol.toStringTag], cls.name); + assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(cls.prototype, Symbol.toStringTag), + { configurable: true, enumerable: false, value: cls.name, writable: false } + ); +} + +const channel = new MessageChannel(); +assert.strictEqual(Object.prototype.toString.call(channel), '[object MessageChannel]'); +assert.strictEqual(Object.prototype.toString.call(channel.port1), '[object MessagePort]'); +assert.strictEqual(Object.prototype.toString.call(channel.port2), '[object MessagePort]'); + +const broadcast = new BroadcastChannel('test'); +assert.strictEqual(Object.prototype.toString.call(broadcast), '[object BroadcastChannel]'); +broadcast.close(); + +// Test globals +assert.strictEqual(globalThis.MessageChannel, MessageChannel); +assert.strictEqual(globalThis.MessagePort, MessagePort); +assert.strictEqual(globalThis.BroadcastChannel, BroadcastChannel); +assert.strictEqual(Object.prototype.toString.call(new globalThis.MessageChannel()), '[object MessageChannel]'); +assert.strictEqual(Object.prototype.toString.call(new globalThis.MessageChannel().port1), '[object MessagePort]'); +const globalBroadcast = new globalThis.BroadcastChannel('test-global'); +assert.strictEqual(Object.prototype.toString.call(globalBroadcast), '[object BroadcastChannel]'); +globalBroadcast.close();