fix: loop-guard scope leak

pull/4084/head
Tan Li Hau 5 years ago committed by Conduitry
parent 6a4956b403
commit 2c9d864e33

@ -344,7 +344,7 @@ export default class Component {
}; };
} }
get_unique_name(name: string): Identifier { get_unique_name(name: string, scope?: Scope): Identifier {
if (test) name = `${name}$`; if (test) name = `${name}$`;
let alias = name; let alias = name;
for ( for (
@ -352,7 +352,8 @@ export default class Component {
reserved.has(alias) || reserved.has(alias) ||
this.var_lookup.has(alias) || this.var_lookup.has(alias) ||
this.used_names.has(alias) || this.used_names.has(alias) ||
this.globally_used_names.has(alias); this.globally_used_names.has(alias) ||
(scope && scope.has(alias));
alias = `${name}_${i++}` alias = `${name}_${i++}`
); );
this.used_names.add(alias); this.used_names.add(alias);
@ -707,8 +708,7 @@ export default class Component {
const remove = (parent, prop, index) => { const remove = (parent, prop, index) => {
to_remove.unshift([parent, prop, index]); to_remove.unshift([parent, prop, index]);
}; };
let scope_updated = false;
const to_insert = new Map();
walk(content, { walk(content, {
enter(node, parent, prop, index) { enter(node, parent, prop, index) {
@ -735,37 +735,21 @@ export default class Component {
} }
component.warn_on_undefined_store_value_references(node, parent, scope); component.warn_on_undefined_store_value_references(node, parent, scope);
},
leave(node) {
// do it on leave, to prevent infinite loop
if (component.compile_options.dev && component.compile_options.loopGuardTimeout > 0) { if (component.compile_options.dev && component.compile_options.loopGuardTimeout > 0) {
const to_insert_for_loop_protect = component.loop_protect(node, prop, index, component.compile_options.loopGuardTimeout); const to_replace_for_loop_protect = component.loop_protect(node, scope, component.compile_options.loopGuardTimeout);
if (to_insert_for_loop_protect) { if (to_replace_for_loop_protect) {
if (!Array.isArray(parent[prop])) { this.replace(to_replace_for_loop_protect);
parent[prop] = { scope_updated = true;
type: 'BlockStatement',
body: [to_insert_for_loop_protect.node, node],
};
} else {
// can't insert directly, will screw up the index in the for-loop of estree-walker
if (!to_insert.has(parent)) {
to_insert.set(parent, []);
}
to_insert.get(parent).push(to_insert_for_loop_protect);
}
} }
} }
},
leave(node) {
if (map.has(node)) { if (map.has(node)) {
scope = scope.parent; scope = scope.parent;
} }
if (to_insert.has(node)) {
const nodes_to_insert = to_insert.get(node);
for (const { index, prop, node: node_to_insert } of nodes_to_insert.reverse()) {
node[prop].splice(index, 0, node_to_insert);
}
to_insert.delete(node);
}
}, },
}); });
@ -778,6 +762,12 @@ export default class Component {
} }
} }
} }
if (scope_updated) {
const { scope, map } = create_scopes(script.content);
this.instance_scope = scope;
this.instance_scope_map = map;
}
} }
track_references_and_mutations() { track_references_and_mutations() {
@ -849,15 +839,12 @@ export default class Component {
} }
} }
loop_protect(node, prop, index, timeout) { loop_protect(node, scope: Scope, timeout: number): Node | null {
if (node.type === 'WhileStatement' || if (node.type === 'WhileStatement' ||
node.type === 'ForStatement' || node.type === 'ForStatement' ||
node.type === 'DoWhileStatement') { node.type === 'DoWhileStatement') {
const guard = this.get_unique_name('guard'); const guard = this.get_unique_name('guard', scope);
this.add_var({ this.used_names.add(guard.name);
name: guard.name,
internal: true,
});
const before = b`const ${guard} = @loop_guard(${timeout})`; const before = b`const ${guard} = @loop_guard(${timeout})`;
const inside = b`${guard}();`; const inside = b`${guard}();`;
@ -870,7 +857,14 @@ export default class Component {
}; };
} }
node.body.body.push(inside[0]); node.body.body.push(inside[0]);
return { index, prop, node: before[0] };
return {
type: 'BlockStatement',
body: [
before[0],
node,
],
};
} }
return null; return null;
} }

