Skip to content
This repository was archived by the owner on Apr 4, 2019. It is now read-only.

Commit 804c5ae

Browse files
wycatstilde-engineering
authored andcommitted
Defer more of rendering to the runtime
This commit makes all tests pass with the new visitor structure, significantly shrinking the size of compiled templates and making it possible to do re-renders without having to execute a render function. This also means that, in theory, hooks like `inline` can know the structure of what they're invoking before actually invoking it, allowing implementation to do smarter caching at the "statement" level. This change is honestly mostly a wonky improvement, but it does help us avoid making as many decisions in the compiled template, deferring them to the runtime. There is still a decent amount of cruft left as of this commit, which we will remove momentarily.
1 parent 9e1228e commit 804c5ae

File tree

7 files changed

+221
-32
lines changed

7 files changed

+221
-32
lines changed

demos/compile-and-run.html

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@
3333
var compiler = requireModule('htmlbars-compiler'),
3434
DOMHelper = requireModule('dom-helper').default,
3535
hooks = requireModule('htmlbars-runtime').hooks,
36-
helpers = requireModule('htmlbars-runtime').helpers;
36+
helpers = requireModule('htmlbars-runtime').helpers,
37+
render = requireModule('htmlbars-runtime').render;
3738

3839
var templateSource = localStorage.getItem('templateSource');
3940
var data = localStorage.getItem('templateData');
41+
var shouldRender = localStorage.getItem('shouldRender');
4042

4143
if (templateSource) {
4244
textarea.value = templateSource;
@@ -46,13 +48,19 @@
4648
dataarea.value = data;
4749
}
4850

