From 762d0429b1fd2c3743fe7836108b3c1c557db750 Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Mon, 2 Jan 2023 06:51:01 +0100
Subject: [PATCH] [fix] propagate bindings correctly (#8114)
Fixes #8103 introduced through #7981
Keeps the infinite loop from happening but reopens #6298 and #5689
---
.../wrappers/InlineComponent/index.ts | 2 +-
src/runtime/internal/Component.ts | 6 ++---
src/runtime/internal/scheduler.ts | 25 +++++++++++++++----
.../binding-indirect-value/Component.svelte | 6 +++++
.../_config.js | 3 ++-
.../binding-indirect-value/main.svelte | 8 ++++++
.../Tab.svelte | 0
.../_config.js | 10 ++++++++
.../main.svelte | 0
9 files changed, 49 insertions(+), 11 deletions(-)
create mode 100644 test/runtime/samples/binding-indirect-value/Component.svelte
rename test/runtime/samples/{binding-no-unnecessary-invalidation => binding-indirect-value}/_config.js (62%)
create mode 100644 test/runtime/samples/binding-indirect-value/main.svelte
rename test/runtime/samples/{binding-no-unnecessary-invalidation => binding-no-unnecessary-invalidation.skip}/Tab.svelte (100%)
create mode 100644 test/runtime/samples/binding-no-unnecessary-invalidation.skip/_config.js
rename test/runtime/samples/{binding-no-unnecessary-invalidation => binding-no-unnecessary-invalidation.skip}/main.svelte (100%)
diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
index f7875dc1b07d..bca01fdb4cd2 100644
--- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
@@ -393,7 +393,7 @@ export default class InlineComponentWrapper extends Wrapper {
component.partly_hoisted.push(body);
- return b`@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}, ${snippet}));`;
+ return b`@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${id}));`;
});
const munged_handlers = this.node.handlers.map(handler => {
diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts
index eedf8dd1ada4..5aec24c651f7 100644
--- a/src/runtime/internal/Component.ts
+++ b/src/runtime/internal/Component.ts
@@ -5,13 +5,11 @@ import { children, detach, start_hydrating, end_hydrating } from './dom';
import { transition_in } from './transitions';
import { T$$ } from './types';
-export function bind(component, name, callback, value) {
+export function bind(component, name, callback) {
const index = component.$$.props[name];
if (index !== undefined) {
component.$$.bound[index] = callback;
- if (value === undefined) {
- callback(component.$$.ctx[index]);
- }
+ callback(component.$$.ctx[index]);
}
}
diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts
index c0b8e57b08e8..f95ba446f550 100644
--- a/src/runtime/internal/scheduler.ts
+++ b/src/runtime/internal/scheduler.ts
@@ -52,17 +52,32 @@ export function add_flush_callback(fn) {
const seen_callbacks = new Set();
let flushidx = 0; // Do *not* move this inside the flush() function
export function flush() {
+ // Do not reenter flush while dirty components are updated, as this can
+ // result in an infinite loop. Instead, let the inner flush handle it.
+ // Reentrancy is ok afterwards for bindings etc.
+ if (flushidx !== 0) {
+ return;
+ }
+
const saved_component = current_component;
do {
// first, call beforeUpdate functions
// and update components
- while (flushidx < dirty_components.length) {
- const component = dirty_components[flushidx];
- flushidx++;
- set_current_component(component);
- update(component.$$);
+ try {
+ while (flushidx < dirty_components.length) {
+ const component = dirty_components[flushidx];
+ flushidx++;
+ set_current_component(component);
+ update(component.$$);
+ }
+ } catch (e) {
+ // reset dirty state to not end up in a deadlocked state and then rethrow
+ dirty_components.length = 0;
+ flushidx = 0;
+ throw e;
}
+
set_current_component(null);
dirty_components.length = 0;
diff --git a/test/runtime/samples/binding-indirect-value/Component.svelte b/test/runtime/samples/binding-indirect-value/Component.svelte
new file mode 100644
index 000000000000..25bf0d6758c6
--- /dev/null
+++ b/test/runtime/samples/binding-indirect-value/Component.svelte
@@ -0,0 +1,6 @@
+
+
+Child component "{value}"
diff --git a/test/runtime/samples/binding-no-unnecessary-invalidation/_config.js b/test/runtime/samples/binding-indirect-value/_config.js
similarity index 62%
rename from test/runtime/samples/binding-no-unnecessary-invalidation/_config.js
rename to test/runtime/samples/binding-indirect-value/_config.js
index a34cf0121f42..4eaf6839aa1e 100644
--- a/test/runtime/samples/binding-no-unnecessary-invalidation/_config.js
+++ b/test/runtime/samples/binding-indirect-value/_config.js
@@ -1,7 +1,8 @@
export default {
async test({ assert, target }) {
assert.htmlEqual(target.innerHTML, `
-
0
+ Parent component "bar"0
+ `); + } +}; diff --git a/test/runtime/samples/binding-no-unnecessary-invalidation/main.svelte b/test/runtime/samples/binding-no-unnecessary-invalidation.skip/main.svelte similarity index 100% rename from test/runtime/samples/binding-no-unnecessary-invalidation/main.svelte rename to test/runtime/samples/binding-no-unnecessary-invalidation.skip/main.svelte