Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Bitmask-based change tracking #3945

Merged
merged 46 commits into from
Nov 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
17f0e42
start updating tests
Rich-Harris Nov 16, 2019
319539e
Merge branch 'master' into bitmasks
Rich-Harris Nov 17, 2019
669101d
Merge branch 'master' into bitmasks
Rich-Harris Nov 17, 2019
7118318
start implementing bitmask-based change tracking (#1943)
Rich-Harris Nov 17, 2019
1a81878
oops
Rich-Harris Nov 17, 2019
1d7dd6a
fix some await block stuff
Rich-Harris Nov 18, 2019
ab8ca08
slots
Rich-Harris Nov 18, 2019
773a780
Merge branch 'master' into bitmasks
Rich-Harris Nov 18, 2019
a74cc69
reactive declarations
Rich-Harris Nov 18, 2019
6c3c3bb
component bindings etc
Rich-Harris Nov 18, 2019
12b9733
start fixing slots
Rich-Harris Nov 18, 2019
b7634b8
fix store value invalidations
Rich-Harris Nov 18, 2019
0f7a42d
slot stuff
Rich-Harris Nov 18, 2019
ec27044
fixes
Rich-Harris Nov 18, 2019
bf49717
fix
Rich-Harris Nov 18, 2019
c59c153
fixes
Rich-Harris Nov 18, 2019
b6d6053
fix some slot stuff
Rich-Harris Nov 19, 2019
a8dca8c
fix some invalidations
Rich-Harris Nov 19, 2019
3ff1cff
fix if blocks
Rich-Harris Nov 19, 2019
acdf892
fix a test
Rich-Harris Nov 19, 2019
904ca7d
destructuring in lets
Rich-Harris Nov 19, 2019
3cd8f35
fix shadowing
Rich-Harris Nov 19, 2019
e79ce79
fix if block case
Rich-Harris Nov 19, 2019
23f62d1
all runtime tests passinfg
Rich-Harris Nov 19, 2019
2c5ddcd
almost all tests passing
Rich-Harris Nov 19, 2019
b604f98
update tests
Rich-Harris Nov 19, 2019
7e7e9a9
never hoist writable vars in dev mode, fix debug statements
Rich-Harris Nov 19, 2019
a5d5fd4
beef up shadowing test
Rich-Harris Nov 19, 2019
7963b4b
always use renderer.reference
Rich-Harris Nov 20, 2019
4338d2e
fix sourcemaps
Rich-Harris Nov 20, 2019
3d8bd5d
ugh so close
Rich-Harris Nov 20, 2019
007fb99
all tests passing. phase one complete, i guess
Rich-Harris Nov 21, 2019
128b066
add test for component with more than 31 dynamic values
Rich-Harris Nov 21, 2019
aa6ad39
stable sort
Rich-Harris Nov 21, 2019
32421f6
stable sort that preserves order
Rich-Harris Nov 21, 2019
d475162
linting
Rich-Harris Nov 21, 2019
ba9d5e1
failing test for bitmask overflow
Rich-Harris Nov 21, 2019
0ad02a5
ok i think this is it
Rich-Harris Nov 22, 2019
fb732e5
lint
Rich-Harris Nov 22, 2019
9659602
rename changed to dirty, for more internal consistency
Rich-Harris Nov 22, 2019
b574b70
update tests
Rich-Harris Nov 22, 2019
8c50a44
use bitwise comparison
Rich-Harris Nov 22, 2019
b8a829e
add comments... sort of
Rich-Harris Nov 22, 2019
32099d9
update tests
Rich-Harris Nov 22, 2019
e0eddb5
moar comments
Rich-Harris Nov 22, 2019
bb17583
I don't know what happened to these tests
Conduitry Nov 22, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,7 @@ module.exports = {
argsIgnorePattern: '^_'
}
],
'@typescript-eslint/no-object-literal-type-assertion': [
'error',
{
allowAsParameter: true
}
],
'@typescript-eslint/no-object-literal-type-assertion': 'off',
'@typescript-eslint/no-unused-vars': 'off'
},
globals: {
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"acorn": "^7.1.0",
"agadoo": "^1.1.0",
"c8": "^5.0.1",
"code-red": "0.0.25",
"code-red": "0.0.26",
"codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0",
Expand Down
68 changes: 4 additions & 64 deletions src/compiler/compile/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ export default class Component {
});

