-
Notifications
You must be signed in to change notification settings - Fork 29.7k
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
url: avoid instanceof for WHATWG URL #11690
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vaguely remember some of the slowdowns of instanceof
are already fixed in the v8 lkgr fork(5.8-ish)...at least for Buffer
last time I played around with it. Nonethess I think testing for internal symbols is better anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hit the wrong option..
lib/internal/url.js
Outdated
@@ -701,7 +701,7 @@ class URLSearchParams { | |||
} | |||
|
|||
[util.inspect.custom](recurseTimes, ctx) { | |||
if (!this || !(this instanceof URLSearchParams)) { | |||
if (!this || this[searchParams] === undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The searchParams
symbol is used by the URL
class as well, so this will no longer error out on URL
objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've now added per-class symbols that are checked instead.
CI again after symbol check changes: https://ci.nodejs.org/job/node-test-pull-request/6718/ |
lib/internal/url.js
Outdated
@@ -7,6 +7,8 @@ const context = Symbol('context'); | |||
const cannotBeBase = Symbol('cannot-be-base'); | |||
const special = Symbol('special'); | |||
const searchParams = Symbol('query'); | |||
const URLInstance = Symbol('url-instance'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer urlInstance
just to be consistent with searchParamsInstance
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
lib/internal/url.js
Outdated
@@ -701,7 +705,7 @@ class URLSearchParams { | |||
} | |||
|
|||
[util.inspect.custom](recurseTimes, ctx) { | |||
if (!this || !(this instanceof URLSearchParams)) { | |||
if (!this || this[searchParamsInstance] !== true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still reluctant about writing out this[searchParamsInstance] !== true
when a !this[searchParamsInstance]
would do. Is this change visible performance-wise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just being more explicit about the expected value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to be more explicit.
lib/internal/url.js
Outdated
@@ -7,6 +7,8 @@ const context = Symbol('context'); | |||
const cannotBeBase = Symbol('cannot-be-base'); | |||
const special = Symbol('special'); | |||
const searchParams = Symbol('query'); | |||
const urlInstance = Symbol('url-instance'); | |||
const searchParamsInstance = Symbol('searchParams-instance'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use the existing context
and searchParams
symbols rather than introducing new ones? All URL
objects should have the context
symbol set. Likewise all URLSearchParams
instances should have the searchParams
symbol set (I believe)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As @TimothyGu noted, both classes use both of those symbols, so they don't currently each have a unique symbol?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probably separate them like this:
if (url[searchParams]) {
if (url[searchParams][searchParams]) { } // URL, it's an array
else {} // URLSearchParams, it's undefined
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any updates on this?
lib/internal/url.js
Outdated
@@ -219,7 +221,8 @@ class URL { | |||
constructor(input, base) { | |||
// toUSVString is not needed. | |||
input = '' + input; | |||
if (base !== undefined && !(base instanceof URL)) | |||
this[urlInstance] = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might put the urlInstance
in the prototype to avoid having to set this every single time an object is created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then URL.prototype
will pass the brand check, which seems bad.
lib/internal/url.js
Outdated
@@ -646,6 +649,7 @@ class URLSearchParams { | |||
// Default parameter is necessary to keep URLSearchParams.length === 0 in | |||
// accordance with Web IDL spec. | |||
constructor(init = undefined) { | |||
this[searchParamsInstance] = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
Alright, I've switched to checking using only
Results are pretty close to what they were before. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally LGTM with a question/suggestion
@@ -239,8 +239,10 @@ class URL { | |||
constructor(input, base) { | |||
// toUSVString is not needed. | |||
input = `${input}`; | |||
if (base !== undefined && !(base instanceof URL)) | |||
if (base !== undefined && | |||
(!base[searchParams] || !base[searchParams][searchParams])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mscdex ... is there benefit to moving this into a separate isURL(u)
function that can be exported by internal/url
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performance-wise? No. There's actually a small but measurable performance hit when extracting all of the logic to a separate function, compared to having it inline. For example:
improvement confidence p.value
url/url-searchparams-read.js n=20000000 param="nonexistent" method="get" 165.07 % *** 1.189398e-66
url/url-searchparams-read.js n=20000000 param="nonexistent" method="getAll" 139.46 % *** 4.331942e-46
url/url-searchparams-read.js n=20000000 param="nonexistent" method="has" 161.48 % *** 2.741360e-52
url/url-searchparams-read.js n=20000000 param="one" method="get" 222.70 % *** 3.111318e-46
url/url-searchparams-read.js n=20000000 param="one" method="getAll" 113.31 % *** 1.122968e-53
url/url-searchparams-read.js n=20000000 param="one" method="has" 232.04 % *** 1.571222e-55
url/url-searchparams-read.js n=20000000 param="three" method="get" 184.13 % *** 2.078669e-48
url/url-searchparams-read.js n=20000000 param="three" method="getAll" 100.34 % *** 4.591714e-56
url/url-searchparams-read.js n=20000000 param="three" method="has" 181.29 % *** 7.340053e-43
url/url-searchparams-read.js n=20000000 param="two" method="get" 209.77 % *** 4.581349e-54
url/url-searchparams-read.js n=20000000 param="two" method="getAll" 102.89 % *** 2.001755e-36
url/url-searchparams-read.js n=20000000 param="two" method="has" 203.09 % *** 1.810527e-44
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still LGTM
Landed in b76a350. |
PR-URL: #11690 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com>
cc @mscdex |
@guybedford I do not know offhand. Someone would have to be sure to run benchmarks on both implementations and compare the results for master. |
I think there's also a concern about |
This PR replaces
instanceof
checks with symbol checks. Various relevant results:CI: https://ci.nodejs.org/job/node-test-pull-request/6707/
/cc @nodejs/url
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)