diff --git a/.changeset/kind-dots-sort.md b/.changeset/kind-dots-sort.md
new file mode 100644
index 0000000000..532b994a7f
--- /dev/null
+++ b/.changeset/kind-dots-sort.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: remove memory leak
diff --git a/.changeset/real-pandas-brush.md b/.changeset/real-pandas-brush.md
new file mode 100644
index 0000000000..4f92cf1af1
--- /dev/null
+++ b/.changeset/real-pandas-brush.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: call beforeUpdate/afterUpdate callbacks when props are mutated
diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js
index 2bfe23ce3e..47547636e1 100644
--- a/packages/svelte/src/internal/client/runtime.js
+++ b/packages/svelte/src/internal/client/runtime.js
@@ -1308,7 +1308,6 @@ export function derived(init) {
create_computation_signal(flags | CLEAN, UNINITIALIZED, current_block)
);
signal.i = init;
- bind_signal_to_component_context(signal);
signal.e = default_equals;
if (current_consumer !== null) {
push_reference(current_consumer, signal);
@@ -1335,26 +1334,7 @@ export function derived_safe_equal(init) {
*/
/*#__NO_SIDE_EFFECTS__*/
export function source(initial_value) {
- const source = create_source_signal(SOURCE | CLEAN, initial_value);
- bind_signal_to_component_context(source);
- return source;
-}
-
-/**
- * This function binds a signal to the component context, so that we can fire
- * beforeUpdate/afterUpdate callbacks at the correct time etc
- * @param {import('./types.js').Signal} signal
- */
-function bind_signal_to_component_context(signal) {
- if (current_component_context === null || !current_component_context.r) return;
-
- const signals = current_component_context.d;
-
- if (signals) {
- signals.push(signal);
- } else {
- current_component_context.d = [signal];
- }
+ return create_source_signal(SOURCE | CLEAN, initial_value);
}
/**
@@ -1366,6 +1346,13 @@ function bind_signal_to_component_context(signal) {
export function mutable_source(initial_value) {
const s = source(initial_value);
s.e = safe_equal;
+
+ // bind the signal to the component context, in case we need to
+ // track updates to trigger beforeUpdate/afterUpdate callbacks
+ if (current_component_context) {
+ (current_component_context.d ??= []).push(s);
+ }
+
return s;
}
@@ -1974,10 +1961,7 @@ function observe_all(context) {
for (const signal of context.d) get(signal);
}
- const props = get_descriptors(context.s);
- for (const descriptor of Object.values(props)) {
- if (descriptor.get) descriptor.get();
- }
+ deep_read(context.s);
}
/**
diff --git a/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/Child.svelte b/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/Child.svelte
new file mode 100644
index 0000000000..9841295830
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/Child.svelte
@@ -0,0 +1,12 @@
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/_config.js b/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/_config.js
new file mode 100644
index 0000000000..aa42c2a2d6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/_config.js
@@ -0,0 +1,20 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+import { logs } from './logs.js';
+
+export default test({
+ html: ``,
+
+ test({ assert, target }) {
+ const btn = target.querySelector('button');
+
+ flushSync(() => btn?.click());
+
+ assert.htmlEqual(target.innerHTML, ``);
+ assert.deepEqual(logs, ['changed', 'changed']);
+ },
+
+ after_test() {
+ logs.length = 0;
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/logs.js b/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/logs.js
new file mode 100644
index 0000000000..8c8a1318cb
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/logs.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const logs = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/main.svelte b/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/main.svelte
new file mode 100644
index 0000000000..6e91de5841
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/before-update-in-legacy-child/main.svelte
@@ -0,0 +1,10 @@
+
+
+
+
+