From 381f32c50e2826760fd399be40e06352f1b8f36d Mon Sep 17 00:00:00 2001 From: Khafra Date: Fri, 26 Apr 2024 14:23:55 -0400 Subject: [PATCH] create fast MessageEvent (#3170) * create fast MessageEvent * expose * use * fixup --- benchmarks/websocket/messageevent.mjs | 20 ++++++++++++++++++++ index-fetch.js | 3 ++- lib/web/eventsource/eventsource.js | 4 ++-- lib/web/websocket/connection.js | 2 +- lib/web/websocket/events.js | 23 ++++++++++++++++++++++- lib/web/websocket/util.js | 11 ++++++----- 6 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 benchmarks/websocket/messageevent.mjs diff --git a/benchmarks/websocket/messageevent.mjs b/benchmarks/websocket/messageevent.mjs new file mode 100644 index 00000000000..b146cbcf300 --- /dev/null +++ b/benchmarks/websocket/messageevent.mjs @@ -0,0 +1,20 @@ +import { bench, group, run } from 'mitata' +import { createFastMessageEvent, MessageEvent as UndiciMessageEvent } from '../../lib/web/websocket/events.js' + +const { port1, port2 } = new MessageChannel() + +group('MessageEvent instantiation', () => { + bench('undici - fast MessageEvent init', () => { + return createFastMessageEvent('event', { data: null, ports: [port1, port2] }) + }) + + bench('undici - MessageEvent init', () => { + return new UndiciMessageEvent('event', { data: null, ports: [port1, port2] }) + }) + + bench('global - MessageEvent init', () => { + return new MessageEvent('event', { data: null, ports: [port1, port2] }) + }) +}) + +await run() diff --git a/index-fetch.js b/index-fetch.js index 7d1268a1203..a5903f1a8cb 100644 --- a/index-fetch.js +++ b/index-fetch.js @@ -17,11 +17,12 @@ module.exports.Headers = require('./lib/web/fetch/headers').Headers module.exports.Response = require('./lib/web/fetch/response').Response module.exports.Request = require('./lib/web/fetch/request').Request -const { CloseEvent, ErrorEvent, MessageEvent } = require('./lib/web/websocket/events') +const { CloseEvent, ErrorEvent, MessageEvent, createFastMessageEvent } = require('./lib/web/websocket/events') module.exports.WebSocket = require('./lib/web/websocket/websocket').WebSocket module.exports.CloseEvent = CloseEvent module.exports.ErrorEvent = ErrorEvent module.exports.MessageEvent = MessageEvent +module.exports.createFastMessageEvent = createFastMessageEvent module.exports.EventSource = require('./lib/web/eventsource/eventsource').EventSource diff --git a/lib/web/eventsource/eventsource.js b/lib/web/eventsource/eventsource.js index 7b0d9b3e5de..0b1e48dbd2d 100644 --- a/lib/web/eventsource/eventsource.js +++ b/lib/web/eventsource/eventsource.js @@ -6,7 +6,7 @@ const { makeRequest } = require('../fetch/request') const { webidl } = require('../fetch/webidl') const { EventSourceStream } = require('./eventsource-stream') const { parseMIMEType } = require('../fetch/data-url') -const { MessageEvent } = require('../websocket/events') +const { createFastMessageEvent } = require('../websocket/events') const { isNetworkError } = require('../fetch/response') const { delay } = require('./util') const { kEnumerableProperty } = require('../../core/util') @@ -290,7 +290,7 @@ class EventSource extends EventTarget { const eventSourceStream = new EventSourceStream({ eventSourceSettings: this.#state, push: (event) => { - this.dispatchEvent(new MessageEvent( + this.dispatchEvent(createFastMessageEvent( event.type, event.options )) diff --git a/lib/web/websocket/connection.js b/lib/web/websocket/connection.js index 74674ee5ab7..8a0ce1914c1 100644 --- a/lib/web/websocket/connection.js +++ b/lib/web/websocket/connection.js @@ -267,7 +267,7 @@ function onSocketClose () { // decode without BOM to the WebSocket connection close // reason. // TODO: process.nextTick - fireEvent('close', ws, CloseEvent, { + fireEvent('close', ws, (type, init) => new CloseEvent(type, init), { wasClean, code, reason }) diff --git a/lib/web/websocket/events.js b/lib/web/websocket/events.js index b1f91d0e190..7b3bb263c61 100644 --- a/lib/web/websocket/events.js +++ b/lib/web/websocket/events.js @@ -2,6 +2,7 @@ const { webidl } = require('../fetch/webidl') const { kEnumerableProperty } = require('../../core/util') +const { kConstruct } = require('../../core/symbols') const { MessagePort } = require('node:worker_threads') /** @@ -11,6 +12,11 @@ class MessageEvent extends Event { #eventInit constructor (type, eventInitDict = {}) { + if (type === kConstruct) { + super(arguments[1], arguments[2]) + return + } + webidl.argumentLengthCheck(arguments, 1, { header: 'MessageEvent constructor' }) type = webidl.converters.DOMString(type) @@ -73,8 +79,22 @@ class MessageEvent extends Event { bubbles, cancelable, data, origin, lastEventId, source, ports }) } + + static createFastMessageEvent (type, init) { + const messageEvent = new MessageEvent(kConstruct, type, init) + messageEvent.#eventInit = init + messageEvent.#eventInit.data ??= null + messageEvent.#eventInit.origin ??= '' + messageEvent.#eventInit.lastEventId ??= '' + messageEvent.#eventInit.source ??= null + messageEvent.#eventInit.ports ??= [] + return messageEvent + } } +const { createFastMessageEvent } = MessageEvent +delete MessageEvent.createFastMessageEvent + /** * @see https://websockets.spec.whatwg.org/#the-closeevent-interface */ @@ -299,5 +319,6 @@ webidl.converters.ErrorEventInit = webidl.dictionaryConverter([ module.exports = { MessageEvent, CloseEvent, - ErrorEvent + ErrorEvent, + createFastMessageEvent } diff --git a/lib/web/websocket/util.js b/lib/web/websocket/util.js index 20cdb995efe..79d9d208182 100644 --- a/lib/web/websocket/util.js +++ b/lib/web/websocket/util.js @@ -2,7 +2,7 @@ const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = require('./symbols') const { states, opcodes } = require('./constants') -const { MessageEvent, ErrorEvent } = require('./events') +const { ErrorEvent, createFastMessageEvent } = require('./events') const { isUtf8 } = require('node:buffer') /* globals Blob */ @@ -51,15 +51,16 @@ function isClosed (ws) { * @see https://dom.spec.whatwg.org/#concept-event-fire * @param {string} e * @param {EventTarget} target + * @param {(...args: ConstructorParameters) => Event} eventFactory * @param {EventInit | undefined} eventInitDict */ -function fireEvent (e, target, eventConstructor = Event, eventInitDict = {}) { +function fireEvent (e, target, eventFactory = (type, init) => new Event(type, init), eventInitDict = {}) { // 1. If eventConstructor is not given, then let eventConstructor be Event. // 2. Let event be the result of creating an event given eventConstructor, // in the relevant realm of target. // 3. Initialize event’s type attribute to e. - const event = new eventConstructor(e, eventInitDict) // eslint-disable-line new-cap + const event = eventFactory(e, eventInitDict) // 4. Initialize any other IDL attributes of event as described in the // invocation of this algorithm. @@ -110,7 +111,7 @@ function websocketMessageReceived (ws, type, data) { // 3. Fire an event named message at the WebSocket object, using MessageEvent, // with the origin attribute initialized to the serialization of the WebSocket // object’s url's origin, and the data attribute initialized to dataForEvent. - fireEvent('message', ws, MessageEvent, { + fireEvent('message', ws, createFastMessageEvent, { origin: ws[kWebSocketURL].origin, data: dataForEvent }) @@ -195,7 +196,7 @@ function failWebsocketConnection (ws, reason) { if (reason) { // TODO: process.nextTick - fireEvent('error', ws, ErrorEvent, { + fireEvent('error', ws, (type, init) => new ErrorEvent(type, init), { error: new Error(reason) }) }