Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create fast MessageEvent #3170

Merged
merged 4 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions benchmarks/websocket/messageevent.mjs
Original file line number Diff line number Diff line change
@@ -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()
3 changes: 2 additions & 1 deletion index-fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions lib/web/eventsource/eventsource.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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
))
Expand Down
2 changes: 1 addition & 1 deletion lib/web/websocket/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
})

Expand Down
23 changes: 22 additions & 1 deletion lib/web/websocket/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')

/**
Expand All @@ -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)
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -299,5 +319,6 @@ webidl.converters.ErrorEventInit = webidl.dictionaryConverter([
module.exports = {
MessageEvent,
CloseEvent,
ErrorEvent
ErrorEvent,
createFastMessageEvent
}
11 changes: 6 additions & 5 deletions lib/web/websocket/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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<typeof Event>) => 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.
Expand Down Expand Up @@ -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
})
Expand Down Expand Up @@ -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)
})
}
Expand Down
Loading