Clean up the compiler a bit

This commit is contained in:
Yehuda Katz
2012-05-26 22:17:22 -07:00
parent facefe8fed
commit 68c76c9b2e
+95 -73
View File
@@ -22,15 +22,18 @@ Handlebars.JavaScriptCompiler = function() {};
invokePartial: 12,
push: 13,
assignToHash: 15,
pushStringParam: 16
pushStringParam: 16,
knownHelper: 17,
pushContext: 18,
lookupOnContext: 19
};
Compiler.MULTI_PARAM_OPCODES = {
appendContent: 1,
getContext: 1,
lookupWithHelpers: 2,
lookupWithHelpers: 1,
lookup: 1,
invokeMustache: 3,
invokeMustache: 2,
pushString: 1,
truthyOrFallback: 1,
functionOrFallback: 1,
@@ -38,7 +41,10 @@ Handlebars.JavaScriptCompiler = function() {};
invokePartial: 1,
push: 1,
assignToHash: 1,
pushStringParam: 1
pushStringParam: 1,
knownHelper: 1,
pushContext: 0,
lookupOnContext: 1
};
Compiler.DISASSEMBLE_MAP = {};
@@ -227,7 +233,7 @@ Handlebars.JavaScriptCompiler = function() {};
mustache: function(mustache) {
var params = this.setupStackForMustache(mustache);
this.opcode('invokeMustache', params.length, mustache.id.original, !!mustache.hash);
this.opcode('invokeMustache', params.length, mustache.id.original);
if(mustache.escaped && !this.options.noEscape) {
this.opcode('appendEscaped');
@@ -241,7 +247,20 @@ Handlebars.JavaScriptCompiler = function() {};
this.opcode('getContext', id.depth);
this.opcode('lookupWithHelpers', id.parts[0] || null, id.isScoped || false);
var name = id.parts[0];
if (!name) {
this.opcode('pushContext');
} else if (!id.isScoped && this.options.knownHelpers[name]) {
this.declare('usingKnownHelper', true);
this.opcode('knownHelper', name);
} else if (id.isScoped || this.options.knownHelpersOnly) {
this.declare('usingKnownHelper', false);
this.opcode('lookupOnContext', name);
} else {
this.declare('usingKnownHelper', false);
this.opcode('lookupWithHelpers', name);
}
for(var i=1, l=id.parts.length; i<l; i++) {
this.opcode('lookup', id.parts[i]);
@@ -311,6 +330,8 @@ Handlebars.JavaScriptCompiler = function() {};
if(mustache.hash) {
this.hash(mustache.hash);
} else {
this.opcode('push', '{}');
}
this.ID(mustache.id);
@@ -521,23 +542,21 @@ Handlebars.JavaScriptCompiler = function() {};
}
},
lookupWithHelpers: function(name, isScoped) {
if(name) {
this.usingKnownHelper = false;
knownHelper: function(name) {
this.pushStackLiteral(this.nameLookup('helpers', name, 'helper'));
},
var toPush;
if (!isScoped && this.options.knownHelpers[name]) {
this.usingKnownHelper = true;
this.pushStackLiteral(this.nameLookup('helpers', name, 'helper'));
} else if (isScoped || this.options.knownHelpersOnly) {
this.pushStackLiteral(this.nameLookup('depth' + this.lastContext, name, 'context'));
} else {
this.register('foundHelper', this.nameLookup('helpers', name, 'helper'));
this.pushStack("foundHelper || " + this.nameLookup('depth' + this.lastContext, name, 'context'));
}
} else {
this.pushStackLiteral('depth' + this.lastContext);
}
lookupOnContext: function(name) {
this.pushStackLiteral(this.nameLookup('depth' + this.lastContext, name, 'context'));
},
pushContext: function() {
this.pushStackLiteral('depth' + this.lastContext);
},
lookupWithHelpers: function(name) {
this.register('foundHelper', this.nameLookup('helpers', name, 'helper'));
this.pushStack("foundHelper || " + this.nameLookup('depth' + this.lastContext, name, 'context'));
},
lookup: function(name) {
@@ -559,44 +578,53 @@ Handlebars.JavaScriptCompiler = function() {};
this.pushStack(name);
},
invokeMustache: function(paramSize, original, hasHash) {
this.populateParams(paramSize, this.quotedString(original), null, null, hasHash, function(nextStack, helperMissingString, id) {
if (!this.usingKnownHelper) {
this.context.aliases.helperMissing = 'helpers.helperMissing';
this.context.aliases.undef = 'void 0';
this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
if (nextStack !== id) {
this.source.push("else { " + nextStack + " = " + id + "; }");
}
// The rules for mustaches are:
//
// If the first parameter resolves to a function, call the function with the remaining parameters
// If the first parameter resolves to undefined, call helperMissing with the string form of the
// first parameter and the remaining parameters
// Otherwise, if there is only one parameter, the expression evaluates to the value of the first
// parameter
invokeMustache: function(paramSize, original) {
this.populateParams(paramSize, this.quotedString(original), [], true, function(nextStack, helperMissingString, id) {
this.context.aliases.helperMissing = 'helpers.helperMissing';
this.context.aliases.undef = 'void 0';
this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
if (nextStack !== id) {
this.source.push("else { " + nextStack + " = " + id + "; }");
}
});
},
invokeProgram: function(guid, paramSize, hasHash) {
var inverse = this.programExpression(this.inverse);
var mainProgram = this.programExpression(guid);
// The rules for mustaches containing a block are:
//
// If the first parameter resolves to a function, call the function with the remaining parameters
// Otherwise, invoke blockHelperMissing with the string form of the first parameter and the
// remaining parameters.
invokeProgram: function(guid, paramSize) {
var options = [], program, inverse;
this.populateParams(paramSize, null, mainProgram, inverse, hasHash, function(nextStack, helperMissingString, id) {
if (!this.usingKnownHelper) {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
}
});
},
populateParams: function(paramSize, helperId, program, inverse, hasHash, callback) {
var needsRegister = hasHash || this.options.stringParams || inverse || this.options.data;
var id = this.popStack(), nextStack;
var params = [], param, stringParam, stringOptions;
var options = [], contexts = [];
if (hasHash) {
options.push("hash:" + this.popStack());
} else {
options.push("hash:{}");
if (inverse = this.programExpression(this.inverse)) {
options.push("inverse:" + inverse);
}
if (program = this.programExpression(guid)) {
options.push("fn:" + program);
}
this.populateParams(paramSize, null, options, false, function(nextStack, helperMissingString) {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
});
},
populateParams: function(paramSize, helperId, options, resolveLambdas, callback) {
var id = this.popStack(), nextStack;
var params = [], contexts = [], param, paramString, stringOptions;
options.push("hash:" + this.popStack());
for(var i=0; i<paramSize; i++) {
param = this.popStack();
params.push(param);
@@ -610,14 +638,6 @@ Handlebars.JavaScriptCompiler = function() {};
options.push("contexts:[" + contexts.join(",") + "]");
}
if (program) {
options.push("fn:" + program);
}
if (inverse) {
options.push("inverse:" + inverse);
}
if(this.options.data) {
options.push("data:data");
}
@@ -626,25 +646,27 @@ Handlebars.JavaScriptCompiler = function() {};
params.push("params");
this.populateCall(params, id, helperId || id, callback, program);
if (this.usingKnownHelper) {
this.pushStack(id + ".call(" + ["depth0"].concat(params).join(", ") + ")");
} else {
this.findAndCallHelper(params, id, helperId || id, resolveLambdas, callback);
}
},
populateCall: function(params, id, helperId, callback, program) {
findAndCallHelper: function(params, id, helperId, resolveLambdas, callback) {
this.useRegister('foundHelper');
this.context.aliases.functionType = '"function"';
var paramString = ["depth0"].concat(params).join(", "), nextStack;
var helperMissingString = ["depth0"].concat(helperId).concat(params).join(", ");
if (this.usingKnownHelper) {
nextStack = this.pushStack(id + ".call(" + paramString + ")");
} else {
this.useRegister('foundHelper');
nextStack = this.nextStack();
var condition = resolveLambdas ? "" : "foundHelper && ";
this.source.push("if(" + condition + "typeof " + id + " === functionType) { " +
nextStack + " = " + id + ".call(" + paramString + "); }");
nextStack = this.nextStack();
this.context.aliases.functionType = '"function"';
var condition = program ? "foundHelper && " : "";
this.source.push("if(" + condition + "typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
}
callback.call(this, nextStack, helperMissingString, id);
this.usingKnownHelper = false;
},
invokePartial: function(context) {