51+
if (shouldRender === "false") {
52+
skipRender.checked = true;
53+
}
54+
4955
button.addEventListener('click', function() {
5056
var source = textarea.value,
5157
data = dataarea.value,
58+
shouldRender = !skipRender.checked,
5259
compileOptions;
5360

5461
localStorage.setItem('templateSource', source);
5562
localStorage.setItem('templateData', data);
63+
localStorage.setItem('shouldRender', shouldRender);
5664

5765
try {
5866
data = JSON.parse(data);
@@ -70,10 +78,10 @@
7078
var templateSpec = compiler.compileSpec(source, compileOptions);
7179
output.innerHTML = '<pre><code>' + templateSpec + '</code></pre>';
7280

73-
if (!skipRender.checked) {
81+
if (shouldRender) {
7482
var env = { dom: new DOMHelper(), hooks: hooks, helpers: helpers };
75-
var template = compiler.compile(source, compileOptions);
76-
var dom = template.render(data, env, { contextualElement: output }).fragment;
83+
var template = compiler.template(templateSpec);
84+
var dom = render(template, data, env, { contextualElement: output }).fragment;
7785

7886
output.innerHTML += '<hr><pre><code>' + JSON.stringify(data) + '</code></pre><hr>';
7987
output.appendChild(dom);

packages/htmlbars-compiler/lib/fragment-javascript-compiler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ FragmentJavaScriptCompiler.prototype.compile = function(opcodes, options) {
2020
this.namespaceFrameStack = [{namespace: null, depth: null}];
2121
this.domNamespace = null;
2222

23-
this.source.push('function build(dom) {\n');
23+
this.source.push('function buildFragment(dom) {\n');
2424
processOpcodes(this, opcodes);
2525
this.source.push(this.indent+'}');
2626

packages/htmlbars-compiler/lib/hydration-javascript-compiler.js

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ prototype.compile = function(opcodes, options) {
2727
this.parentCount = 0;
2828
this.indent = (options && options.indent) || "";
2929
this.hooks = {};
30+
this.statements = [];
31+
this.expressionStack = [];
32+
this.augmentContext = [];
3033

3134
processOpcodes(this, opcodes);
3235

@@ -40,6 +43,8 @@ prototype.compile = function(opcodes, options) {
4043
createMorphsProgram: '',
4144
hydrateMorphsProgram: '',
4245
fragmentProcessingProgram: '',
46+
statements: this.statements,
47+
augmentContext: this.augmentContext,
4348
hasMorphs: false
4449
};
4550

@@ -48,11 +53,11 @@ prototype.compile = function(opcodes, options) {
4853
if (this.morphs.length) {
4954
result.hasMorphs = true;
5055
morphs =
51-
indent+'var morphs = new Array(' + this.morphs.length + ');\n';
56+
indent+' var morphs = new Array(' + this.morphs.length + ');\n';
5257

5358
for (i = 0, l = this.morphs.length; i < l; ++i) {
5459
var morph = this.morphs[i];
55-
morphs += indent+'morphs['+i+'] = '+morph+';\n';
60+
morphs += indent+' morphs['+i+'] = '+morph+';\n';
5661
}
5762
}
5863

@@ -65,46 +70,54 @@ prototype.compile = function(opcodes, options) {
6570
}
6671

6772
if (result.hasMorphs) {
68-
indent = indent.substr(2);
6973
result.createMorphsProgram =
70-
indent + 'function buildRenderNodes(dom, fragment, contextualElement) {\n' +
74+
'function buildRenderNodes(dom, fragment, contextualElement) {\n' +
7175
result.fragmentProcessingProgram +
7276
morphs +
7377
indent + ' return morphs;\n' +
74-
indent+'}\n';
78+
indent+'}';
7579
} else {
7680
result.createMorphsProgram =
77-
' function buildRenderNodes() { return []; }\n';
81+
'function buildRenderNodes() { return []; }';
7882
}
7983

8084
return result;
8185
};
8286

8387
prototype.prepareArray = function(length) {
8488
var values = [];
89+
var expressionValues = [];
8590

8691
for (var i = 0; i < length; i++) {
8792
values.push(this.stack.pop());
93+
expressionValues.push(this.expressionStack.pop());
8894
}
8995

96+
this.expressionStack.push(expressionValues);
9097
this.stack.push('[' + values.join(', ') + ']');
9198
};
9299

93100
prototype.prepareObject = function(size) {
94101
var pairs = [];
102+
var expressionPairs = [];
95103

96104
for (var i = 0; i < size; i++) {
97105
pairs.push(this.stack.pop() + ': ' + this.stack.pop());
106+
expressionPairs.push(this.expressionStack.pop(), this.expressionStack.pop());
98107
}
99108

109+
this.expressionStack.push(expressionPairs);
100110
this.stack.push('{' + pairs.join(', ') + '}');
101111
};
102112

103113
prototype.pushRaw = function(value) {
114+
this.expressionStack.push(value);
104115
this.stack.push(value);
105116
};
106117

107118
prototype.pushLiteral = function(value) {
119+
this.expressionStack.push(value);
120+
108121
if (typeof value === 'string') {
109122
this.stack.push(string(value));
110123
} else {
@@ -118,6 +131,8 @@ prototype.pushHook = function(name, args) {
118131
};
119132

120133
prototype.pushGetHook = function(path, morphNum) {
134+
this.expressionStack.push([ 'get', path ]);
135+
121136
this.pushHook('get', [
122137
'env',
123138
'morphs[' + morphNum + ']',
@@ -127,6 +142,13 @@ prototype.pushGetHook = function(path, morphNum) {
127142
};
128143

129144
prototype.pushSexprHook = function(morphNum) {
145+
this.expressionStack.push([
146+
'subexpr',
147+
this.expressionStack.pop(),
148+
this.expressionStack.pop(),
149+
this.expressionStack.pop()
150+
]);
151+
130152
this.pushHook('subexpr', [
131153
'env',
132154
'morphs[' + morphNum + ']',
@@ -138,6 +160,8 @@ prototype.pushSexprHook = function(morphNum) {
138160
};
139161

140162
prototype.pushConcatHook = function(morphNum) {
163+
this.expressionStack.push([ 'concat', this.expressionStack.pop() ]);
164+
141165
this.pushHook('concat', [
142166
'env',
143167
'morphs[' + morphNum + ']',
@@ -151,6 +175,8 @@ prototype.printHook = function(name, args) {
151175
};
152176

153177
prototype.printSetHook = function(name, index) {
178+
this.augmentContext.push(name);
179+
154180
this.printHook('set', [
155181
'env',
156182
'context',
@@ -160,6 +186,15 @@ prototype.printSetHook = function(name, index) {
160186
};
161187

162188
prototype.printBlockHook = function(morphNum, templateId, inverseId) {
189+
this.statements.push([
190+
'block',
191+
this.expressionStack.pop(), // path
192+
this.expressionStack.pop(), // params
193+
this.expressionStack.pop(), // hash
194+
templateId,
195+
inverseId
196+
]);
197+
163198
this.printHook('block', [
164199
'env',
165200
'morphs[' + morphNum + ']',
@@ -173,17 +208,29 @@ prototype.printBlockHook = function(morphNum, templateId, inverseId) {
173208
};
174209

175210
prototype.printInlineHook = function(morphNum) {
211+
var path = this.stack.pop();
212+
var params = this.stack.pop();
213+
var hash = this.stack.pop();
214+
215+
var exprPath = this.expressionStack.pop();
216+
var exprParams = this.expressionStack.pop();
217+
var exprHash = this.expressionStack.pop();
218+
219+
this.statements.push([ 'inline', exprPath, exprParams, exprHash ]);
220+
176221
this.printHook('inline', [
177222
'env',
178223
'morphs[' + morphNum + ']',
179224
'context',
180-
this.stack.pop(), // path
181-
this.stack.pop(), // params
182-
this.stack.pop() // hash
225+
path,
226+
params,
227+
hash
183228
]);
184229
};
185230

186231
prototype.printContentHook = function(morphNum) {
232+
this.statements.push([ 'content', this.expressionStack.pop() ]);
233+
187234
this.printHook('content', [
188235
'env',
189236
'morphs[' + morphNum + ']',
@@ -193,6 +240,13 @@ prototype.printContentHook = function(morphNum) {
193240
};
194241

195242
prototype.printComponentHook = function(morphNum, templateId) {
243+
this.statements.push([
244+
'component',
245+
this.expressionStack.pop(), // path
246+
this.expressionStack.pop(), // attrs
247+
templateId
248+
]);
249+
196250
this.printHook('component', [
197251
'env',
198252
'morphs[' + morphNum + ']',
@@ -204,6 +258,12 @@ prototype.printComponentHook = function(morphNum, templateId) {
204258
};
205259

206260
prototype.printAttributeHook = function(attrMorphNum) {
261+
this.statements.push([
262+
'attribute',
263+
this.expressionStack.pop(), // name
264+
this.expressionStack.pop() // value;
265+
]);
266+
207267
this.printHook('attribute', [
208268
'env',
209269
'morphs[' + attrMorphNum + ']',
@@ -213,6 +273,13 @@ prototype.printAttributeHook = function(attrMorphNum) {
213273
};
214274

215275
prototype.printElementHook = function(morphNum) {
276+
this.statements.push([
277+
'element',
278+
this.expressionStack.pop(), // path
279+
this.expressionStack.pop(), // params
280+
this.expressionStack.pop() // hash
281+
]);
282+
216283
this.printHook('element', [
217284
'env',
218285
'morphs[' + morphNum + ']',

packages/htmlbars-compiler/lib/template-compiler.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ TemplateCompiler.prototype.endProgram = function(program, programDepth) {
8888
templateSignature += ', blockArguments';
8989
}
9090

91+
var statements = hydrationPrograms.statements.map(function(s) {
92+
return indent+' '+JSON.stringify(s);
93+
}).join(",\n");
94+
95+
var augmentContext = JSON.stringify(hydrationPrograms.augmentContext);
96+
97+
var templates = this.childTemplates.map(function(_, index) {
98+
return 'child' + index;
99+
}).join(', ');
100+
91101
var template =
92102
'(function() {\n' +
93103
this.getChildTemplateVars(indent + ' ') +
@@ -96,18 +106,13 @@ TemplateCompiler.prototype.endProgram = function(program, programDepth) {
96106
indent+' blockParams: ' + blockParams.length + ',\n' +
97107
indent+' cachedFragment: null,\n' +
98108
indent+' hasRendered: false,\n' +
99-
indent+' build: ' + fragmentProgram + ',\n' +
100-
indent+' buildRenderNodes: buildRenderNodes,\n' +
101-
indent+' render: function render(' + templateSignature + ') {\n' +
102-
indent+' var dom = env.dom;\n' +
103-
this.getHydrationHooks(indent + ' ', this.hydrationCompiler.hooks) +
104-
indent+' var contextualElement = options && options.contextualElement;\n' +
105-
indent+' dom.detectNamespace(contextualElement);\n' +
106-
indent+' var morphs = rootNode.childNodes;\n' +
107-
hydrationPrograms.hydrateMorphsProgram +
108-
indent+' }\n' +
109+
indent+' buildFragment: ' + fragmentProgram + ',\n' +
110+
indent+' buildRenderNodes: ' + hydrationPrograms.createMorphsProgram + ',\n' +
111+
indent+' statements: [\n' + statements + '\n' +
112+
indent+' ],\n' +
113+
indent+' augmentContext: ' + augmentContext + ',\n' +
114+
indent+' templates: [' + templates + ']\n' +
109115
indent+' };\n' +
110-
hydrationPrograms.createMorphsProgram +
111116
indent+'}())';
112117

113118
this.templates.push(template);

0 commit comments

Comments
 (0)