const subscribable_name = name.slice(1);
this.add_reference(subscribable_name);

const variable = this.var_lookup.get(subscribable_name);
if (variable) variable.subscribable = true;
Expand Down Expand Up @@ -889,50 +888,6 @@ export default class Component {
return null;
}

invalidate(name, value?) {
const variable = this.var_lookup.get(name);

if (variable && (variable.subscribable && (variable.reassigned || variable.export_name))) {
return x`${`$$subscribe_${name}`}($$invalidate('${name}', ${value || name}))`;
}

if (name[0] === '$' && name[1] !== '$') {
return x`${name.slice(1)}.set(${value || name})`;
}

if (
variable &&
!variable.referenced &&
!variable.is_reactive_dependency &&
!variable.export_name &&
!name.startsWith('$$')
) {
return value || name;
}

if (value) {
return x`$$invalidate('${name}', ${value})`;
}

// if this is a reactive declaration, invalidate dependencies recursively
const deps = new Set([name]);

deps.forEach(name => {
const reactive_declarations = this.reactive_declarations.filter(x =>
x.assignees.has(name)
);
reactive_declarations.forEach(declaration => {
declaration.dependencies.forEach(name => {
deps.add(name);
});
});
});

return Array.from(deps)
.map(n => x`$$invalidate('${n}', ${n})`)
.reduce((lhs, rhs) => x`${lhs}, ${rhs}}`);
}

rewrite_props(get_insert: (variable: Var) => Node[]) {
if (!this.ast.instance) return;

Expand Down Expand Up @@ -1054,6 +1009,10 @@ export default class Component {
if (!d.init) return false;
if (d.init.type !== 'Literal') return false;

// everything except const values can be changed by e.g. svelte devtools
// which means we can't hoist it
if (node.kind !== 'const' && this.compile_options.dev) return false;

const { name } = d.id as Identifier;

const v = this.var_lookup.get(name);
Expand Down Expand Up @@ -1325,25 +1284,6 @@ export default class Component {
});
}

qualify(name) {
if (name === `$$props`) return x`#ctx.$$props`;

let [head, ...tail] = name.split('.');

const variable = this.var_lookup.get(head);

if (variable) {
this.add_reference(name); // TODO we can probably remove most other occurrences of this

if (!variable.hoistable) {
tail.unshift(head);
head = '#ctx';
}
}

return [head, ...tail].reduce((lhs, rhs) => x`${lhs}.${rhs}`);
}

