summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bench/throughput.js8
-rw-r--r--lib/handlebars/compiler/compiler.js8
-rw-r--r--lib/handlebars/compiler/javascript-compiler.js24
-rw-r--r--lib/handlebars/runtime.js8
-rw-r--r--spec/blocks.js21
-rw-r--r--spec/env/common.js2
6 files changed, 62 insertions, 9 deletions
diff --git a/bench/throughput.js b/bench/throughput.js
index 308446a..d27a94d 100644
--- a/bench/throughput.js
+++ b/bench/throughput.js
@@ -28,11 +28,13 @@ function makeSuite(bench, name, template, handlebarsOnly) {
partials = template.partials,
handlebarsOut,
+ compatOut,
dustOut,
ecoOut,
mustacheOut;
var handlebar = Handlebars.compile(template.handlebars, {data: false}),
+ compat = Handlebars.compile(template.handlebars, {data: false, compat: true}),
options = {helpers: template.helpers};
_.each(template.partials && template.partials.handlebars, function(partial, name) {
Handlebars.registerPartial(name, Handlebars.compile(partial, {data: false}));
@@ -43,6 +45,11 @@ function makeSuite(bench, name, template, handlebarsOnly) {
handlebar(context, options);
});
+ compatOut = compat(context, options);
+ bench("compat", function() {
+ compat(context, options);
+ });
+
if (handlebarsOnly) {
return;
}
@@ -107,6 +114,7 @@ function makeSuite(bench, name, template, handlebarsOnly) {
}
}
+ compare(compatOut, 'compat');
compare(dustOut, 'dust');
compare(ecoOut, 'eco');
compare(mustacheOut, 'mustache');
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index 48575d4..ffddec4 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -308,7 +308,7 @@ Compiler.prototype = {
// Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
this.opcode('pushContext');
} else {
- this.opcode('lookupOnContext', id.parts, id.falsy);
+ this.opcode('lookupOnContext', id.parts, id.falsy, id.isScoped);
}
},
@@ -424,6 +424,9 @@ export function precompile(input, options, env) {
if (!('data' in options)) {
options.data = true;
}
+ if (options.compat) {
+ options.useDepths = true;
+ }
var ast = env.parse(input);
var environment = new env.Compiler().compile(ast, options);
@@ -440,6 +443,9 @@ export function compile(input, options, env) {
if (!('data' in options)) {
options.data = true;
}
+ if (options.compat) {
+ options.useDepths = true;
+ }
var compiled;
diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js
index f1b6d36..b9b858d 100644
--- a/lib/handlebars/compiler/javascript-compiler.js
+++ b/lib/handlebars/compiler/javascript-compiler.js
@@ -17,6 +17,11 @@ JavaScriptCompiler.prototype = {
return parent + "['" + name + "']";
}
},
+ depthedLookup: function(name) {
+ this.aliases.lookup = 'this.lookup';
+
+ return 'lookup(depths, "' + name + '")';
+ },
compilerInfo: function() {
var revision = COMPILER_REVISION,
@@ -69,7 +74,7 @@ JavaScriptCompiler.prototype = {
this.compileChildren(environment, options);
- this.useDepths = this.useDepths || environment.depths.list.length;
+ this.useDepths = this.useDepths || environment.depths.list.length || this.options.compat;
var opcodes = environment.opcodes,
opcode,
@@ -356,15 +361,20 @@ JavaScriptCompiler.prototype = {
//
// Looks up the value of `name` on the current context and pushes
// it onto the stack.
- lookupOnContext: function(parts, falsy) {
+ lookupOnContext: function(parts, falsy, scoped) {
/*jshint -W083 */
- this.pushContext();
- if (!parts) {
- return;
+ var i = 0,
+ len = parts.length;
+
+ if (!scoped && this.isChild && this.options.compat && !this.lastContext) {
+ // The depthed query is expected to handle the undefined logic for the root level that
+ // is implemented below, so we evaluate that directly in compat mode
+ this.pushStackLiteral(this.depthedLookup(parts[i++]));
+ } else {
+ this.pushContext();
}
- var len = parts.length;
- for (var i = 0; i < len; i++) {
+ for (; i < len; i++) {
this.replaceStack(function(current) {
var lookup = this.nameLookup(current, parts[i], 'context');
// We want to ensure that zero and false are handled properly for the first element
diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js
index 530dbd2..ec54f53 100644
--- a/lib/handlebars/runtime.js
+++ b/lib/handlebars/runtime.js
@@ -63,6 +63,14 @@ export function template(templateSpec, env) {
// Just add water
var container = {
+ lookup: function(depths, name) {
+ var len = depths.length;
+ for (var i = 0; i < len; i++) {
+ if (depths[i] && depths[i][name] != null) {
+ return depths[i][name];
+ }
+ }
+ },
lambda: function(current, context) {
return typeof current === 'function' ? current.call(context) : current;
},
diff --git a/spec/blocks.js b/spec/blocks.js
index c57a67e..a13cce2 100644
--- a/spec/blocks.js
+++ b/spec/blocks.js
@@ -94,4 +94,25 @@ describe('blocks', function() {
'No people\n');
});
});
+
+ describe('compat mode', function() {
+ it("block with deep recursive lookup lookup", function() {
+ var string = "{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}";
+ var hash = {omg: "OMG!", outer: [{ inner: [{ text: "goodbye" }] }] };
+
+ shouldCompileTo(string, [hash, undefined, undefined, true], "Goodbye cruel OMG!");
+ });
+ it("block with deep recursive pathed lookup", function() {
+ var string = "{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}";
+ var hash = {omg: {yes: "OMG!"}, outer: [{ inner: [{ yes: 'no', text: "goodbye" }] }] };
+
+ shouldCompileTo(string, [hash, undefined, undefined, true], "Goodbye cruel OMG!");
+ });
+ it("block with missed recursive lookup", function() {
+ var string = "{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}";
+ var hash = {omg: {no: "OMG!"}, outer: [{ inner: [{ yes: 'no', text: "goodbye" }] }] };
+
+ shouldCompileTo(string, [hash, undefined, undefined, true], "Goodbye cruel ");
+ });
+ });
});
diff --git a/spec/env/common.js b/spec/env/common.js
index 03eef93..a603fc0 100644
--- a/spec/env/common.js
+++ b/spec/env/common.js
@@ -17,7 +17,7 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
if(Object.prototype.toString.call(hashOrArray) === "[object Array]") {
ary = [];
ary.push(hashOrArray[0]);
- ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2], compat: hashOrArray[3] });
+ ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] });
options = {compat: hashOrArray[3]};
} else {
ary = [hashOrArray];