fix: don't try to add owners to non-`$state` class fields (#14533)

* fix: don't try to add owners to non-`$state` class fields

`$state.raw` and `$derived(.by)` will not have a state symbol on them, potentially causing a disastrous amount of traversal to potentially not find any state symbol. So it's better to not traverse them.

Potentially someone could create a `$state` while creating `$state.raw` or inside a `$derived.by`, but that feels so much of an edge case that it doesn't warrant a perf hit for the common case.

Fixes #14491

* for bind:, too
pull/14537/head
Simon H 9 months ago committed by GitHub
parent b5588523fc
commit bbee1fc7e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: don't try to add owners to non-`$state` class fields

@ -184,17 +184,22 @@ export function ClassBody(node, context) {
'method', 'method',
b.id('$.ADD_OWNER'), b.id('$.ADD_OWNER'),
[b.id('owner')], [b.id('owner')],
Array.from(public_state.keys()).map((name) => Array.from(public_state)
b.stmt( // Only run ownership addition on $state fields.
b.call( // Theoretically someone could create a `$state` while creating `$state.raw` or inside a `$derived.by`,
'$.add_owner', // but that feels so much of an edge case that it doesn't warrant a perf hit for the common case.
b.call('$.get', b.member(b.this, b.private_id(name))), .filter(([_, { kind }]) => kind === 'state')
b.id('owner'), .map(([name]) =>
b.literal(false), b.stmt(
is_ignored(node, 'ownership_invalid_binding') && b.true b.call(
'$.add_owner',
b.call('$.get', b.member(b.this, b.private_id(name))),
b.id('owner'),
b.literal(false),
is_ignored(node, 'ownership_invalid_binding') && b.true
)
) )
) ),
),
true true
) )
); );

@ -2,7 +2,7 @@
/** @import { AST, TemplateNode } from '#compiler' */ /** @import { AST, TemplateNode } from '#compiler' */
/** @import { ComponentContext } from '../../types.js' */ /** @import { ComponentContext } from '../../types.js' */
import { dev, is_ignored } from '../../../../../state.js'; import { dev, is_ignored } from '../../../../../state.js';
import { get_attribute_chunks } from '../../../../../utils/ast.js'; import { get_attribute_chunks, object } from '../../../../../utils/ast.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '../../../../../utils/builders.js';
import { create_derived } from '../../utils.js'; import { create_derived } from '../../utils.js';
import { build_bind_this, validate_binding } from '../shared/utils.js'; import { build_bind_this, validate_binding } from '../shared/utils.js';
@ -176,16 +176,26 @@ export function build_component(node, component_name, context, anchor = context.
bind_this = attribute.expression; bind_this = attribute.expression;
} else { } else {
if (dev) { if (dev) {
binding_initializers.push( const left = object(attribute.expression);
b.stmt( let binding;
b.call( if (left?.type === 'Identifier') {
b.id('$.add_owner_effect'), binding = context.state.scope.get(left.name);
b.thunk(expression), }
b.id(component_name), // Only run ownership addition on $state fields.
is_ignored(node, 'ownership_invalid_binding') && b.true // Theoretically someone could create a `$state` while creating `$state.raw` or inside a `$derived.by`,
// but that feels so much of an edge case that it doesn't warrant a perf hit for the common case.
if (binding?.kind !== 'derived' && binding?.kind !== 'raw_state') {
binding_initializers.push(
b.stmt(
b.call(
b.id('$.add_owner_effect'),
b.thunk(expression),
b.id(component_name),
is_ignored(node, 'ownership_invalid_binding') && b.true
)
) )
) );
); }
} }
const is_store_sub = const is_store_sub =

Loading…
Cancel
Save