diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index f7875dc1b0..bca01fdb4c 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 eedf8dd1ad..5aec24c651 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 c0b8e57b08..f95ba446f5 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 0000000000..25bf0d6758 --- /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 a34cf0121f..4eaf6839aa 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"
+ Child component "bar"
`); } }; diff --git a/test/runtime/samples/binding-indirect-value/main.svelte b/test/runtime/samples/binding-indirect-value/main.svelte new file mode 100644 index 0000000000..db2734c890 --- /dev/null +++ b/test/runtime/samples/binding-indirect-value/main.svelte @@ -0,0 +1,8 @@ + + +Parent component "{value}"
+ diff --git a/test/runtime/samples/binding-no-unnecessary-invalidation/Tab.svelte b/test/runtime/samples/binding-no-unnecessary-invalidation.skip/Tab.svelte similarity index 100% rename from test/runtime/samples/binding-no-unnecessary-invalidation/Tab.svelte rename to test/runtime/samples/binding-no-unnecessary-invalidation.skip/Tab.svelte diff --git a/test/runtime/samples/binding-no-unnecessary-invalidation.skip/_config.js b/test/runtime/samples/binding-no-unnecessary-invalidation.skip/_config.js new file mode 100644 index 0000000000..d7e553fd46 --- /dev/null +++ b/test/runtime/samples/binding-no-unnecessary-invalidation.skip/_config.js @@ -0,0 +1,10 @@ +// this test currently fails because the fix that made it pass broke other tests, +// see https://github.com/sveltejs/svelte/pull/8114 for more context. +export default { + skip: true, + async test({ assert, target }) { + assert.htmlEqual(target.innerHTML, ` +

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