Skip to content

Commit

Permalink
faster brand check
Browse files Browse the repository at this point in the history
  • Loading branch information
tsctx committed Oct 11, 2024
1 parent b6d53fe commit 69e11d8
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 21 deletions.
26 changes: 26 additions & 0 deletions benchmarks/fetch/webidl-is.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { bench, run, barplot } from 'mitata'
import { Headers, FormData } from '../../index.js'
import { webidl } from '../../lib/web/fetch/webidl.js'

const headers = new Headers()
const fd = new FormData()

barplot(() => {
bench('webidl.is.FormData (ok)', () => {
return webidl.is.FormData(fd)
})

bench('webidl.is.FormData (bad)', () => {
return !webidl.is.FormData(headers)
})

bench('instanceof (ok)', () => {
return fd instanceof FormData
})

bench('instanceof (bad)', () => {
return !(headers instanceof FormData)
})
})

await run()
2 changes: 1 addition & 1 deletion lib/web/fetch/formdata.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,6 @@ function makeEntry (name, value, filename) {
return { name, value }
}

webidl.is.FormData = webidl.util.MakeTypeAssertion(FormData.prototype)
webidl.is.FormData = webidl.util.MakeTypeAssertion(FormData)

module.exports = { FormData, makeEntry, setFormDataState }
2 changes: 1 addition & 1 deletion lib/web/fetch/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ Object.defineProperties(Request.prototype, {
}
})

webidl.is.Request = webidl.util.MakeTypeAssertion(Request.prototype)
webidl.is.Request = webidl.util.MakeTypeAssertion(Request)

// https://fetch.spec.whatwg.org/#requestinfo
webidl.converters.RequestInfo = function (V, prefix, argument) {
Expand Down
2 changes: 1 addition & 1 deletion lib/web/fetch/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ webidl.converters.ResponseInit = webidl.dictionaryConverter([
}
])

webidl.is.Response = webidl.util.MakeTypeAssertion(Response.prototype)
webidl.is.Response = webidl.util.MakeTypeAssertion(Response)

module.exports = {
isNetworkError,
Expand Down
25 changes: 13 additions & 12 deletions lib/web/fetch/webidl.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const BIGINT = 6
const NULL = 7
const OBJECT = 8 // function and object

const FunctionPrototypeSymbolHasInstance = Function.call.bind(Function.prototype[Symbol.hasInstance])

/** @type {import('../../../types/webidl').Webidl} */
const webidl = {
converters: {},
Expand Down Expand Up @@ -45,15 +47,15 @@ webidl.errors.invalidArgument = function (context) {

// https://webidl.spec.whatwg.org/#implements
webidl.brandCheck = function (V, I) {
if (!I.prototype.isPrototypeOf(V)) { // eslint-disable-line no-prototype-builtins
if (!FunctionPrototypeSymbolHasInstance(I, V)) {
const err = new TypeError('Illegal invocation')
err.code = 'ERR_INVALID_THIS' // node compat.
throw err
}
}

webidl.brandCheckMultiple = function (List) {
const prototypes = List.map((c) => webidl.util.MakeTypeAssertion(c.prototype))
const prototypes = List.map((c) => webidl.util.MakeTypeAssertion(c))

return (V) => {
if (prototypes.every(typeCheck => !typeCheck(V))) {
Expand Down Expand Up @@ -81,9 +83,8 @@ webidl.illegalConstructor = function () {
})
}

const isPrototypeOf = Object.prototype.isPrototypeOf
webidl.util.MakeTypeAssertion = function (Prototype) {
return (O) => isPrototypeOf.call(Prototype, O)
webidl.util.MakeTypeAssertion = function (I) {
return (O) => FunctionPrototypeSymbolHasInstance(I, O)
}

// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
Expand Down Expand Up @@ -462,13 +463,13 @@ webidl.nullableConverter = function (converter) {
}
}

webidl.is.ReadableStream = webidl.util.MakeTypeAssertion(ReadableStream.prototype)
webidl.is.Blob = webidl.util.MakeTypeAssertion(Blob.prototype)
webidl.is.URLSearchParams = webidl.util.MakeTypeAssertion(URLSearchParams.prototype)
webidl.is.File = webidl.util.MakeTypeAssertion((globalThis.File ?? require('node:buffer').File).prototype)
webidl.is.URL = webidl.util.MakeTypeAssertion(URL.prototype)
webidl.is.AbortSignal = webidl.util.MakeTypeAssertion(AbortSignal.prototype)
webidl.is.MessagePort = webidl.util.MakeTypeAssertion(MessagePort.prototype)
webidl.is.ReadableStream = webidl.util.MakeTypeAssertion(ReadableStream)
webidl.is.Blob = webidl.util.MakeTypeAssertion(Blob)
webidl.is.URLSearchParams = webidl.util.MakeTypeAssertion(URLSearchParams)
webidl.is.File = webidl.util.MakeTypeAssertion(globalThis.File ?? require('node:buffer').File)
webidl.is.URL = webidl.util.MakeTypeAssertion(URL)
webidl.is.AbortSignal = webidl.util.MakeTypeAssertion(AbortSignal)
webidl.is.MessagePort = webidl.util.MakeTypeAssertion(MessagePort)

// https://webidl.spec.whatwg.org/#es-DOMString
webidl.converters.DOMString = function (V, prefix, argument, opts) {
Expand Down
2 changes: 1 addition & 1 deletion lib/web/websocket/stream/websocketerror.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ Object.defineProperties(WebSocketError.prototype, {
}
})

webidl.is.WebSocketError = webidl.util.MakeTypeAssertion(WebSocketError.prototype)
webidl.is.WebSocketError = webidl.util.MakeTypeAssertion(WebSocketError)

module.exports = { WebSocketError, createUnvalidatedWebSocketError }
4 changes: 1 addition & 3 deletions test/webidl/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ test('webidl.interfaceConverter', (t) => {
class A {}
class B {}

const converter = webidl.interfaceConverter(
webidl.util.MakeTypeAssertion(A.prototype)
)
const converter = webidl.interfaceConverter(webidl.util.MakeTypeAssertion(A))

assert.throws(() => {
converter(new B(), 'converter', 'converter')
Expand Down
4 changes: 2 additions & 2 deletions types/webidl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ interface WebidlUtil {
*/
Stringify (V: any): string

MakeTypeAssertion <T extends { prototype: T }>(Prototype: T['prototype']): (arg: any) => arg is T
MakeTypeAssertion <I extends abstract new (...args: any[]) => any>(I: I): (arg: any) => arg is InstanceType<I>
}

interface WebidlConverters {
Expand Down Expand Up @@ -176,7 +176,7 @@ interface WebidlConverters {
[Key: string]: (...args: any[]) => unknown
}

type IsAssertion<T> = (arg: any) => arg is T
type IsAssertion<T extends abstract new (...args: any[]) => any> = (arg: any) => arg is InstanceType<T>

interface WebidlIs {
Request: IsAssertion<undici.Request>

Check failure on line 182 in types/webidl.d.ts

View workflow job for this annotation

GitHub Actions / Test TypeScript types

Type 'Request' does not satisfy the constraint 'abstract new (...args: any[]) => any'.
Expand Down

0 comments on commit 69e11d8

Please sign in to comment.