import { COMPILER_REVISION, REVISION_CHANGES } from "../base"; function Literal(value) { this.value = value; }; export function JavaScriptCompiler() {}; JavaScriptCompiler.prototype = { // PUBLIC API: You can override these methods in a subclass to provide // alternative compiled forms for name lookup and buffering semantics nameLookup: function(parent, name /* , type*/) { if (/^[0-9]+$/.test(name)) { return parent + "[" + name + "]"; } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { return parent + "." + name; } else { return parent + "['" + name + "']"; } }, appendToBuffer: function(string) { if (this.environment.isSimple) { return "return " + string + ";"; } else { return { appendToBuffer: true, content: string, toString: function() { return "buffer += " + string + ";"; } }; } }, initializeBuffer: function() { return this.quotedString(""); }, namespace: "Handlebars", // END PUBLIC API compile: function(environment, options, context, asObject) { this.environment = environment; this.options = options || {}; // TODO: a module-compatible logger // Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n"); log(this.environment.disassemble() + "\n\n"); this.name = this.environment.name; this.isChild = !!context; this.context = context || { programs: [], environments: [], aliases: { } }; this.preamble(); this.stackSlot = 0; this.stackVars = []; this.registers = { list: [] }; this.compileStack = []; this.inlineStack = []; this.compileChildren(environment, options); var opcodes = environment.opcodes, opcode; this.i = 0; for(var l=opcodes.length; this.i 0) { this.source[1] = this.source[1] + ", " + 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]; } } } 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.environment.isSimple) { this.source.push("return buffer;"); } var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } return this.topStackName(); }, topStackName: function() { return "stack" + this.stackSlot; }, flushInline: function() { var inlineStack = this.inlineStack; if (inlineStack.length) { this.inlineStack = []; for (var i = 0, len = inlineStack.length; i < len; i++) { var entry = inlineStack[i]; if (entry instanceof Literal) { this.compileStack.push(entry); } else { this.pushStack(entry); } } } }, isInline: function() { return this.inlineStack.length; }, popStack: function(wrapped) { var inline = this.isInline(), item = (inline ? this.inlineStack : this.compileStack).pop(); if (!wrapped && (item instanceof Literal)) { return item.value; } else { if (!inline) { this.stackSlot--; } return item; } }, topStack: function(wrapped) { var stack = (this.isInline() ? this.inlineStack : this.compileStack), item = stack[stack.length - 1]; if (!wrapped && (item instanceof Literal)) { return item.value; } else { return item; } }, quotedString: function(str) { return '"' + str .replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 .replace(/\u2029/g, '\\u2029') + '"'; }, setupHelper: function(paramSize, name, missingParams) { var params = []; this.setupParams(paramSize, params, missingParams); var foundHelper = this.nameLookup('helpers', name, 'helper'); return { params: params, name: foundHelper, callParams: ["depth0"].concat(params).join(", "), helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") }; }, // the params and contexts arguments are passed in arrays // to fill in setupParams: function(paramSize, params, useRegister) { var options = [], contexts = [], types = [], 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