diff options
author | kpdecker <kpdecker@gmail.com> | 2014-01-01 19:14:51 -0600 |
---|---|---|
committer | kpdecker <kpdecker@gmail.com> | 2014-01-01 19:14:51 -0600 |
commit | 6e4e1f84040d8c7ee415ef711be7572ec94f3eb6 (patch) | |
tree | 90445963d0a48378bc200158d8d85fd4ce124dad | |
parent | 3c85720137c039cefc38046541f05731d151a506 (diff) | |
download | handlebars.js-6e4e1f84040d8c7ee415ef711be7572ec94f3eb6.zip handlebars.js-6e4e1f84040d8c7ee415ef711be7572ec94f3eb6.tar.gz handlebars.js-6e4e1f84040d8c7ee415ef711be7572ec94f3eb6.tar.bz2 |
Include line info in compiler thrown exceptions
Fixes #636
-rw-r--r-- | lib/handlebars/base.js | 2 | ||||
-rw-r--r-- | lib/handlebars/compiler/ast.js | 19 | ||||
-rw-r--r-- | lib/handlebars/compiler/compiler.js | 5 | ||||
-rw-r--r-- | lib/handlebars/exception.js | 16 | ||||
-rw-r--r-- | lib/handlebars/runtime.js | 6 | ||||
-rw-r--r-- | spec/ast.js | 18 |
6 files changed, 41 insertions, 25 deletions
diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index 027f052..11667af 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -53,7 +53,7 @@ function registerDefaultHelpers(instance) { if(arguments.length === 2) { return undefined; } else { - throw new Error("Missing helper: '" + arg + "'"); + throw new Exception("Missing helper: '" + arg + "'"); } }); diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index 09bf927..002fd3b 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -106,12 +106,12 @@ var AST = { }, BlockNode: function(mustache, program, inverse, close, locInfo) { + LocationInfo.call(this, locInfo); + if(mustache.sexpr.id.original !== close.path.original) { - throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original); + throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original, this); } - LocationInfo.call(this, locInfo); - this.type = 'block'; this.mustache = mustache; this.program = program; @@ -155,11 +155,16 @@ var AST = { original += (parts[i].separator || '') + part; if (part === ".." || part === "." || part === "this") { - if (dig.length > 0) { throw new Exception("Invalid path: " + original); } - else if (part === "..") { depth++; } - else { this.isScoped = true; } + if (dig.length > 0) { + throw new Exception("Invalid path: " + original, this); + } else if (part === "..") { + depth++; + } else { + this.isScoped = true; + } + } else { + dig.push(part); } - else { dig.push(part); } } this.original = original; diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js index 0b25ba5..461f98b 100644 --- a/lib/handlebars/compiler/compiler.js +++ b/lib/handlebars/compiler/compiler.js @@ -279,7 +279,7 @@ Compiler.prototype = { if (this.options.knownHelpers[name]) { this.opcode('invokeKnownHelper', params.length, name); } else if (this.options.knownHelpersOnly) { - throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name); + throw new Exception("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr); } else { this.opcode('invokeHelper', params.length, name, sexpr.isRoot); } @@ -316,7 +316,7 @@ Compiler.prototype = { DATA: function(data) { this.options.data = true; if (data.id.isScoped || data.id.depth) { - throw new Exception('Scoped data references are not supported: ' + data.original); + throw new Exception('Scoped data references are not supported: ' + data.original, data); } this.opcode('lookupData'); @@ -350,7 +350,6 @@ Compiler.prototype = { }, addDepth: function(depth) { - if(isNaN(depth)) { throw new Error("EWOT"); } if(depth === 0) { return; } if(!this.depths[depth]) { diff --git a/lib/handlebars/exception.js b/lib/handlebars/exception.js index 6de9cfd..8c5c2f6 100644 --- a/lib/handlebars/exception.js +++ b/lib/handlebars/exception.js @@ -1,13 +1,25 @@ var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; -function Exception(/* message */) { - var tmp = Error.prototype.constructor.apply(this, arguments); +function Exception(message, node) { + var line; + if (node && node.firstLine) { + line = node.firstLine; + + message += ' - ' + line + ':' + node.firstColumn; + } + + var tmp = Error.prototype.constructor.call(this, message); // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. for (var idx = 0; idx < errorProps.length; idx++) { this[errorProps[idx]] = tmp[errorProps[idx]]; } + + if (line) { + this.lineNumber = line; + this.column = node.firstColumn; + } } Exception.prototype = new Error(); diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index 5c63645..5ae8d8a 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -10,11 +10,11 @@ export function checkRevision(compilerInfo) { if (compilerRevision < currentRevision) { var runtimeVersions = REVISION_CHANGES[currentRevision], compilerVersions = REVISION_CHANGES[compilerRevision]; - throw new Error("Template was precompiled with an older version of Handlebars than the current runtime. "+ + throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+ "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+")."); } else { // Use the embedded version info since the runtime doesn't know about this revision yet - throw new Error("Template was precompiled with a newer version of Handlebars than the current runtime. "+ + throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+ "Please update your runtime to a newer version ("+compilerInfo[1]+")."); } } @@ -24,7 +24,7 @@ export function checkRevision(compilerInfo) { export function template(templateSpec, env) { if (!env) { - throw new Error("No environment passed to template"); + throw new Exception("No environment passed to template"); } // Note: Using env.VM references rather than local var references throughout this section to allow diff --git a/spec/ast.js b/spec/ast.js index 0cb1103..46f0131 100644 --- a/spec/ast.js +++ b/spec/ast.js @@ -78,8 +78,8 @@ describe('ast', function() { shouldThrow(function() { var sexprNode = new handlebarsEnv.AST.SexprNode([{ original: 'foo'}], null); var mustacheNode = new handlebarsEnv.AST.MustacheNode(sexprNode, null, '{{', {}); - new handlebarsEnv.AST.BlockNode(mustacheNode, {}, {}, {path: {original: 'bar'}}); - }, Handlebars.Exception, "foo doesn't match bar"); + new handlebarsEnv.AST.BlockNode(mustacheNode, {}, {}, {path: {original: 'bar'}}, {first_line: 2, first_column: 2}); + }, Handlebars.Exception, "foo doesn't match bar - 2:2"); }); it('stores location info', function(){ @@ -102,22 +102,22 @@ describe('ast', function() { {part: 'foo'}, {part: '..'}, {part: 'bar'} - ]); - }, Handlebars.Exception, "Invalid path: foo.."); + ], {first_line: 1, first_column: 1}); + }, Handlebars.Exception, "Invalid path: foo.. - 1:1"); shouldThrow(function() { new handlebarsEnv.AST.IdNode([ {part: 'foo'}, {part: '.'}, {part: 'bar'} - ]); - }, Handlebars.Exception, "Invalid path: foo."); + ], {first_line: 1, first_column: 1}); + }, Handlebars.Exception, "Invalid path: foo. - 1:1"); shouldThrow(function() { new handlebarsEnv.AST.IdNode([ {part: 'foo'}, {part: 'this'}, {part: 'bar'} - ]); - }, Handlebars.Exception, "Invalid path: foothis"); + ], {first_line: 1, first_column: 1}); + }, Handlebars.Exception, "Invalid path: foothis - 1:1"); }); it('stores location info', function(){ @@ -216,7 +216,7 @@ describe('ast', function() { firstColumn: 0, lastColumn: 0 }; - var pn = new handlebarsEnv.AST.ProgramNode([], {strip: {}}, [ clone ], LOCATION_INFO); + pn = new handlebarsEnv.AST.ProgramNode([], {strip: {}}, [ clone ], LOCATION_INFO); testLocationInfoStorage(pn); // Assert that the newly created ProgramNode has the same location |