@ -1,8 +1,13 @@
/* generated by Svelte vX.Y.Z */ /* generated by Svelte vX.Y.Z */
import { import {
SvelteComponentDev, SvelteComponentDev,
add_location,
binding_callbacks,
detach_dev,
dispatch_dev, dispatch_dev,
element,
init, init,
insert_dev,
loop_guard, loop_guard,
noop, noop,
safe_not_equal safe_not_equal
@ -11,16 +16,27 @@ import {
const file = undefined; const file = undefined;
function create_fragment(ctx) { function create_fragment(ctx) {
let div;
const block = { const block = {
c: noop, c: function create() {
div = element("div");
add_location(div, file, 22, 0, 288);
},
l: function claim(nodes) { l: function claim(nodes) {
throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option"); throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");
}, },
m: noop, m: function mount(target, anchor) {
insert_dev(target, div, anchor);
/*div_binding*/ ctx[1](div);
},
p: noop, p: noop,
i: noop, i: noop,
o: noop, o: noop,
d: noop d: function destroy(detaching) {
if (detaching) detach_dev(div);
/*div_binding*/ ctx[1](null);
}
}; };
dispatch_dev("SvelteRegisterBlock", { dispatch_dev("SvelteRegisterBlock", {
@ -34,62 +50,91 @@ function create_fragment(ctx) {
return block; return block;
} }
function instance($$self) { function foo() {
const guard = loop_guard(100); const guard = "foo";
{
const guard_1 = loop_guard(100);
while (true) { while (true) {
foo(); console.log(guard);
guard(); guard_1();
}
} }
}
function instance($$self, $$props, $$invalidate) {
let node;
const guard_1 = loop_guard(100); {
const guard = loop_guard(100);
for (; ; ) { while (true) {
foo(); foo();
guard_1(); guard();
}
} }
const guard_2 = loop_guard(100); {
const guard_2 = loop_guard(100);
while (true) { for (; ; ) {
foo(); foo();
guard_2(); guard_2();
}
} }
const guard_4 = loop_guard(100); {
const guard_3 = loop_guard(100);
do { while (true) {
foo(); foo();
guard_4(); guard_3();
} while (true); }
}
{
const guard_5 = loop_guard(100);
do {
foo();
guard_5();
} while (true);
}
function div_binding($$value) {
binding_callbacks[$$value ? "unshift" : "push"](() => {
$$invalidate(0, node = $$value);
});
}
$$self.$capture_state = () => { $$self.$capture_state = () => {
return {}; return {};
}; };
$$self.$inject_state = $$props => { $$self.$inject_state = $$props => {
if ("node" in $$props) $$invalidate(0, node = $$props.node);
}; };
$: { $: {
const guard_3 = loop_guard(100); const guard_4 = loop_guard(100);
while (true) { while (true) {
foo(); foo();
guard_3(); guard_4();
} }
} }
$: { $: {
const guard_5 = loop_guard(100); const guard_6 = loop_guard(100);
do { do {
foo(); foo();
guard_5(); guard_6();
} while (true); } while (true);
} }
return []; return [node, div_binding];
} }
class Component extends SvelteComponentDev { class Component extends SvelteComponentDev {

@ -1,4 +1,13 @@
<script> <script>
let node;
function foo() {
const guard = 'foo';
while(true) {
console.log(guard);
}
}
while(true) { while(true) {
foo(); foo();
} }
@ -9,4 +18,6 @@
$: while(true) foo(); $: while(true) foo();
do foo(); while(true); do foo(); while(true);
$: do foo(); while(true); $: do foo(); while(true);
</script> </script>
<div bind:this={node}></div>

@ -0,0 +1,7 @@
export default {
html: '<div></div>',
compileOptions: {
dev: true,
loopGuardTimeout: 100,
}
};

@ -0,0 +1,7 @@
<script>
let node;
(function () {
while(false) ;
}());
</script>
<div bind:this={node}></div>
Loading…
Cancel
Save