diff options
author | kpdecker <kpdecker@gmail.com> | 2014-02-09 12:20:25 -0600 |
---|---|---|
committer | kpdecker <kpdecker@gmail.com> | 2014-02-09 12:20:25 -0600 |
commit | 71ed8b5e932831bb7c43ba42a54a61366dace5ac (patch) | |
tree | 9a255c45f7270bfaa33b62470a9bd84ecf442e75 | |
parent | 7a6f706dc41a39b360e33c83fe31684316d5d3f9 (diff) | |
parent | bc8f6792e8ab8eb5028b667065e410bd510479f5 (diff) | |
download | handlebars.js-71ed8b5e932831bb7c43ba42a54a61366dace5ac.zip handlebars.js-71ed8b5e932831bb7c43ba42a54a61366dace5ac.tar.gz handlebars.js-71ed8b5e932831bb7c43ba42a54a61366dace5ac.tar.bz2 |
Merge branch 'master' of github.com:wycats/handlebars.js
-rw-r--r-- | lib/handlebars/compiler/javascript-compiler.js | 112 | ||||
-rw-r--r-- | lib/handlebars/runtime.js | 62 | ||||
-rw-r--r-- | spec/expected/empty.amd.js | 8 | ||||
-rw-r--r-- | spec/javascript-compiler.js | 2 |
4 files changed, 95 insertions, 89 deletions
diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 55edb9a..8222506 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -33,7 +33,7 @@ JavaScriptCompiler.prototype = { compilerInfo: function() { var revision = COMPILER_REVISION, versions = REVISION_CHANGES[revision]; - return "this.compilerInfo = ["+revision+",'"+versions+"'];\n"; + return [revision, versions]; }, appendToBuffer: function(string) { @@ -60,6 +60,7 @@ JavaScriptCompiler.prototype = { this.options = options || {}; this.stringParams = this.options.stringParams; this.trackIds = this.options.trackIds; + this.precompile = !asObject; log('debug', this.environment.disassemble() + "\n\n"); @@ -67,14 +68,14 @@ JavaScriptCompiler.prototype = { this.isChild = !!context; this.context = context || { programs: [], - environments: [], - aliases: { } + environments: [] }; this.preamble(); this.stackSlot = 0; this.stackVars = []; + this.aliases = {}; this.registers = { list: [] }; this.hashes = []; this.compileStack = []; @@ -108,22 +109,38 @@ JavaScriptCompiler.prototype = { throw new Exception('Compile completed with content left on stack'); } - return this.createFunctionContext(asObject); - }, + var fn = this.createFunctionContext(asObject); + if (!this.isChild) { + var ret = { + compiler: this.compilerInfo(), + main: fn + }; + this.context.programs.map(function(program, index) { + if (program) { + ret[index] = program; + } + }); - preamble: function() { - var out = []; + if (this.environment.usePartial) { + ret.usePartial = true; + } + if (this.options.data) { + ret.useData = true; + } - if (!this.isChild) { - var namespace = this.namespace; + if (!asObject) { + ret.compiler = JSON.stringify(ret.compiler); + ret = this.objectLiteral(ret); + } - var copies = "helpers = this.merge(helpers, " + namespace + ".helpers);"; - if (this.environment.usePartial) { copies = copies + " partials = this.merge(partials, " + namespace + ".partials);"; } - if (this.options.data) { copies = copies + " data = this.initData(depth0, data);"; } - out.push(copies); + return ret; } else { - out.push(''); + return fn; } + }, + + preamble: function() { + var out = []; if (!this.environment.isSimple) { out.push(", buffer = " + this.initializeBuffer()); @@ -141,32 +158,25 @@ JavaScriptCompiler.prototype = { var locals = this.stackVars.concat(this.registers.list); if(locals.length > 0) { - this.source[1] = this.source[1] + ", " + locals.join(", "); + this.source[0] += ", " + locals.join(", "); } // Generate minimizer alias mappings - if (!this.isChild) { - for (var alias in this.context.aliases) { - if (this.context.aliases.hasOwnProperty(alias)) { - this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; - } + for (var alias in this.aliases) { + if (this.aliases.hasOwnProperty(alias)) { + this.source[0] += ', ' + alias + '=' + this.aliases[alias]; } } - if (this.source[1]) { - this.source[1] = "var " + this.source[1].substring(2) + ";"; - } - - // Merge children - if (!this.isChild) { - this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; + if (this.source[0]) { + this.source[0] = "var " + this.source[0].substring(2) + ";"; } if (!this.environment.isSimple) { this.pushSource("return buffer;"); } - var params = this.isChild ? ["depth0", "data"] : [this.namespace, "depth0", "helpers", "partials", "data"]; + var params = ["depth0", "helpers", "partials", "data"]; for(var i=0, l=this.environment.depths.list.length; i<l; i++) { params.push("depth" + this.environment.depths.list[i]); @@ -175,18 +185,12 @@ JavaScriptCompiler.prototype = { // Perform a second pass over the output to merge content when possible var source = this.mergeSource(); - if (!this.isChild) { - source = this.compilerInfo()+source; - } - if (asObject) { params.push(source); return Function.apply(this, params); } else { - var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}'; - log('debug', functionSource + "\n\n"); - return functionSource; + return 'function(' + params.join(',') + ') {\n ' + source + '}'; } }, mergeSource: function() { @@ -223,7 +227,7 @@ JavaScriptCompiler.prototype = { // replace it on the stack with the result of properly // invoking blockHelperMissing. blockValue: function(name) { - this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; + this.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; var params = ["depth0"]; this.setupParams(name, 0, params); @@ -241,7 +245,7 @@ JavaScriptCompiler.prototype = { // On stack, after, if no lastHelper: same as [blockValue] // On stack, after, if lastHelper: value ambiguousBlockValue: function() { - this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; + this.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; // We're being a bit cheeky and reusing the options value from the prior exec var params = ["depth0"]; @@ -313,7 +317,7 @@ JavaScriptCompiler.prototype = { // // Escape `value` and append it to the buffer appendEscaped: function() { - this.context.aliases.escapeExpression = 'this.escapeExpression'; + this.aliases.escapeExpression = 'this.escapeExpression'; this.pushSource(this.appendToBuffer("escapeExpression(" + this.popStack() + ")")); }, @@ -360,7 +364,7 @@ JavaScriptCompiler.prototype = { // 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"'; + this.aliases.functionType = '"function"'; this.replaceStack(function(current) { return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current; @@ -505,7 +509,7 @@ JavaScriptCompiler.prototype = { // // If the helper is not found, `helperMissing` is called. invokeHelper: function(paramSize, name, isRoot) { - this.context.aliases.helperMissing = 'helpers.helperMissing'; + this.aliases.helperMissing = 'helpers.helperMissing'; this.useRegister('helper'); var nonHelper = this.popStack(); @@ -551,7 +555,7 @@ JavaScriptCompiler.prototype = { // and can be avoided by passing the `knownHelpers` and // `knownHelpersOnly` flags at compile-time. invokeAmbiguous: function(name, helperCall) { - this.context.aliases.functionType = '"function"'; + this.aliases.functionType = '"function"'; this.useRegister('helper'); this.emptyHash(); @@ -580,8 +584,7 @@ JavaScriptCompiler.prototype = { params.push("data"); } - this.context.aliases.self = "this"; - this.push("self.invokePartial(" + params.join(", ") + ")"); + this.push("this.invokePartial(" + params.join(", ") + ")"); }, // [assignToHash] @@ -646,7 +649,7 @@ JavaScriptCompiler.prototype = { index = this.context.programs.length; child.index = index; child.name = 'program' + index; - this.context.programs[index] = compiler.compile(child, options, this.context); + this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile); this.context.environments[index] = child; } else { child.index = index; @@ -664,25 +667,22 @@ JavaScriptCompiler.prototype = { }, programExpression: function(guid) { - this.context.aliases.self = "this"; - if(guid == null) { - return "self.noop"; + return 'this.noop'; } var child = this.environment.children[guid], depths = child.depths.list, depth; - var programParams = [child.index, child.name, "data"]; + var programParams = [child.index, 'data']; for(var i=0, l = depths.length; i<l; i++) { depth = depths[i]; - if(depth === 1) { programParams.push("depth0"); } - else { programParams.push("depth" + (depth - 1)); } + programParams.push('depth' + (depth - 1)); } - return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")"; + return (depths.length === 0 ? 'this.program(' : 'this.programWithDepth(') + programParams.join(', ') + ')'; }, register: function(name, val) { @@ -840,7 +840,7 @@ JavaScriptCompiler.prototype = { .replace(/\u2029/g, '\\u2029') + '"'; }, - setupHash: function(obj) { + objectLiteral: function(obj) { var pairs = []; for (var key in obj) { @@ -886,13 +886,11 @@ JavaScriptCompiler.prototype = { // helpers to do a check for `if (options.fn)` if (program || inverse) { if (!program) { - this.context.aliases.self = "this"; - program = "self.noop"; + program = 'this.noop'; } if (!inverse) { - this.context.aliases.self = "this"; - inverse = "self.noop"; + inverse = 'this.noop'; } options.fn = program; @@ -930,7 +928,7 @@ JavaScriptCompiler.prototype = { // the params and contexts arguments are passed in arrays // to fill in setupParams: function(helperName, paramSize, params, useRegister) { - var options = this.setupHash(this.setupOptions(helperName, paramSize, params)); + var options = this.objectLiteral(this.setupOptions(helperName, paramSize, params)); if (useRegister) { this.useRegister('options'); diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index 6014691..e65db6e 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -29,6 +29,8 @@ export function template(templateSpec, env) { // Note: Using env.VM references rather than local var references throughout this section to allow // for external users to override these as psuedo-supported APIs. + env.VM.checkRevision(templateSpec.compiler); + var invokePartialWrapper = function(partial, name, context, hash, helpers, partials, data) { if (hash) { context = Utils.extend({}, context, hash); @@ -50,16 +52,24 @@ export function template(templateSpec, env) { var container = { escapeExpression: Utils.escapeExpression, invokePartial: invokePartialWrapper, + + fn: function(i) { + return templateSpec[i]; + }, + programs: [], - program: function(i, fn, data) { - var programWrapper = this.programs[i]; + program: function(i, data) { + var programWrapper = this.programs[i], + fn = this.fn(i); if(data) { - programWrapper = program(i, fn, data); + programWrapper = program(this, i, fn, data); } else if (!programWrapper) { - programWrapper = this.programs[i] = program(i, fn); + programWrapper = this.programs[i] = program(this, i, fn); } return programWrapper; }, + programWithDepth: env.VM.programWithDepth, + initData: function(context, data) { if (!data || !('root' in data)) { data = data ? createFrame(data) : {}; @@ -76,54 +86,56 @@ export function template(templateSpec, env) { return ret; }, - programWithDepth: env.VM.programWithDepth, + noop: env.VM.noop, - compilerInfo: null + compilerInfo: templateSpec.compiler }; return function(context, options) { options = options || {}; var namespace = options.partial ? options : env, helpers, - partials; + partials, + data = options.data; if (!options.partial) { - helpers = options.helpers; - partials = options.partials; - } - var result = templateSpec.call( - container, - namespace, context, - helpers, - partials, - options.data); + helpers = container.helpers = container.merge(options.helpers, namespace.helpers); - if (!options.partial) { - env.VM.checkRevision(container.compilerInfo); + if (templateSpec.usePartial) { + partials = container.partials = container.merge(options.partials, namespace.partials); + } + if (templateSpec.useData) { + data = container.initData(context, data); + } + } else { + helpers = container.helpers = options.helpers; + partials = container.partials = options.partials; } - - return result; + return templateSpec.main.call(container, context, helpers, partials, data); }; } -export function programWithDepth(i, fn, data /*, $depth */) { - var args = Array.prototype.slice.call(arguments, 3); +export function programWithDepth(i, data /*, $depth */) { + /*jshint -W040 */ + var args = Array.prototype.slice.call(arguments, 2), + container = this, + fn = container.fn(i); var prog = function(context, options) { options = options || {}; - return fn.apply(this, [context, options.data || data].concat(args)); + return fn.apply(container, [context, container.helpers, container.partials, options.data || data].concat(args)); }; prog.program = i; prog.depth = args.length; return prog; } -export function program(i, fn, data) { +export function program(container, i, fn, data) { var prog = function(context, options) { options = options || {}; - return fn(context, options.data || data); + return fn.call(container, context, container.helpers, container.partials, options.data || data); }; prog.program = i; prog.depth = 0; diff --git a/spec/expected/empty.amd.js b/spec/expected/empty.amd.js index ca80b88..ae6fdf9 100644 --- a/spec/expected/empty.amd.js +++ b/spec/expected/empty.amd.js @@ -1,11 +1,7 @@ define(['handlebars.runtime'], function(Handlebars) { Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {}; -return templates['empty'] = template(function (Handlebars,depth0,helpers,partials,data) { - this.compilerInfo = [4,'>= 1.0.0']; -helpers = this.merge(helpers, Handlebars.helpers); data = this.initData(depth0, data); +return templates['empty'] = template({"compiler":[4,">= 1.0.0"],"main":function(depth0,helpers,partials,data) { var buffer = ""; - - return buffer; - }); + },"useData":true}); }); diff --git a/spec/javascript-compiler.js b/spec/javascript-compiler.js index 884abf1..a9ae0f8 100644 --- a/spec/javascript-compiler.js +++ b/spec/javascript-compiler.js @@ -32,7 +32,7 @@ describe('javascript-compiler api', function() { }); it('should allow compilerInfo override', function() { handlebarsEnv.JavaScriptCompiler.prototype.compilerInfo = function() { - return 'this.compilerInfo = "crazy";'; + return 'crazy'; }; handlebarsEnv.VM.checkRevision = function(compilerInfo) { if (compilerInfo !== 'crazy') { |