warn_if_undefined(name: string, node, template_scope: TemplateScope) {
if (name[0] === '$') {
if (name === '$' || name[1] === '$' && name !== '$$props') {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/nodes/Action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class Action extends Node {
component.warn_if_undefined(info.name, info, scope);

this.name = info.name;
component.qualify(info.name);
component.add_reference(info.name.split('.')[0]);

this.expression = info.expression
? new Expression(component, this, scope, info.expression)
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/nodes/Animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class Animation extends Node {
component.warn_if_undefined(info.name, info, scope);

this.name = info.name;
component.qualify(info.name);
component.add_reference(info.name.split('.')[0]);

if (parent.animation) {
component.error(this, {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/nodes/Transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default class Transition extends Node {
component.warn_if_undefined(info.name, info, scope);

this.name = info.name;
component.qualify(info.name);
component.add_reference(info.name.split('.')[0]);

this.directive = info.intro && info.outro ? 'transition' : info.intro ? 'in' : 'out';
this.is_local = info.modifiers.includes('local');
Expand Down
45 changes: 21 additions & 24 deletions src/compiler/compile/nodes/shared/Expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import TemplateScope from './TemplateScope';
import get_object from '../../utils/get_object';
import Block from '../../render_dom/Block';
import is_dynamic from '../../render_dom/wrappers/shared/is_dynamic';
import { x, b, p } from 'code-red';
import { invalidate } from '../../utils/invalidate';
import { Node, FunctionExpression } from 'estree';
import { b } from 'code-red';
import { invalidate } from '../../render_dom/invalidate';
import { Node, FunctionExpression, Identifier } from 'estree';
import { TemplateNode } from '../../../interfaces';

type Owner = Wrapper | TemplateNode;
Expand Down Expand Up @@ -213,7 +213,8 @@ export default class Expression {
component.add_reference(name); // TODO is this redundant/misplaced?
}
} else if (is_contextual(component, template_scope, name)) {
this.replace(x`#ctx.${node}`);
const reference = block.renderer.reference(node);
this.replace(reference);
}

this.skip();
Expand Down Expand Up @@ -260,42 +261,38 @@ export default class Expression {
// function can be hoisted inside the component init
component.partly_hoisted.push(declaration);

this.replace(x`#ctx.${id}` as any);

component.add_var({
name: id.name,
internal: true,
referenced: true
});
block.renderer.add_to_context(id.name);
this.replace(block.renderer.reference(id));
}

else {
// we need a combo block/init recipe
(node as FunctionExpression).params.unshift({
type: 'ObjectPattern',
properties: Array.from(contextual_dependencies).map(name => p`${name}` as any)
});
const deps = Array.from(contextual_dependencies);

(node as FunctionExpression).params = [
...deps.map(name => ({ type: 'Identifier', name } as Identifier)),
...(node as FunctionExpression).params
];

const context_args = deps.map(name => block.renderer.reference(name));

component.partly_hoisted.push(declaration);

this.replace(id as any);
block.renderer.add_to_context(id.name);
const callee = block.renderer.reference(id);

component.add_var({
name: id.name,
internal: true,
referenced: true
});
this.replace(id as any);

if ((node as FunctionExpression).params.length > 0) {
declarations.push(b`
function ${id}(...args) {
return #ctx.${id}(#ctx, ...args);
return ${callee}(${context_args}, ...args);
}
`);
} else {
declarations.push(b`
function ${id}() {
return #ctx.${id}(#ctx);
return ${callee}(${context_args});
}
`);
}
Expand Down Expand Up @@ -329,7 +326,7 @@ export default class Expression {
}
});

this.replace(invalidate(component, scope, node, traced));
this.replace(invalidate(block.renderer, scope, node, traced));
}
}
});
Expand Down
14 changes: 9 additions & 5 deletions src/compiler/compile/render_dom/Block.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Renderer from './Renderer';
import Wrapper from './wrappers/shared/Wrapper';
import { b, x } from 'code-red';
import { Node, Identifier } from 'estree';
import { Node, Identifier, ArrayPattern } from 'estree';
import { is_head } from './wrappers/shared/is_head';

export interface BlockOptions {
Expand Down Expand Up @@ -34,7 +34,7 @@ export default class Block {
key: Identifier;
first: Identifier;

dependencies: Set<string>;
dependencies: Set<string> = new Set();

bindings: Map<string, {
object: Identifier;
Expand Down Expand Up @@ -90,8 +90,6 @@ export default class Block {
this.key = options.key;
this.first = null;

this.dependencies = new Set();

this.bindings = options.bindings;

this.chunks = {
Expand Down Expand Up @@ -302,7 +300,13 @@ export default class Block {
properties.update = noop;
} else {
const ctx = this.maintain_context ? x`#new_ctx` : x`#ctx`;
properties.update = x`function #update(#changed, ${ctx}) {

let dirty: Identifier | ArrayPattern = { type: 'Identifier', name: '#dirty' };
if (!this.renderer.context_overflow && !this.parent) {
dirty = { type: 'ArrayPattern', elements: [dirty] };
}

properties.update = x`function #update(${ctx}, ${dirty}) {
${this.maintain_context && b`#ctx = ${ctx};`}
${this.chunks.update}
}`;
Expand Down
Loading