Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d243115

Browse filesBrowse files
anonrigRafaelGSS
authored andcommitted
url: improve URLSearchParams creation performance
PR-URL: #47190 Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
1 parent 760e13c commit d243115
Copy full SHA for d243115

File tree

Expand file treeCollapse file tree

2 files changed

+49
-25
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

2 files changed

+49
-25
lines changed
Open diff view settings
Collapse file

‎lib/internal/url.js‎

Copy file name to clipboardExpand all lines: lib/internal/url.js
+45-25Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const {
44
Array,
5+
ArrayIsArray,
56
ArrayPrototypeJoin,
67
ArrayPrototypeMap,
78
ArrayPrototypePush,
@@ -156,52 +157,74 @@ function isURLSearchParams(self) {
156157
}
157158

158159
class URLSearchParams {
160+
[searchParams] = [];
161+
162+
// "associated url object"
163+
[context] = null;
164+
159165
// URL Standard says the default value is '', but as undefined and '' have
160166
// the same result, undefined is used to prevent unnecessary parsing.
161167
// Default parameter is necessary to keep URLSearchParams.length === 0 in
162168
// accordance with Web IDL spec.
163169
constructor(init = undefined) {
164-
if (init === null || init === undefined) {
165-
this[searchParams] = [];
170+
if (init == null) {
171+
// Do nothing
166172
} else if (typeof init === 'object' || typeof init === 'function') {
167173
const method = init[SymbolIterator];
168174
if (method === this[SymbolIterator]) {
169175
// While the spec does not have this branch, we can use it as a
170176
// shortcut to avoid having to go through the costly generic iterator.
171177
const childParams = init[searchParams];
172178
this[searchParams] = childParams.slice();
173-
} else if (method !== null && method !== undefined) {
179+
} else if (method != null) {
180+
// Sequence<sequence<USVString>>
174181
if (typeof method !== 'function') {
175182
throw new ERR_ARG_NOT_ITERABLE('Query pairs');
176183
}
177184

178-
// Sequence<sequence<USVString>>
179-
// Note: per spec we have to first exhaust the lists then process them
180-
const pairs = [];
185+
// The following implementationd differs from the URL specification:
186+
// Sequences must first be converted from ECMAScript objects before
187+
// and operations are done on them, and the operation of converting
188+
// the sequences would first exhaust the iterators. If the iterator
189+
// returns something invalid in the middle, whether it would be called
190+
// after that would be an observable change to the users.
191+
// Exhausting the iterator and later converting them to USVString comes
192+
// with a significant cost (~40-80%). In order optimize URLSearchParams
193+
// creation duration, Node.js merges the iteration and converting
194+
// iterations into a single iteration.
181195
for (const pair of init) {
182-
if ((typeof pair !== 'object' && typeof pair !== 'function') ||
183-
pair === null ||
184-
typeof pair[SymbolIterator] !== 'function') {
196+
if (pair == null) {
185197
throw new ERR_INVALID_TUPLE('Each query pair', '[name, value]');
186-
}
187-
const convertedPair = [];
188-
for (const element of pair)
189-
ArrayPrototypePush(convertedPair, toUSVString(element));
190-
ArrayPrototypePush(pairs, convertedPair);
191-
}
198+
} else if (ArrayIsArray(pair)) {
199+
// If innerSequence's size is not 2, then throw a TypeError.
200+
if (pair.length !== 2) {
201+
throw new ERR_INVALID_TUPLE('Each query pair', '[name, value]');
202+
}
203+
// Append (innerSequence[0], innerSequence[1]) to querys list.
204+
ArrayPrototypePush(this[searchParams], toUSVString(pair[0]), toUSVString(pair[1]));
205+
} else {
206+
if (((typeof pair !== 'object' && typeof pair !== 'function') ||
207+
typeof pair[SymbolIterator] !== 'function')) {
208+
throw new ERR_INVALID_TUPLE('Each query pair', '[name, value]');
209+
}
192210

193-
this[searchParams] = [];
194-
for (const pair of pairs) {
195-
if (pair.length !== 2) {
196-
throw new ERR_INVALID_TUPLE('Each query pair', '[name, value]');
211+
let length = 0;
212+
213+
for (const element of pair) {
214+
length++;
215+
ArrayPrototypePush(this[searchParams], toUSVString(element));
216+
}
217+
218+
// If innerSequence's size is not 2, then throw a TypeError.
219+
if (length !== 2) {
220+
throw new ERR_INVALID_TUPLE('Each query pair', '[name, value]');
221+
}
197222
}
198-
ArrayPrototypePush(this[searchParams], pair[0], pair[1]);
199223
}
200224
} else {
201225
// Record<USVString, USVString>
202226
// Need to use reflection APIs for full spec compliance.
203227
const visited = {};
204-
this[searchParams] = [];
205228
const keys = ReflectOwnKeys(init);
206229
for (let i = 0; i < keys.length; i++) {
207230
const key = keys[i];
@@ -223,13 +246,10 @@ class URLSearchParams {
223246
}
224247
}
225248
} else {
226-
// USVString
249+
// https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams
227250
init = toUSVString(init);
228251
this[searchParams] = init ? parseParams(init) : [];
229252
}
230-
231-
// "associated url object"
232-
this[context] = null;
233253
}
234254

235255
[inspect.custom](recurseTimes, ctx) {
Collapse file

‎test/parallel/test-whatwg-url-custom-searchparams-constructor.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-whatwg-url-custom-searchparams-constructor.js
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ function makeIterableFunc(array) {
4747
assert.throws(() => new URLSearchParams([null]), tupleError);
4848
assert.throws(() => new URLSearchParams([{ [Symbol.iterator]: 42 }]),
4949
tupleError);
50+
51+
assert.throws(() => new URLSearchParams(
52+
makeIterableFunc([['key', 'val', 'val2']])
53+
), tupleError);
5054
}
5155

5256
{

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.