perf: use Set for exclude checks in rest_props/exclude_from_object/legacy_rest_props

Replaces Array.includes() (O(n)) with Set.has() (O(1)) across three
sibling paths that were missed when client runes rest_props was updated:
- server/index.js:rest_props (called per SSR request)
- shared/utils.js:exclude_from_object (called for rest destructuring)
- client/reactivity/props.js:legacy_rest_props_handler ($$restProps proxy)

No signature changes — arrays are still accepted and converted to Set
internally. legacy_rest_props.deleteProperty push() → add() to match.

Fixes #18601
pull/18654/head
Xsidz 2 months ago
parent c0987c09f8
commit 8103d62b40

@ -0,0 +1,5 @@
---
'svelte': patch
---
perf: use Set for exclude-list membership checks in rest_props, exclude_from_object, and legacy_rest_props

@ -96,11 +96,11 @@ export function rest_props(props, exclude, name) {
/**
* The proxy handler for legacy $$restProps and $$props
* @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Array<string | symbol>, special: Record<string | symbol, (v?: unknown) => unknown>, version: Source<number>, parent_effect: Effect }>}}
* @type {ProxyHandler<{ props: Record<string | symbol, unknown>, exclude: Set<string | symbol>, special: Record<string | symbol, (v?: unknown) => unknown>, version: Source<number>, parent_effect: Effect }>}}
*/
const legacy_rest_props_handler = {
get(target, key) {
if (target.exclude.includes(key)) return;
if (target.exclude.has(key)) return;
get(target.version);
return key in target.special ? target.special[key]() : target.props[key];
},
@ -132,7 +132,7 @@ const legacy_rest_props_handler = {
return true;
},
getOwnPropertyDescriptor(target, key) {
if (target.exclude.includes(key)) return;
if (target.exclude.has(key)) return;
if (key in target.props) {
return {
enumerable: true,
@ -143,17 +143,17 @@ const legacy_rest_props_handler = {
},
deleteProperty(target, key) {
// Svelte 4 allowed for deletions on $$restProps
if (target.exclude.includes(key)) return true;
target.exclude.push(key);
if (target.exclude.has(key)) return true;
target.exclude.add(key);
update(target.version);
return true;
},
has(target, key) {
if (target.exclude.includes(key)) return false;
if (target.exclude.has(key)) return false;
return key in target.props;
},
ownKeys(target) {
return Reflect.ownKeys(target.props).filter((key) => !target.exclude.includes(key));
return Reflect.ownKeys(target.props).filter((key) => !target.exclude.has(key));
}
};
@ -166,7 +166,7 @@ export function legacy_rest_props(props, exclude) {
return new Proxy(
{
props,
exclude,
exclude: new Set(exclude),
special: {},
version: source(0),
// TODO this is only necessary because we need to track component

@ -347,11 +347,11 @@ export function slot(renderer, $$props, name, slot_props, fallback_fn) {
* @returns {Record<string, unknown>}
*/
export function rest_props(props, rest) {
const exclude = new Set(rest);
/** @type {Record<string, unknown>} */
const rest_props = {};
let key;
for (key of Object.keys(props)) {
if (!rest.includes(key)) {
for (const key of Object.keys(props)) {
if (!exclude.has(key)) {
rest_props[key] = props[key];
}
}

@ -125,17 +125,18 @@ export function to_array(value, n) {
* @returns {Record<string | symbol, unknown>}
*/
export function exclude_from_object(obj, keys) {
const exclude = new Set(keys);
/** @type {Record<string | symbol, unknown>} */
var result = {};
for (var key in obj) {
if (!keys.includes(key)) {
if (!exclude.has(key)) {
result[key] = obj[key];
}
}
for (var symbol of Object.getOwnPropertySymbols(obj)) {
if (Object.propertyIsEnumerable.call(obj, symbol) && !keys.includes(symbol)) {
if (Object.propertyIsEnumerable.call(obj, symbol) && !exclude.has(symbol)) {
result[symbol] = obj[symbol];
}
}

Loading…
Cancel
Save