From b284458cdfe87217a183934c0d936b6278e4a781 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Mon, 28 Jan 2019 22:04:41 -0500 Subject: [PATCH] differentiate between mutated and reassigned --- src/Stats.ts | 2 +- src/compile/Component.ts | 24 ++++++----------- src/compile/nodes/Binding.ts | 4 +-- src/compile/nodes/EventHandler.ts | 2 +- src/compile/nodes/shared/Expression.ts | 23 +++++++++------- src/compile/render-dom/wrappers/AwaitBlock.ts | 6 +++-- .../render-dom/wrappers/Element/index.ts | 4 +-- .../wrappers/InlineComponent/index.ts | 14 +++++----- src/compile/render-dom/wrappers/Window.ts | 2 +- src/interfaces.ts | 3 ++- .../mutated-vs-reassigned-bindings/_config.js | 26 +++++++++++++++++++ .../mutated-vs-reassigned-bindings/input.html | 7 +++++ .../samples/mutated-vs-reassigned/_config.js | 26 +++++++++++++++++++ .../samples/mutated-vs-reassigned/input.html | 7 +++++ test/stats/samples/props/_config.js | 8 +++--- 15 files changed, 112 insertions(+), 46 deletions(-) create mode 100644 test/stats/samples/mutated-vs-reassigned-bindings/_config.js create mode 100644 test/stats/samples/mutated-vs-reassigned-bindings/input.html create mode 100644 test/stats/samples/mutated-vs-reassigned/_config.js create mode 100644 test/stats/samples/mutated-vs-reassigned/input.html diff --git a/src/Stats.ts b/src/Stats.ts index bec51d5a2d..5236e6a85c 100644 --- a/src/Stats.ts +++ b/src/Stats.ts @@ -99,7 +99,7 @@ export default class Stats { return { timings, warnings: this.warnings, - vars: component.vars.filter(variable => !variable.global && !variable.implicit).map(variable => ({ + vars: component.vars.filter(variable => !variable.global && !variable.implicit && !variable.internal).map(variable => ({ name: variable.name, export_name: variable.export_name || null, injected: variable.injected || false, diff --git a/src/compile/Component.ts b/src/compile/Component.ts index e4fe24fd68..42d3f7a449 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -429,28 +429,16 @@ export default class Component { if (node.type === 'ExportNamedDeclaration') { if (node.declaration) { - const { kind } = node.declaration; - if (node.declaration.type === 'VariableDeclaration') { node.declaration.declarations.forEach(declarator => { extractNames(declarator.id).forEach(name => { const variable = this.var_lookup.get(name); variable.export_name = name; - if (kind !== 'const') variable.mutated = true; }); }); } else { const { name } = node.declaration.id; - const kind = node.declaration.type === 'ClassDeclaration' - ? 'class' - : node.declaration.type === 'FunctionDeclaration' - ? 'function' - : null; - - // sanity check - if (!kind) throw new Error(`Unknown declaration type ${node.declaration.type}`); - const variable = this.var_lookup.get(name); variable.export_name = name; } @@ -636,13 +624,17 @@ export default class Component { walk(this.ast.instance.content, { enter(node, parent) { - let names; if (map.has(node)) { scope = map.get(node); } + let names; + let deep = false; + if (node.type === 'AssignmentExpression') { - names = node.left.type === 'MemberExpression' + deep = node.left.type === 'MemberExpression'; + + names = deep ? [getObject(node.left).name] : extractNames(node.left); } else if (node.type === 'UpdateExpression') { @@ -653,7 +645,7 @@ export default class Component { names.forEach(name => { if (scope.findOwner(name) === instance_scope) { const variable = component.var_lookup.get(name); - variable.mutated = true; + variable[deep ? 'mutated' : 'reassigned'] = true; } }); } @@ -836,7 +828,7 @@ export default class Component { this.ast.instance.content.body.forEach(node => { if (node.type === 'VariableDeclaration') { - if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.var_lookup.get(d.id.name).mutated)) { + if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.var_lookup.get(d.id.name).reassigned)) { node.declarations.forEach(d => { const variable = this.var_lookup.get(d.id.name); variable.hoistable = true; diff --git a/src/compile/nodes/Binding.ts b/src/compile/nodes/Binding.ts index 85fcf7cf2d..213314c3cb 100644 --- a/src/compile/nodes/Binding.ts +++ b/src/compile/nodes/Binding.ts @@ -34,11 +34,11 @@ export default class Binding extends Node { if (this.isContextual) { scope.dependenciesForName.get(name).forEach(name => { const variable = component.var_lookup.get(name); - variable.mutated = true; + variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true; }); } else { const variable = component.var_lookup.get(name); - variable.mutated = true; + variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true; } if (this.expression.node.type === 'MemberExpression') { diff --git a/src/compile/nodes/EventHandler.ts b/src/compile/nodes/EventHandler.ts index 797442893e..e426efa79c 100644 --- a/src/compile/nodes/EventHandler.ts +++ b/src/compile/nodes/EventHandler.ts @@ -44,7 +44,7 @@ export default class EventHandler extends Node { component.add_var({ name, - injected: true, + internal: true, referenced: true }); diff --git a/src/compile/nodes/shared/Expression.ts b/src/compile/nodes/shared/Expression.ts index eea1235e42..ea2237543a 100644 --- a/src/compile/nodes/shared/Expression.ts +++ b/src/compile/nodes/shared/Expression.ts @@ -149,30 +149,33 @@ export default class Expression { } // track any assignments from template expressions as mutable - let mutated; + let names; + let deep = false; + if (function_expression) { if (node.type === 'AssignmentExpression') { - mutated = node.left.type === 'MemberExpression' + deep = node.left.type === 'MemberExpression'; + names = deep ? [getObject(node.left).name] : extractNames(node.left); } else if (node.type === 'UpdateExpression') { const { name } = getObject(node.argument); - mutated = [name]; + names = [name]; } } - if (mutated) { - mutated.forEach(name => { + if (names) { + names.forEach(name => { if (template_scope.names.has(name)) { template_scope.dependenciesForName.get(name).forEach(name => { const variable = component.var_lookup.get(name); - if (variable) variable.mutated = true; + if (variable) variable[deep ? 'mutated' : 'reassigned'] = true; }); } else { component.add_reference(name); const variable = component.var_lookup.get(name); - if (variable) variable.mutated = true; + if (variable) variable[deep ? 'mutated' : 'reassigned'] = true; } }); } @@ -381,7 +384,7 @@ export default class Expression { component.add_var({ name, - injected: true, + internal: true, hoistable: true, referenced: true }); @@ -394,7 +397,7 @@ export default class Expression { component.add_var({ name, - injected: true, + internal: true, referenced: true }); } @@ -406,7 +409,7 @@ export default class Expression { component.add_var({ name, - injected: true, + internal: true, referenced: true }); diff --git a/src/compile/render-dom/wrappers/AwaitBlock.ts b/src/compile/render-dom/wrappers/AwaitBlock.ts index 22247a14e1..e896510fa8 100644 --- a/src/compile/render-dom/wrappers/AwaitBlock.ts +++ b/src/compile/render-dom/wrappers/AwaitBlock.ts @@ -181,9 +181,11 @@ export default class AwaitBlockWrapper extends Wrapper { } const conditions = []; - if (this.node.expression.dependencies.size > 0) { + const dependencies = this.node.expression.dynamic_dependencies(); + + if (dependencies.length > 0) { conditions.push( - `(${[...this.node.expression.dependencies].map(dep => `'${dep}' in changed`).join(' || ')})` + `(${dependencies.map(dep => `'${dep}' in changed`).join(' || ')})` ); } diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 78ede56473..71d5bd2831 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -408,7 +408,7 @@ export default class ElementWrapper extends Wrapper { renderer.component.add_var({ name: handler, - injected: true, + internal: true, referenced: true }); @@ -513,7 +513,7 @@ export default class ElementWrapper extends Wrapper { renderer.component.add_var({ name, - injected: true, + internal: true, referenced: true }); diff --git a/src/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compile/render-dom/wrappers/InlineComponent/index.ts index 7e2503025a..f4dfe6f123 100644 --- a/src/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts @@ -147,14 +147,16 @@ export default class InlineComponentWrapper extends Wrapper { } const fragment_dependencies = new Set(); - this.slots.forEach((slot, name) => { + this.slots.forEach(slot => { slot.block.dependencies.forEach(name => { const is_let = slot.scope.is_let(name); const variable = renderer.component.var_lookup.get(name); - if (is_let || variable.mutated) { - fragment_dependencies.add(name); - } + if (is_let) fragment_dependencies.add(name); + + if (!variable) return; + if (variable.mutated || variable.reassigned) fragment_dependencies.add(name); + if (!variable.module && variable.writable && variable.export_name) fragment_dependencies.add(name); }); }); @@ -241,7 +243,7 @@ export default class InlineComponentWrapper extends Wrapper { component.add_var({ name: fn, - injected: true, + internal: true, referenced: true }); @@ -276,7 +278,7 @@ export default class InlineComponentWrapper extends Wrapper { component.add_var({ name, - injected: true, + internal: true, referenced: true }); diff --git a/src/compile/render-dom/wrappers/Window.ts b/src/compile/render-dom/wrappers/Window.ts index 16995b23ea..18a15dcd77 100644 --- a/src/compile/render-dom/wrappers/Window.ts +++ b/src/compile/render-dom/wrappers/Window.ts @@ -120,7 +120,7 @@ export default class WindowWrapper extends Wrapper { component.add_var({ name: handler_name, - injected: true, + internal: true, referenced: true }); diff --git a/src/interfaces.ts b/src/interfaces.ts index a14cc1a58a..157ad56eb7 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -91,7 +91,8 @@ export interface Var { // used internally, but not exposed global?: boolean; - implicit?: boolean; + implicit?: boolean; // logic-less template references + internal?: boolean; // event handlers, bindings initialised?: boolean; hoistable?: boolean; } \ No newline at end of file diff --git a/test/stats/samples/mutated-vs-reassigned-bindings/_config.js b/test/stats/samples/mutated-vs-reassigned-bindings/_config.js new file mode 100644 index 0000000000..a1d027cbb5 --- /dev/null +++ b/test/stats/samples/mutated-vs-reassigned-bindings/_config.js @@ -0,0 +1,26 @@ +export default { + test(assert, stats) { + assert.deepEqual(stats.vars, [ + { + name: 'count', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: true, + referenced: true, + writable: true + }, + { + name: 'user', + export_name: null, + injected: false, + module: false, + mutated: true, + reassigned: false, + referenced: true, + writable: false + } + ]); + } +}; \ No newline at end of file diff --git a/test/stats/samples/mutated-vs-reassigned-bindings/input.html b/test/stats/samples/mutated-vs-reassigned-bindings/input.html new file mode 100644 index 0000000000..c1186ad3a7 --- /dev/null +++ b/test/stats/samples/mutated-vs-reassigned-bindings/input.html @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/test/stats/samples/mutated-vs-reassigned/_config.js b/test/stats/samples/mutated-vs-reassigned/_config.js new file mode 100644 index 0000000000..a1d027cbb5 --- /dev/null +++ b/test/stats/samples/mutated-vs-reassigned/_config.js @@ -0,0 +1,26 @@ +export default { + test(assert, stats) { + assert.deepEqual(stats.vars, [ + { + name: 'count', + export_name: null, + injected: false, + module: false, + mutated: false, + reassigned: true, + referenced: true, + writable: true + }, + { + name: 'user', + export_name: null, + injected: false, + module: false, + mutated: true, + reassigned: false, + referenced: true, + writable: false + } + ]); + } +}; \ No newline at end of file diff --git a/test/stats/samples/mutated-vs-reassigned/input.html b/test/stats/samples/mutated-vs-reassigned/input.html new file mode 100644 index 0000000000..6a5e604c26 --- /dev/null +++ b/test/stats/samples/mutated-vs-reassigned/input.html @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/test/stats/samples/props/_config.js b/test/stats/samples/props/_config.js index e966c7fcfb..cbec831f61 100644 --- a/test/stats/samples/props/_config.js +++ b/test/stats/samples/props/_config.js @@ -6,7 +6,7 @@ export default { export_name: 'name', injected: false, module: false, - mutated: true, + mutated: false, reassigned: false, referenced: true, writable: true @@ -16,7 +16,7 @@ export default { export_name: 'cats', injected: false, module: false, - mutated: true, + mutated: false, reassigned: false, referenced: true, writable: true @@ -36,8 +36,8 @@ export default { export_name: null, injected: false, module: false, - mutated: true, - reassigned: false, + mutated: false, + reassigned: true, referenced: true, writable: true }