Skip to content

Commit 074615d

Browse files
authored
fix: prevent infinite loops stemming from invalidation method (#9811)
* fix: prevent infinite loops stemming from invalidation method The logic was flawed: the captured signals where always added to the previous captured no matter what, which meant a) memory leak b) that when another one runs afterwards, it will falsely contain the signals from the previous run fixes #9788 * fix lint
1 parent edc569e commit 074615d

File tree

4 files changed

+57
-9
lines changed

4 files changed

+57
-9
lines changed

.changeset/healthy-planes-vanish.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: prevent infinite loops stemming from invalidation method

packages/svelte/src/internal/client/runtime.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -863,28 +863,28 @@ export function set_sync(signal, value) {
863863
* Invokes a function and captures all signals that are read during the invocation,
864864
* then invalidates them.
865865
* @param {() => any} fn
866-
* @returns {Set<import('./types.js').Signal>}
867866
*/
868867
export function invalidate_inner_signals(fn) {
869-
const previous_is_signals_recorded = is_signals_recorded;
870-
const previous_captured_signals = captured_signals;
868+
var previous_is_signals_recorded = is_signals_recorded;
869+
var previous_captured_signals = captured_signals;
871870
is_signals_recorded = true;
872871
captured_signals = new Set();
872+
var captured = captured_signals;
873+
var signal;
873874
try {
874875
untrack(fn);
875876
} finally {
876877
is_signals_recorded = previous_is_signals_recorded;
877-
let signal;
878-
for (signal of captured_signals) {
879-
previous_captured_signals.add(signal);
878+
if (is_signals_recorded) {
879+
for (signal of captured_signals) {
880+
previous_captured_signals.add(signal);
881+
}
880882
}
881883
captured_signals = previous_captured_signals;
882884
}
883-
let signal;
884-
for (signal of captured_signals) {
885+
for (signal of captured) {
885886
mutate(signal, null /* doesnt matter */);
886887
}
887-
return captured_signals;
888888
}
889889

890890
/**
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { flushSync } from 'svelte';
2+
import { ok, test } from '../../test';
3+
4+
export default test({
5+
html: `
6+
<select>
7+
<option value="a">A</option>
8+
<option value="b">B</option>
9+
</select>
10+
selected: a
11+
`,
12+
13+
test({ assert, target }) {
14+
const select = target.querySelector('select');
15+
ok(select);
16+
const event = new window.Event('change');
17+
select.value = 'b';
18+
select.dispatchEvent(event);
19+
flushSync();
20+
21+
assert.htmlEqual(
22+
target.innerHTML,
23+
`
24+
<select>
25+
<option value="a">A</option>
26+
<option value="b">B</option>
27+
</select>
28+
selected: b
29+
`
30+
);
31+
}
32+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
let entries = [{selected: 'a' }]
3+
</script>
4+
5+
{#each entries as entry}
6+
<select bind:value={entry.selected}>
7+
<option value='a'>A</option>
8+
<option value='b'>B</option>
9+
</select>
10+
selected: {entry.selected}
11+
{/each}

0 commit comments

Comments
 (0)