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