Skip to content

Commit b6e0f64

Browse files
RomainLanzaduh95
authored andcommitted
url: create URLSearchParams collision map lazily
Object property keys are already unique unless USVString normalization changes a key. Avoid allocating and updating a SafeMap for ordinary record keys. Create and backfill it when normalization first changes a key. This improves object construction by 10% to 25% in the measured cases and reduces allocation by about 31% for a ten-property record. Assisted-by: Amp Signed-off-by: Romain Lanz <romain.lanz@pm.me> PR-URL: #65801 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent b161cfd commit b6e0f64

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

‎lib/internal/url.js‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ class URLSearchParams {
447447
} else {
448448
// Record<USVString, USVString>
449449
// Need to use reflection APIs for full spec compliance.
450-
const visited = new SafeMap();
450+
// Object keys are unique, but USVString normalization may merge
451+
// distinct keys. Lazily track normalized keys if a key changes.
452+
let visited;
451453
const keys = ReflectOwnKeys(init);
452454
for (let i = 0; i < keys.length; i++) {
453455
const key = keys[i];
@@ -456,8 +458,18 @@ class URLSearchParams {
456458
const typedKey = toUSVString(key);
457459
const typedValue = toUSVString(init[key]);
458460

459-
// Two different keys may become the same USVString after normalization.
460-
// In that case, we retain the later one. Refer to WPT.
461+
if (visited === undefined && typedKey === key) {
462+
ArrayPrototypePush(this.#searchParams, typedKey, typedValue);
463+
continue;
464+
}
465+
466+
if (visited === undefined) {
467+
visited = new SafeMap();
468+
for (let j = 0; j < this.#searchParams.length; j += 2) {
469+
visited.set(this.#searchParams[j], j + 1);
470+
}
471+
}
472+
461473
const keyIdx = visited.get(typedKey);
462474
if (keyIdx !== undefined) {
463475
this.#searchParams[keyIdx] = typedValue;

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ function makeIterableFunc(array) {
4747
params = new URLSearchParams({ hasOwnProperty: 1 });
4848
assert.strictEqual(params.get('hasOwnProperty'), '1');
4949
assert.strictEqual(params.toString(), 'hasOwnProperty=1');
50+
// A malformed key can collide with a valid key collected before the map
51+
// exists.
52+
params = new URLSearchParams({
53+
'before': '0',
54+
'\uFFFDx': 'first',
55+
'\uD835x': 'last',
56+
'after': '3'
57+
});
58+
assert.deepStrictEqual([...params], [
59+
['before', '0'],
60+
['\uFFFDx', 'last'],
61+
['after', '3'],
62+
]);
5063
assert.throws(() => new URLSearchParams([[1]]), tupleError);
5164
assert.throws(() => new URLSearchParams([[1, 2, 3]]), tupleError);
5265
assert.throws(() => new URLSearchParams({ [Symbol('test')]: 42 }),

0 commit comments

Comments
 (0)