ok i think this is it

pull/3945/head
Rich Harris 6 years ago
parent ba9d5e16a3
commit 0ad02a5097

@ -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 {
@ -300,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(${ctx}, #changed) {
let changed: Identifier | ArrayPattern = { type: 'Identifier', name: '#changed' };
if (!this.renderer.context_overflow && !this.parent) {
changed = { type: 'ArrayPattern', elements: [changed] }
}
properties.update = x`function #update(${ctx}, ${changed}) {
${this.maintain_context && b`#ctx = ${ctx};`}
${this.chunks.update}
}`;

@ -3,7 +3,7 @@ import { CompileOptions, Var } from '../../interfaces';
import Component from '../Component';
import FragmentWrapper from './wrappers/Fragment';
import { x } from 'code-red';
import { Node, Identifier, MemberExpression, Literal } from 'estree';
import { Node, Identifier, MemberExpression, Literal, Expression, BinaryExpression } from 'estree';
import flatten_reference from '../utils/flatten_reference';
interface ContextMember {
@ -21,6 +21,7 @@ export default class Renderer {
context: ContextMember[] = [];
context_lookup: Map<string, ContextMember> = new Map();
context_overflow: boolean;
blocks: Array<Block | Node | Node[]> = [];
readonly: Set<string> = new Set();
meta_bindings: Array<Node | Node[]> = []; // initial values for e.g. window.innerWidth, if there's a <svelte:window> meta tag
@ -80,6 +81,8 @@ export default class Renderer {
null
);
this.context_overflow = this.context.length > 31;
// TODO messy
this.blocks.forEach(block => {
if (block instanceof Block) {
@ -189,50 +192,67 @@ export default class Renderer {
.reduce((lhs, rhs) => x`${lhs}, ${rhs}}`);
}
get_bitmask(names) {
const { context_lookup } = this;
changed(names, is_reactive_declaration = false): Expression {
const renderer = this;
// names.forEach(name => {
// if (!context_lookup.has(name)) {
// console.log({ names });
// throw new Error(`wut ${name}`);
// }
// });
let changed = (is_reactive_declaration
? x`$$self.$$.dirty`
: x`#changed`) as Identifier | MemberExpression;
return {
type: 'Literal',
const get_bitmask = () => names.reduce((bits, name) => {
const member = renderer.context_lookup.get(name);
// we need to use a getter so that bitmasks can be determined
// lazily, once context has fully shaken out. TODO would be nice
// to do everything in a different order so that this isn't necessary
get value() {
return names.reduce((bits, name) => {
const member = context_lookup.get(name);
if (!member) return bits;
if (!member) return bits;
if (member.index.value === -1) {
throw new Error(`unset index`);
}
if (member.index.value === -1) {
throw new Error(`unset index`);
}
const value = member.index.value as number;
const i = (value / 31) | 0;
const j = 1 << (value % 31);
// if (!member) {
// console.log({ names });
// throw new Error(`wut ${name}`);
// }
bits[i] |= j;
const bit = 1 << (member.index.value as number);
return bits | bit;
}, 0);
}
};
}
return bits;
}, Array((this.context.length / 31) | 0).fill(0));
changed(names, is_reactive_declaration = false) {
const bitmask = this.get_bitmask(names);
let operator;
let left;
let right;
return is_reactive_declaration
? x`$$self.$$.dirty & ${bitmask}`
: x`#changed & ${bitmask}`;
return {
get type() {
// we make the type a getter, even though it's always
// a BinaryExpression, because it gives us an opportunity
// to lazily create the node
const bitmask = get_bitmask();
if (renderer.context_overflow) {
const expression = bitmask
.map((bits, i) => ({ bits, i }))
.filter(({ bits }) => bits)
.map(({ bits, i }) => x`${changed}[${i}] & ${bits}`)
.reduce((lhs, rhs) => x`${lhs} || ${rhs}`);
({ operator, left, right } = expression);
} else {
({ operator, left, right } = x`${changed} & ${bitmask[0] || 0}` as BinaryExpression); // TODO the `|| 0` case should never apply
}
return 'BinaryExpression';
},
get operator() {
return operator;
},
get left() {
return left;
},
get right() {
return right;
}
} as Expression;
}
reference(node: string | Identifier | MemberExpression) {

@ -427,6 +427,14 @@ export default function dom(
${props.filter(v => v.export_name && !v.module).map(v => p`${v.export_name}: ${renderer.context_lookup.get(v.name).index}`)}
}` as ObjectExpression;
let dirty;
if (renderer.context_overflow) {
dirty = x`[]`;
for (let i = 0; i < renderer.context.length; i += 31) {
dirty.elements.push(x`-1`);
}
}
if (options.customElement) {
const declaration = b`
class ${name} extends @SvelteElement {
@ -435,7 +443,7 @@ export default function dom(
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes});
@init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
${dev_props_check}
@ -487,7 +495,7 @@ export default function dom(
constructor(options) {
super(${options.dev && `options`});
${should_add_css && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`}
@init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes});
@init(this, options, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`}
${dev_props_check}

@ -119,13 +119,12 @@ export default class SlotWrapper extends Wrapper {
});
if (dynamic_dependencies.length > 0) {
const bitmask = renderer.get_bitmask(dynamic_dependencies);
changes.properties.push(p`${attribute.name}: #changes & ${bitmask}`);
changes.properties.push(p`${attribute.name}: ${renderer.changed(dynamic_dependencies)}`);
}
});
renderer.blocks.push(b`
const ${get_slot_changes_fn} = #changes => ${changes};
const ${get_slot_changes_fn} = #changed => ${changes};
const ${get_slot_context_fn} = #ctx => ${get_slot_data(block, this.node.values)};
`);
} else {

@ -21,7 +21,7 @@ interface Fragment {
}
// eslint-disable-next-line @typescript-eslint/class-name-casing
interface T$$ {
dirty: number;
dirty: number[];
ctx: null|any;
bound: any;
update: () => void;
@ -88,15 +88,15 @@ export function destroy_component(component, detaching) {
}
function make_dirty(component, i) {
if (component.$$.dirty === -1) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty = 0;
component.$$.dirty.fill(0);
}
component.$$.dirty |= (1 << i);
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
export function init(component, options, instance, create_fragment, not_equal, props) {
export function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) {
const parent_component = current_component;
set_current_component(component);
@ -121,7 +121,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
// everything else
callbacks: blank_object(),
dirty: -1
dirty
};
let ready = false;

@ -74,7 +74,7 @@ function update($$) {
$$.update();
run_all($$.before_update);
$$.fragment && $$.fragment.p($$.ctx, $$.dirty);
$$.dirty = -1;
$$.dirty = [-1];
$$.after_update.forEach(add_render_callback);
}

@ -23,7 +23,7 @@ function create_fragment(ctx) {
insert(target, button, anchor);
foo_action = foo.call(null, button, ctx[1]) || ({});
},
p(ctx, changed) {
p(ctx, [changed]) {
if (is_function(foo_action.update) && changed & 1) foo_action.update.call(null, ctx[1]);
},
i: noop,

@ -27,7 +27,7 @@ function create_fragment(ctx) {
insert(target, details, anchor);
details.open = ctx[0];
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
details.open = ctx[0];
}

@ -37,7 +37,7 @@ function create_fragment(ctx) {
insert(target, input, anchor);
set_input_value(input, ctx[0]);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) set_data(t0, ctx[0]);
if (changed & 1 && input.value !== ctx[0]) {

@ -34,7 +34,7 @@ function create_fragment(ctx) {
insert(target, p, anchor);
append(p, t);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) set_data(t, ctx[0]);
},
i: noop,

@ -46,7 +46,7 @@ function create_fragment(ctx) {
set_input_value(input, ctx[0]);
current = true;
},
p(ctx, changed) {
p(ctx, [changed]) {
const bar_changes = {};
if (changed & 1) bar_changes.x = ctx[0];
bar.$set(bar_changes);

@ -28,7 +28,7 @@ function create_fragment(ctx) {
insert(target, h1, anchor);
append(h1, t);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) set_data(t, ctx[0]);
},
i: noop,

@ -39,7 +39,7 @@ function create_fragment(ctx) {
insert(target, t1, anchor);
insert(target, button, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 2) set_data(t0, ctx[1]);
},
i: noop,

@ -29,7 +29,7 @@ function create_fragment(ctx) {
insert(target, t, anchor);
insert(target, div1, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
attr(div1, "data-foo", ctx[0]);
}

@ -44,7 +44,7 @@ function create_fragment(ctx) {
append_dev(h1, t2);
insert_dev(target, t3, anchor);
},
p: function update(ctx, changed) {
p: function update(ctx, [changed]) {
if (changed & 1) set_data_dev(t1, ctx[0]);
debugger;
},

@ -119,7 +119,7 @@ function create_fragment(ctx) {
append_dev(p, t1);
append_dev(p, t2);
},
p: function update(ctx, changed) {
p: function update(ctx, [changed]) {
if (changed & 1) {
each_value = ctx[0];
let i;

@ -113,7 +113,7 @@ function create_fragment(ctx) {
append_dev(p, t1);
append_dev(p, t2);
},
p: function update(ctx, changed) {
p: function update(ctx, [changed]) {
if (changed & 1) {
each_value = ctx[0];
let i;

@ -23,7 +23,7 @@ function create_fragment(ctx) {
throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");
},
m: noop,
p: function update(ctx, changed) {
p: function update(ctx, [changed]) {
if (changed & 3) {
const obj = ctx[0];
const kobzol = ctx[1];

@ -88,7 +88,7 @@ function create_fragment(ctx) {
insert_dev(target, each_1_anchor, anchor);
},
p: function update(ctx, changed) {
p: function update(ctx, [changed]) {
if (changed & 0) {
each_value = things;
let i;

@ -68,7 +68,7 @@ function create_fragment(ctx) {
insert(target, each_1_anchor, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
each_value = ctx[0];
let i;

@ -41,7 +41,7 @@ function create_fragment(ctx) {
append_dev(p, t1);
append_dev(p, t2);
},
p: function update(ctx, changed) {
p: function update(ctx, [changed]) {
if (changed & 1 && t0_value !== (t0_value = Math.max(0, ctx[0]) + "")) set_data_dev(t0, t0_value);
if (changed & 2) set_data_dev(t2, ctx[1]);
},

@ -68,7 +68,7 @@ function create_fragment(ctx) {
insert(target, each_1_anchor, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 31) {
each_value = [ctx[0], ctx[1], ctx[2], ctx[3], ctx[4]];
let i;

@ -110,7 +110,7 @@ function create_fragment(ctx) {
insert(target, p, anchor);
append(p, t1);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 7) {
each_value = ctx[0];
let i;

@ -91,7 +91,7 @@ function create_fragment(ctx) {
insert(target, each_1_anchor, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
const each_value = ctx[0];
for (let i = 0; i < each_blocks.length; i += 1) each_blocks[i].r();
each_blocks = update_keyed_each(each_blocks, changed, get_key, 1, ctx, each_value, each_1_lookup, each_1_anchor.parentNode, fix_and_destroy_block, create_each_block, each_1_anchor, get_each_context);

@ -76,7 +76,7 @@ function create_fragment(ctx) {
insert(target, each_1_anchor, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
const each_value = ctx[0];
each_blocks = update_keyed_each(each_blocks, changed, get_key, 1, ctx, each_value, each_1_lookup, each_1_anchor.parentNode, destroy_block, create_each_block, each_1_anchor, get_each_context);
},

@ -61,7 +61,7 @@ function create_fragment(ctx) {
insert(target, t5, anchor);
insert(target, button2, anchor);
},
p(new_ctx, changed) {
p(new_ctx, [changed]) {
ctx = new_ctx;
if (changed & 2) set_data(t4, ctx[1]);
},

@ -65,7 +65,7 @@ function create_fragment(ctx) {
if_block.m(target, anchor);
insert(target, if_block_anchor, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (current_block_type !== (current_block_type = select_block_type(ctx, changed))) {
if_block.d(1);
if_block = current_block_type(ctx);

@ -40,7 +40,7 @@ function create_fragment(ctx) {
if (if_block) if_block.m(target, anchor);
insert(target, if_block_anchor, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (ctx[0]) {
if (!if_block) {
if_block = create_if_block(ctx);

@ -22,7 +22,7 @@ function create_fragment(ctx) {
m(target, anchor) {
insert(target, div, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
set_style(div, "color", ctx[0]);
}

@ -21,7 +21,7 @@ function create_fragment(ctx) {
m(target, anchor) {
insert(target, div, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
set_style(div, "background", "url(data:image/png;base64," + ctx[0] + ")");
}

@ -21,7 +21,7 @@ function create_fragment(ctx) {
m(target, anchor) {
insert(target, div, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
set_style(div, "color", ctx[0]);
}

@ -30,7 +30,7 @@ function create_fragment(ctx) {
insert(target, t, anchor);
insert(target, div1, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
attr(div0, "style", ctx[0]);
}

@ -40,7 +40,7 @@ function create_fragment(ctx) {
append(form, t0);
append(form, button);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1 && input.value !== ctx[0]) {
set_input_value(input, ctx[0]);
}

@ -28,7 +28,7 @@ function create_fragment(ctx) {
insert(target, input, anchor);
set_input_value(input, ctx[0]);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
set_input_value(input, ctx[0]);
}

@ -25,7 +25,7 @@ function create_fragment(ctx) {
insert(target, input, anchor);
input.checked = ctx[0];
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) {
input.checked = ctx[0];
}

@ -39,7 +39,7 @@ function create_fragment(ctx) {
append(p, t2);
append(p, t3);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) set_data(t3, ctx[0]);
},
i: noop,

@ -40,7 +40,7 @@ function create_fragment(ctx) {
append(p, t2);
append(p, t3);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1 && t3_value !== (t3_value = ctx[0].length + "")) set_data(t3, t3_value);
},
i: noop,

@ -39,7 +39,7 @@ function create_fragment(ctx) {
append(p, t2);
append(p, t3);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) set_data(t3, ctx[0]);
},
i: noop,

@ -40,7 +40,7 @@ function create_fragment(ctx) {
append(p, t2);
append(p, t3);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1 && t3_value !== (t3_value = ctx[0].length + "")) set_data(t3, t3_value);
},
i: noop,

@ -67,7 +67,7 @@ function create_fragment(ctx) {
audio.playbackRate = ctx[7];
}
},
p(ctx, changed) {
p(ctx, [changed]) {
if (!audio_updating && changed & 8 && !isNaN(ctx[3])) {
audio.currentTime = ctx[3];
}

@ -43,7 +43,7 @@ function create_fragment(ctx) {
}
}
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1 && select_value_value !== (select_value_value = ctx[0])) {
for (var i = 0; i < select.options.length; i += 1) {
var option = select.options[i];

@ -44,7 +44,7 @@ function create_fragment(ctx) {
insert(target, t, anchor);
insert(target, img1, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1 && img0.src !== (img0_src_value = ctx[0])) {
attr(img0, "src", img0_src_value);
}

@ -8,7 +8,7 @@ function create_fragment(ctx) {
return {
c: noop,
m: noop,
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1 && title_value !== (title_value = "a " + ctx[0] + " title")) {
document.title = title_value;
}

@ -91,7 +91,7 @@ function create_fragment(ctx) {
if (if_block) if_block.m(target, anchor);
insert(target, if_block_anchor, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (ctx[0]) {
if (if_block) {
if_block.p(ctx, changed);

@ -61,7 +61,7 @@ function create_fragment(ctx) {
insert(target, if_block_anchor, anchor);
current = true;
},
p(ctx, changed) {
p(ctx, [changed]) {
if (ctx[0] < 5) {
if (!if_block) {
if_block = create_if_block(ctx);

@ -56,7 +56,7 @@ function create_fragment(ctx) {
append(p3, t8);
append(p3, t9);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) set_data(t9, ctx[0]);
},
i: noop,

@ -27,7 +27,7 @@ function create_fragment(ctx) {
insert(target, p, anchor);
append(p, t);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1) set_data(t, ctx[0]);
},
i: noop,

@ -155,7 +155,7 @@ function create_fragment(ctx) {
if (if_block4) if_block4.m(target, anchor);
insert(target, if_block4_anchor, anchor);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (ctx[0]) {
if (!if_block0) {
if_block0 = create_if_block_4(ctx);

@ -47,7 +47,7 @@ function create_fragment(ctx) {
insert(target, video, anchor);
video_resize_listener = add_resize_listener(video, ctx[4].bind(video));
},
p(ctx, changed) {
p(ctx, [changed]) {
if (!video_updating && changed & 1 && !isNaN(ctx[0])) {
video.currentTime = ctx[0];
}

@ -46,7 +46,7 @@ function create_fragment(ctx) {
append(p, t0);
append(p, t1);
},
p(ctx, changed) {
p(ctx, [changed]) {
if (changed & 1 && !scrolling) {
scrolling = true;
clearTimeout(scrolling_timeout);

@ -41,6 +41,9 @@ export default {
<p>38</p>
<p>39</p>
<p>40</p>
<p>5:36</p>
<p>6:37</p>
<p>38</p>
`,
test({ assert, component, target }) {
@ -52,14 +55,19 @@ export default {
component._32 = 'd';
component._40 = 'e';
component._5 = 'f';
component._6 = 'g';
component._36 = 'h';
component._37 = 'i';
assert.htmlEqual(target.innerHTML, `
<p>a</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>f</p>
<p>g</p>
<p>7</p>
<p>8</p>
<p>9</p>
@ -89,18 +97,25 @@ export default {
<p>33</p>
<p>34</p>
<p>35</p>
<p>36</p>
<p>37</p>
<p>h</p>
<p>i</p>
<p>38</p>
<p>39</p>
<p>e</p>
<p>f:h</p>
<p>g:i</p>
<p>38</p>
`);
assert.deepEqual(component.reads, {
_0: 1,
_5: 2,
_6: 3,
_30: 1,
_31: 1,
_32: 1,
_36: 2,
_37: 3,
_40: 1
});
}

@ -43,6 +43,9 @@
export let _39 = '39';
export let _40 = '40';
$: foo = read(_6, '_6') + ':' + read(_37, '_37');
$: bar = read(_38, '_38');
const read = (value, label) => {
if (!reads[label]) reads[label] = 0;
reads[label] += 1;
@ -92,3 +95,7 @@
<p>{read(_38, '_38')}</p>
<p>{read(_39, '_39')}</p>
<p>{read(_40, '_40')}</p>
<p>{read(_5, '_5') + ':' + read(_36, '_36')}</p>
<p>{foo}</p>
<p>{bar}</p>
Loading…
Cancel
Save