Remove unneeded code and add docs

There were a few operations that are no longer
needed, so remove them. Also document all
operations.
This commit is contained in:
tomhuda
2012-05-28 17:35:21 -07:00
parent 175c6fae0f
commit 727eb26cb6
+213 -169
View File
@@ -240,21 +240,7 @@ Handlebars.JavaScriptCompiler = function() {};
ID: function(id) {
this.addDepth(id.depth);
this.opcode('getContext', id.depth);
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);
}
this.opcode('lookupOnContext', id.parts[0]);
for(var i=1, l=id.parts.length; i<l; i++) {
this.opcode('lookup', id.parts[i]);
@@ -360,14 +346,6 @@ Handlebars.JavaScriptCompiler = function() {};
this.opcode('pushLiteral', '{}');
}
return params;
},
setupStackForMustache: function(mustache) {
var params = this.setupMustacheParams(mustache);
this.ID(mustache.id);
return params;
}
};
@@ -525,6 +503,15 @@ Handlebars.JavaScriptCompiler = function() {};
}
},
// [blockValue]
//
// On stack, before: hash, inverse, program, value
// On stack, after: return value of blockHelperMissing
//
// The purpose of this opcode is to take a block of the form
// `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
// replace it on the stack with the result of properly
// invoking blockHelperMissing.
blockValue: function() {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
@@ -537,6 +524,12 @@ Handlebars.JavaScriptCompiler = function() {};
});
},
// [ambiguousBlockValue]
//
// On stack, before: hash, inverse, program, value
// Compiler value, before: lastHelper=value of last found helper, if any
// On stack, after, if no lastHelper: same as [blockValue]
// On stack, after, if lastHelper: value
ambiguousBlockValue: function() {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
@@ -549,10 +542,25 @@ Handlebars.JavaScriptCompiler = function() {};
this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
},
// [appendContent]
//
// On stack, before: ...
// On stack, after: ...
//
// Appends the string value of `content` to the current buffer
appendContent: function(content) {
this.source.push(this.appendToBuffer(this.quotedString(content)));
},
// [append]
//
// On stack, before: value, ...
// On stack, after: ...
//
// Coerces `value` to a String and appends it to the current buffer.
//
// If `value` is truthy, or 0, it is coerced into a string and appended
// Otherwise, the empty string is appended
append: function() {
var local = this.popStack();
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
@@ -561,6 +569,12 @@ Handlebars.JavaScriptCompiler = function() {};
}
},
// [appendEscaped]
//
// On stack, before: value, ...
// On stack, after: ...
//
// Escape `value` and append it to the buffer
appendEscaped: function() {
var opcode = this.nextOpcode(), extra = "";
this.context.aliases.escapeExpression = 'this.escapeExpression';
@@ -573,24 +587,47 @@ Handlebars.JavaScriptCompiler = function() {};
this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
},
// [getContext]
//
// On stack, before: ...
// On stack, after: ...
// Compiler value, after: lastContext=depth
//
// Set the value of the `lastContext` compiler value to the depth
getContext: function(depth) {
if(this.lastContext !== depth) {
this.lastContext = depth;
}
},
knownHelper: function(name) {
this.pushStackLiteral(this.nameLookup('helpers', name, 'helper'));
},
// [lookupOnContext]
//
// On stack, before: ...
// On stack, after: currentContext[name], ...
//
// Looks up the value of `name` on the current context and pushes
// it onto the stack.
lookupOnContext: function(name) {
this.pushStack(this.nameLookup('depth' + this.lastContext, name, 'context'));
},
// [pushContext]
//
// On stack, before: ...
// On stack, after: currentContext, ...
//
// Pushes the value of the current context onto the stack.
pushContext: function() {
this.pushStackLiteral('depth' + this.lastContext);
},
// [resolvePossibleLambda]
//
// On stack, before: value, ...
// On stack, after: resolved value, ...
//
// If the `value` is a lambda, replace it on the stack by
// the return value of the lambda
resolvePossibleLambda: function() {
this.context.aliases.functionType = '"function"';
@@ -599,57 +636,72 @@ Handlebars.JavaScriptCompiler = function() {};
});
},
lookupWithHelpers: function(name) {
var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
this.register('foundHelper', helperName);
this.pushStack("foundHelper || " + this.nameLookup('depth' + this.lastContext, name, 'context'));
},
// [lookup]
//
// On stack, before: value, ...
// On stack, after: value[name], ...
//
// Replace the value on the stack with the result of looking
// up `name` on `value`
lookup: function(name) {
this.replaceStack(function(current) {
return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
});
},
// [pushStringParam]
//
// On stack, before: ...
// On stack, after: string, currentContext, ...
//
// This opcode is designed for use in string mode, which
// provides the string value of a parameter along with its
// depth rather than resolving it immediately.
pushStringParam: function(string) {
this.pushStackLiteral('depth' + this.lastContext);
this.pushString(string);
},
// [pushString]
//
// On stack, before: ...
// On stack, after: quotedString(string), ...
//
// Push a quoted version of `string` onto the stack
pushString: function(string) {
this.pushStackLiteral(this.quotedString(string));
},
push: function(name) {
this.pushStack(name);
// [push]
//
// On stack, before: ...
// On stack, after: expr, ...
//
// Push an expression onto the stack
push: function(expr) {
this.pushStack(expr);
},
// [pushLiteral]
//
// On stack, before: ...
// On stack, after: value, ...
//
// Pushes a value onto the stack. This operation prevents
// the compiler from creating a temporary variable to hold
// it.
pushLiteral: function(value) {
this.pushStackLiteral(value);
},
// The rules for mustaches containing a block are:
// [pushProgram]
//
// 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;
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 + "); }");
});
},
// On stack, before: ...
// On stack, after: program(guid), ...
//
// Push a program expression onto the stack. This takes
// a compile-time guid and converts it into a runtime-accessible
// expression.
pushProgram: function(guid) {
if (guid != null) {
this.pushStackLiteral(this.programExpression(guid));
@@ -658,6 +710,15 @@ Handlebars.JavaScriptCompiler = function() {};
}
},
// [invokeHelper]
//
// On stack, before: hash, inverse, program, params..., ...
// On stack, after: result of helper invocation
//
// Pops off the helper's parameters, invokes the helper,
// and pushes the helper's return value onto the stack.
//
// If the helper is not found, `helperMissing` is called.
invokeHelper: function(paramSize, name) {
this.context.aliases.helperMissing = 'helpers.helperMissing';
@@ -669,11 +730,30 @@ Handlebars.JavaScriptCompiler = function() {};
helper.helperMissingParams + ")");
},
// [invokeKnownHelper]
//
// On stack, before: hash, inverse, program, params..., ...
// On stack, after: result of helper invocation
//
// This operation is used when the helper is known to exist,
// so a `helperMissing` fallback is not required.
invokeKnownHelper: function(paramSize, name) {
var helper = this.setupHelper(paramSize, name);
this.pushStack(helper.name + ".call(" + helper.callParams + ")");
},
// [invokeAmbiguous]
//
// On stack, before: hash, inverse, program, params..., ...
// On stack, after: result of disambiguation
//
// This operation is used when an expression like `{{foo}}`
// is provided, but we don't know at compile-time whether it
// is a helper or a path.
//
// This operation emits more code than the other options,
// and can be avoided by passing the `knownHelpers` and
// `knownHelpersOnly` flags at compile-time.
invokeAmbiguous: function(name) {
this.context.aliases.functionType = '"function"';
@@ -690,119 +770,15 @@ Handlebars.JavaScriptCompiler = function() {};
this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '() : ' + nextStack + '; }');
},
setupHelper: function(paramSize, name) {
var params = [];
this.setupParams(paramSize, params);
var foundHelper = this.nameLookup('helpers', name, 'helper');
return {
params: params,
name: foundHelper,
callParams: ["depth0"].concat(params).join(", "),
helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
};
},
// the params and contexts arguments are passed in arrays
// to fill in
setupParams: function(paramSize, params) {
var options = [], contexts = [], param, inverse, program;
options.push("hash:" + this.popStack());
inverse = this.popStack();
program = this.popStack();
// Avoid setting fn and inverse if neither are set. This allows
// helpers to do a check for `if (options.fn)`
if (program || inverse) {
if (!program) {
this.context.aliases.self = "this";
program = "self.noop";
}
if (!inverse) {
this.context.aliases.self = "this";
inverse = "self.noop";
}
options.push("inverse:" + inverse);
options.push("fn:" + program);
}
for(var i=0; i<paramSize; i++) {
param = this.popStack();
params.push(param);
if(this.options.stringParams) {
contexts.push(this.popStack());
}
}
if (this.options.stringParams) {
options.push("contexts:[" + contexts.join(",") + "]");
}
if(this.options.data) {
options.push("data:data");
}
params.push("{" + options.join(",") + "}");
return params.join(", ");
},
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);
if(this.options.stringParams) {
contexts.push(this.popStack());
}
}
if (this.options.stringParams) {
options.push("contexts:[" + contexts.join(",") + "]");
}
if(this.options.data) {
options.push("data:data");
}
this.register("params", "{" + options.join(",") + "}");
params.push("params");
if (this.usingKnownHelper) {
this.pushStack(id + ".call(" + ["depth0"].concat(params).join(", ") + ")");
} else {
this.findAndCallHelper(params, id, helperId || id, resolveLambdas, callback);
}
},
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(", ");
nextStack = this.nextStack();
var condition = resolveLambdas ? "" : "foundHelper && ";
this.source.push("if(" + condition + "typeof " + id + " === functionType) { " +
nextStack + " = " + id + ".call(" + paramString + "); }");
callback.call(this, nextStack, helperMissingString, id);
},
invokePartial: function(context) {
var params = [this.nameLookup('partials', context, 'partial'), "'" + context + "'", this.popStack(), "helpers", "partials"];
// [invokePartial]
//
// On stack, before: context, ...
// On stack after: result of partial invocation
//
// This operation pops off a context, invokes a partial with that context,
// and pushes the result of the invocation back.
invokePartial: function(name) {
var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
if (this.options.data) {
params.push("data");
@@ -812,6 +788,13 @@ Handlebars.JavaScriptCompiler = function() {};
this.pushStack("self.invokePartial(" + params.join(", ") + ");");
},
// [assignToHash]
//
// On stack, before: value, hash, ...
// On stack, after: hash, ...
//
// Pops a value and hash off the stack, assigns `hash[key] = value`
// and pushes the hash back onto the stack.
assignToHash: function(key) {
var value = this.popStack();
var hash = this.topStack();
@@ -934,6 +917,67 @@ Handlebars.JavaScriptCompiler = function() {};
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r') + '"';
},
setupHelper: function(paramSize, name) {
var params = [];
this.setupParams(paramSize, params);
var foundHelper = this.nameLookup('helpers', name, 'helper');
return {
params: params,
name: foundHelper,
callParams: ["depth0"].concat(params).join(", "),
helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
};
},
// the params and contexts arguments are passed in arrays
// to fill in
setupParams: function(paramSize, params) {
var options = [], contexts = [], param, inverse, program;
options.push("hash:" + this.popStack());
inverse = this.popStack();
program = this.popStack();
// Avoid setting fn and inverse if neither are set. This allows
// helpers to do a check for `if (options.fn)`
if (program || inverse) {
if (!program) {
this.context.aliases.self = "this";
program = "self.noop";
}
if (!inverse) {
this.context.aliases.self = "this";
inverse = "self.noop";
}
options.push("inverse:" + inverse);
options.push("fn:" + program);
}
for(var i=0; i<paramSize; i++) {
param = this.popStack();
params.push(param);
if(this.options.stringParams) {
contexts.push(this.popStack());
}
}
if (this.options.stringParams) {
options.push("contexts:[" + contexts.join(",") + "]");
}
if(this.options.data) {
options.push("data:data");
}
params.push("{" + options.join(",") + "}");
return params.join(", ");
}
};