differentiate between mutated and reassigned

pull/2011/head
Richard Harris 7 years ago
parent 2431780851
commit b284458cdf

@ -99,7 +99,7 @@ export default class Stats {
return { return {
timings, timings,
warnings: this.warnings, 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, name: variable.name,
export_name: variable.export_name || null, export_name: variable.export_name || null,
injected: variable.injected || false, injected: variable.injected || false,

@ -429,28 +429,16 @@ export default class Component {
if (node.type === 'ExportNamedDeclaration') { if (node.type === 'ExportNamedDeclaration') {
if (node.declaration) { if (node.declaration) {
const { kind } = node.declaration;
if (node.declaration.type === 'VariableDeclaration') { if (node.declaration.type === 'VariableDeclaration') {
node.declaration.declarations.forEach(declarator => { node.declaration.declarations.forEach(declarator => {
extractNames(declarator.id).forEach(name => { extractNames(declarator.id).forEach(name => {
const variable = this.var_lookup.get(name); const variable = this.var_lookup.get(name);
variable.export_name = name; variable.export_name = name;
if (kind !== 'const') variable.mutated = true;
}); });
}); });
} else { } else {
const { name } = node.declaration.id; 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); const variable = this.var_lookup.get(name);
variable.export_name = name; variable.export_name = name;
} }
@ -636,13 +624,17 @@ export default class Component {
walk(this.ast.instance.content, { walk(this.ast.instance.content, {
enter(node, parent) { enter(node, parent) {
let names;
if (map.has(node)) { if (map.has(node)) {
scope = map.get(node); scope = map.get(node);
} }
let names;
let deep = false;
if (node.type === 'AssignmentExpression') { if (node.type === 'AssignmentExpression') {
names = node.left.type === 'MemberExpression' deep = node.left.type === 'MemberExpression';
names = deep
? [getObject(node.left).name] ? [getObject(node.left).name]
: extractNames(node.left); : extractNames(node.left);
} else if (node.type === 'UpdateExpression') { } else if (node.type === 'UpdateExpression') {
@ -653,7 +645,7 @@ export default class Component {
names.forEach(name => { names.forEach(name => {
if (scope.findOwner(name) === instance_scope) { if (scope.findOwner(name) === instance_scope) {
const variable = component.var_lookup.get(name); 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 => { this.ast.instance.content.body.forEach(node => {
if (node.type === 'VariableDeclaration') { 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 => { node.declarations.forEach(d => {
const variable = this.var_lookup.get(d.id.name); const variable = this.var_lookup.get(d.id.name);
variable.hoistable = true; variable.hoistable = true;

@ -34,11 +34,11 @@ export default class Binding extends Node {
if (this.isContextual) { if (this.isContextual) {
scope.dependenciesForName.get(name).forEach(name => { scope.dependenciesForName.get(name).forEach(name => {
const variable = component.var_lookup.get(name); const variable = component.var_lookup.get(name);
variable.mutated = true; variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true;
}); });
} else { } else {
const variable = component.var_lookup.get(name); 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') { if (this.expression.node.type === 'MemberExpression') {

@ -44,7 +44,7 @@ export default class EventHandler extends Node {
component.add_var({ component.add_var({
name, name,
injected: true, internal: true,
referenced: true referenced: true
}); });

@ -149,30 +149,33 @@ export default class Expression {
} }
// track any assignments from template expressions as mutable // track any assignments from template expressions as mutable
let mutated; let names;
let deep = false;
if (function_expression) { if (function_expression) {
if (node.type === 'AssignmentExpression') { if (node.type === 'AssignmentExpression') {
mutated = node.left.type === 'MemberExpression' deep = node.left.type === 'MemberExpression';
names = deep
? [getObject(node.left).name] ? [getObject(node.left).name]
: extractNames(node.left); : extractNames(node.left);
} else if (node.type === 'UpdateExpression') { } else if (node.type === 'UpdateExpression') {
const { name } = getObject(node.argument); const { name } = getObject(node.argument);
mutated = [name]; names = [name];
} }
} }
if (mutated) { if (names) {
mutated.forEach(name => { names.forEach(name => {
if (template_scope.names.has(name)) { if (template_scope.names.has(name)) {
template_scope.dependenciesForName.get(name).forEach(name => { template_scope.dependenciesForName.get(name).forEach(name => {
const variable = component.var_lookup.get(name); const variable = component.var_lookup.get(name);
if (variable) variable.mutated = true; if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
}); });
} else { } else {
component.add_reference(name); component.add_reference(name);
const variable = component.var_lookup.get(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({ component.add_var({
name, name,
injected: true, internal: true,
hoistable: true, hoistable: true,
referenced: true referenced: true
}); });
@ -394,7 +397,7 @@ export default class Expression {
component.add_var({ component.add_var({
name, name,
injected: true, internal: true,
referenced: true referenced: true
}); });
} }
@ -406,7 +409,7 @@ export default class Expression {
component.add_var({ component.add_var({
name, name,
injected: true, internal: true,
referenced: true referenced: true
}); });

@ -181,9 +181,11 @@ export default class AwaitBlockWrapper extends Wrapper {
} }
const conditions = []; const conditions = [];
if (this.node.expression.dependencies.size > 0) { const dependencies = this.node.expression.dynamic_dependencies();
if (dependencies.length > 0) {
conditions.push( conditions.push(
`(${[...this.node.expression.dependencies].map(dep => `'${dep}' in changed`).join(' || ')})` `(${dependencies.map(dep => `'${dep}' in changed`).join(' || ')})`
); );
} }

@ -408,7 +408,7 @@ export default class ElementWrapper extends Wrapper {
renderer.component.add_var({ renderer.component.add_var({
name: handler, name: handler,
injected: true, internal: true,
referenced: true referenced: true
}); });
@ -513,7 +513,7 @@ export default class ElementWrapper extends Wrapper {
renderer.component.add_var({ renderer.component.add_var({
name, name,
injected: true, internal: true,
referenced: true referenced: true
}); });

@ -147,14 +147,16 @@ export default class InlineComponentWrapper extends Wrapper {
} }
const fragment_dependencies = new Set(); const fragment_dependencies = new Set();
this.slots.forEach((slot, name) => { this.slots.forEach(slot => {
slot.block.dependencies.forEach(name => { slot.block.dependencies.forEach(name => {
const is_let = slot.scope.is_let(name); const is_let = slot.scope.is_let(name);
const variable = renderer.component.var_lookup.get(name); const variable = renderer.component.var_lookup.get(name);
if (is_let || variable.mutated) { if (is_let) fragment_dependencies.add(name);
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({ component.add_var({
name: fn, name: fn,
injected: true, internal: true,
referenced: true referenced: true
}); });
@ -276,7 +278,7 @@ export default class InlineComponentWrapper extends Wrapper {
component.add_var({ component.add_var({
name, name,
injected: true, internal: true,
referenced: true referenced: true
}); });

@ -120,7 +120,7 @@ export default class WindowWrapper extends Wrapper {
component.add_var({ component.add_var({
name: handler_name, name: handler_name,
injected: true, internal: true,
referenced: true referenced: true
}); });

@ -91,7 +91,8 @@ export interface Var {
// used internally, but not exposed // used internally, but not exposed
global?: boolean; global?: boolean;
implicit?: boolean; implicit?: boolean; // logic-less template references
internal?: boolean; // event handlers, bindings
initialised?: boolean; initialised?: boolean;
hoistable?: boolean; hoistable?: boolean;
} }

@ -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
}
]);
}
};

@ -0,0 +1,7 @@
<script>
let count;
const user = { name: 'world' };
</script>
<input bind:value={user.name}>
<input type=number bind:value={count}>

@ -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
}
]);
}
};

@ -0,0 +1,7 @@
<script>
let count;
const user = { name: 'world' };
</script>
<input on:input="{e => user.name = e.value}">
<input type=number on:input="{e => count = +e.value}">

@ -6,7 +6,7 @@ export default {
export_name: 'name', export_name: 'name',
injected: false, injected: false,
module: false, module: false,
mutated: true, mutated: false,
reassigned: false, reassigned: false,
referenced: true, referenced: true,
writable: true writable: true
@ -16,7 +16,7 @@ export default {
export_name: 'cats', export_name: 'cats',
injected: false, injected: false,
module: false, module: false,
mutated: true, mutated: false,
reassigned: false, reassigned: false,
referenced: true, referenced: true,
writable: true writable: true
@ -36,8 +36,8 @@ export default {
export_name: null, export_name: null,
injected: false, injected: false,
module: false, module: false,
mutated: true, mutated: false,
reassigned: false, reassigned: true,
referenced: true, referenced: true,
writable: true writable: true
} }

Loading…
Cancel
Save