From 69e11d8b1a3b69f85573803d258721d88b115649 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:43:17 +0000 Subject: [PATCH 1/2] faster brand check --- benchmarks/fetch/webidl-is.mjs | 26 ++++++++++++++++++++++ lib/web/fetch/formdata.js | 2 +- lib/web/fetch/request.js | 2 +- lib/web/fetch/response.js | 2 +- lib/web/fetch/webidl.js | 25 +++++++++++---------- lib/web/websocket/stream/websocketerror.js | 2 +- test/webidl/helpers.js | 4 +--- types/webidl.d.ts | 4 ++-- 8 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 benchmarks/fetch/webidl-is.mjs diff --git a/benchmarks/fetch/webidl-is.mjs b/benchmarks/fetch/webidl-is.mjs new file mode 100644 index 00000000000..733f634fcc7 --- /dev/null +++ b/benchmarks/fetch/webidl-is.mjs @@ -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() diff --git a/lib/web/fetch/formdata.js b/lib/web/fetch/formdata.js index ef45cefb369..c115528f1dd 100644 --- a/lib/web/fetch/formdata.js +++ b/lib/web/fetch/formdata.js @@ -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 } diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index fec12b58308..d7520ef798b 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -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) { diff --git a/lib/web/fetch/response.js b/lib/web/fetch/response.js index 60cee8bb26e..3e5590a46cf 100644 --- a/lib/web/fetch/response.js +++ b/lib/web/fetch/response.js @@ -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, diff --git a/lib/web/fetch/webidl.js b/lib/web/fetch/webidl.js index 34653e6439e..7e584677892 100644 --- a/lib/web/fetch/webidl.js +++ b/lib/web/fetch/webidl.js @@ -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: {}, @@ -45,7 +47,7 @@ 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 @@ -53,7 +55,7 @@ webidl.brandCheck = function (V, I) { } 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))) { @@ -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 @@ -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) { diff --git a/lib/web/websocket/stream/websocketerror.js b/lib/web/websocket/stream/websocketerror.js index e47551a96b9..888af987186 100644 --- a/lib/web/websocket/stream/websocketerror.js +++ b/lib/web/websocket/stream/websocketerror.js @@ -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 } diff --git a/test/webidl/helpers.js b/test/webidl/helpers.js index fd7a7dfb97b..c9d4c6d1ce1 100644 --- a/test/webidl/helpers.js +++ b/test/webidl/helpers.js @@ -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') diff --git a/types/webidl.d.ts b/types/webidl.d.ts index 902d54f5252..7c87394d95f 100644 --- a/types/webidl.d.ts +++ b/types/webidl.d.ts @@ -84,7 +84,7 @@ interface WebidlUtil { */ Stringify (V: any): string - MakeTypeAssertion (Prototype: T['prototype']): (arg: any) => arg is T + MakeTypeAssertion any>(I: I): (arg: any) => arg is InstanceType } interface WebidlConverters { @@ -176,7 +176,7 @@ interface WebidlConverters { [Key: string]: (...args: any[]) => unknown } -type IsAssertion = (arg: any) => arg is T +type IsAssertion any> = (arg: any) => arg is InstanceType interface WebidlIs { Request: IsAssertion From eed0d1955a4b6d02afe503f6e93d046b5f153f98 Mon Sep 17 00:00:00 2001 From: tsctx <91457664+tsctx@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:53:35 +0000 Subject: [PATCH 2/2] fixup type --- types/webidl.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/webidl.d.ts b/types/webidl.d.ts index 7c87394d95f..10037eb5362 100644 --- a/types/webidl.d.ts +++ b/types/webidl.d.ts @@ -84,7 +84,7 @@ interface WebidlUtil { */ Stringify (V: any): string - MakeTypeAssertion any>(I: I): (arg: any) => arg is InstanceType + MakeTypeAssertion (I: I): (arg: any) => arg is I } interface WebidlConverters { @@ -176,7 +176,7 @@ interface WebidlConverters { [Key: string]: (...args: any[]) => unknown } -type IsAssertion any> = (arg: any) => arg is InstanceType +type IsAssertion = (arg: any) => arg is T interface WebidlIs { Request: IsAssertion