From 98ab76076eb10902e212a016622525f55a91e0d1 Mon Sep 17 00:00:00 2001 From: Chris Armstrong Date: Tue, 18 Jun 2019 14:36:00 +1000 Subject: [PATCH 1/4] standardize error output --- jmespath.js | 70 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/jmespath.js b/jmespath.js index 0a36691..89528f8 100644 --- a/jmespath.js +++ b/jmespath.js @@ -233,6 +233,19 @@ ch === "_"; } + function createErrorClass(className) { + var ExtendedError = function (message) { + Error.call(this, message); + this.name = className; + } + ExtendedError.prototype = Object.create(Error.prototype); + ExtendedError.prototype.constructor = ExtendedError; + + return ExtendedError; + } + + var LexerError = createErrorClass("LexerError"); + function Lexer() { } Lexer.prototype = { @@ -304,8 +317,7 @@ tokens.push({type: TOK_PIPE, value: "|", start: start}); } } else { - var error = new Error("Unknown character:" + stream[this._current]); - error.name = "LexerError"; + var error = new LexerError("Unknown character:" + stream[this._current]); throw error; } } @@ -500,6 +512,8 @@ bindingPower[TOK_LBRACKET] = 55; bindingPower[TOK_LPAREN] = 60; + var ParserError = createErrorClass("ParserError"); + function Parser() { } @@ -510,9 +524,8 @@ var ast = this.expression(0); if (this._lookahead(0) !== TOK_EOF) { var t = this._lookaheadToken(0); - var error = new Error( + var error = new ParserError( "Unexpected token type: " + t.type + ", value: " + t.value); - error.name = "ParserError"; throw error; } return ast; @@ -562,7 +575,7 @@ case TOK_QUOTEDIDENTIFIER: var node = {type: "Field", name: token.value}; if (this._lookahead(0) === TOK_LPAREN) { - throw new Error("Quoted identifier not allowed for function names."); + throw new ParserError("Quoted identifier not allowed for function names."); } return node; case TOK_NOT: @@ -704,17 +717,15 @@ this._advance(); } else { var t = this._lookaheadToken(0); - var error = new Error("Expected " + tokenType + ", got: " + t.type); - error.name = "ParserError"; + var error = new ParserError("Expected " + tokenType + ", got: " + t.type); throw error; } }, _errorToken: function(token) { - var error = new Error("Invalid token (" + + var error = new ParserError("Invalid token (" + token.type + "): \"" + token.value + "\""); - error.name = "ParserError"; throw error; }, @@ -759,9 +770,8 @@ this._advance(); } else { var t = this._lookahead(0); - var error = new Error("Syntax error, unexpected token: " + + var error = new ParserError("Syntax error, unexpected token: " + t.value + "(" + t.type + ")"); - error.name = "Parsererror"; throw error; } currentToken = this._lookahead(0); @@ -805,9 +815,8 @@ right = this._parseDotRHS(rbp); } else { var t = this._lookaheadToken(0); - var error = new Error("Sytanx error, unexpected token: " + + var error = new ParserError("Syntax error, unexpected token: " + t.value + "(" + t.type + ")"); - error.name = "ParserError"; throw error; } return right; @@ -821,7 +830,7 @@ if (this._lookahead(0) === TOK_COMMA) { this._match(TOK_COMMA); if (this._lookahead(0) === TOK_RBRACKET) { - throw new Error("Unexpected token Rbracket"); + throw new ParserError("Unexpected token Rbracket"); } } } @@ -836,7 +845,7 @@ for (;;) { keyToken = this._lookaheadToken(0); if (identifierTypes.indexOf(keyToken.type) < 0) { - throw new Error("Expecting an identifier token, got: " + + throw new ParserError("Expecting an identifier token, got: " + keyToken.type); } keyName = keyToken.value; @@ -856,6 +865,7 @@ } }; + var InterpreterError = createErrorClass("InterpreterError"); function TreeInterpreter(runtime) { this.runtime = runtime; @@ -997,7 +1007,7 @@ result = first <= second; break; default: - throw new Error("Unknown comparator: " + node.name); + throw new InterpreterError("Unknown comparator: " + node.name); } return result; case TOK_FLATTEN: @@ -1073,7 +1083,7 @@ refNode.jmespathType = TOK_EXPREF; return refNode; default: - throw new Error("Unknown node type: " + node.type); + throw new InterpreterError("Unknown node type: " + node.type); } }, @@ -1122,6 +1132,10 @@ }; + var RuntimeError = createErrorClass("RuntimeError"); + var ArgumentError = createErrorClass("ArgumentError"); + var TypeMismatchError = createErrorClass("TypeMismatchError"); + function Runtime(interpreter) { this._interpreter = interpreter; this.functionTable = { @@ -1209,7 +1223,7 @@ callFunction: function(name, resolvedArgs) { var functionEntry = this.functionTable[name]; if (functionEntry === undefined) { - throw new Error("Unknown function: " + name + "()"); + throw new RuntimeError("Unknown function: " + name + "()"); } this._validateArgs(name, resolvedArgs, functionEntry._signature); return functionEntry._func.call(this, resolvedArgs); @@ -1225,13 +1239,13 @@ if (signature[signature.length - 1].variadic) { if (args.length < signature.length) { pluralized = signature.length === 1 ? " argument" : " arguments"; - throw new Error("ArgumentError: " + name + "() " + + throw new ArgumentError(name + "() " + "takes at least" + signature.length + pluralized + " but received " + args.length); } } else if (args.length !== signature.length) { pluralized = signature.length === 1 ? " argument" : " arguments"; - throw new Error("ArgumentError: " + name + "() " + + throw new ArgumentError(name + "() " + "takes " + signature.length + pluralized + " but received " + args.length); } @@ -1254,7 +1268,7 @@ return TYPE_NAME_TABLE[typeIdentifier]; }) .join(','); - throw new Error("TypeError: " + name + "() " + + throw new TypeMismatchError(name + "() " + "expected argument " + (i + 1) + " to be type " + expected + " but received type " + @@ -1547,7 +1561,7 @@ var requiredType = this._getTypeName( interpreter.visit(exprefNode, sortedArray[0])); if ([TYPE_NUMBER, TYPE_STRING].indexOf(requiredType) < 0) { - throw new Error("TypeError"); + throw new TypeMismatchError("Array elements must be of type Number or String to sort"); } var that = this; // In order to get a stable sort out of an unstable @@ -1565,12 +1579,12 @@ var exprA = interpreter.visit(exprefNode, a[1]); var exprB = interpreter.visit(exprefNode, b[1]); if (that._getTypeName(exprA) !== requiredType) { - throw new Error( - "TypeError: expected " + requiredType + ", received " + + throw new TypeMismatchError( + "expected " + requiredType + ", received " + that._getTypeName(exprA)); } else if (that._getTypeName(exprB) !== requiredType) { - throw new Error( - "TypeError: expected " + requiredType + ", received " + + throw new TypeMismatchError( + "expected " + requiredType + ", received " + that._getTypeName(exprB)); } if (exprA > exprB) { @@ -1631,9 +1645,9 @@ var keyFunc = function(x) { var current = interpreter.visit(exprefNode, x); if (allowedTypes.indexOf(that._getTypeName(current)) < 0) { - var msg = "TypeError: expected one of " + allowedTypes + + var msg = "expected one of " + allowedTypes + ", received " + that._getTypeName(current); - throw new Error(msg); + throw new TypeMismatchError(msg); } return current; }; From d427ce844ffd2be032fbc0fbc067c469efde4b79 Mon Sep 17 00:00:00 2001 From: Chris Armstrong Date: Tue, 18 Jun 2019 15:19:48 +1000 Subject: [PATCH 2/4] fix missing semicolon --- jmespath.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jmespath.js b/jmespath.js index 89528f8..d3993a2 100644 --- a/jmespath.js +++ b/jmespath.js @@ -237,7 +237,7 @@ var ExtendedError = function (message) { Error.call(this, message); this.name = className; - } + }; ExtendedError.prototype = Object.create(Error.prototype); ExtendedError.prototype.constructor = ExtendedError; From 356a49ca53df9258cefe524c9b57aab87f590a6d Mon Sep 17 00:00:00 2001 From: Chris Armstrong Date: Tue, 18 Jun 2019 17:31:31 +1000 Subject: [PATCH 3/4] fix: generation of Error class --- jmespath.js | 21 ++++++++++++++++----- test/jmespath.js | 1 + 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/jmespath.js b/jmespath.js index d3993a2..1ed4609 100644 --- a/jmespath.js +++ b/jmespath.js @@ -234,12 +234,23 @@ } function createErrorClass(className) { - var ExtendedError = function (message) { - Error.call(this, message); - this.name = className; + function ExtendedError(message) { + var instance = new Error(message); + instance.name = className; + Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); + if (Error.captureStackTrace) { + Error.captureStackTrace(instance, ExtendedError) + } + return instance; }; - ExtendedError.prototype = Object.create(Error.prototype); - ExtendedError.prototype.constructor = ExtendedError; + ExtendedError.prototype = Object.create(Error.prototype, { + constructor: { + value: Error, + enumerable: false, + writeable: true, + configurable: true, + }, + }); return ExtendedError; } diff --git a/test/jmespath.js b/test/jmespath.js index 98d4ead..28f68cc 100644 --- a/test/jmespath.js +++ b/test/jmespath.js @@ -227,6 +227,7 @@ describe('search', function() { assert(e.message.search( 'expected argument 1 to be type string,array,object' ), e.message); + assert.strictEqual(e.name, 'TypeMismatchError'); assert(e.message.search('received type null'), e.message); } } From 9dee021855f485908eeccf9bce282d1b7085bbf2 Mon Sep 17 00:00:00 2001 From: Chris Armstrong Date: Tue, 18 Jun 2019 17:35:28 +1000 Subject: [PATCH 4/4] fix linting errors --- jmespath.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jmespath.js b/jmespath.js index 1ed4609..c654bb7 100644 --- a/jmespath.js +++ b/jmespath.js @@ -239,10 +239,10 @@ instance.name = className; Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); if (Error.captureStackTrace) { - Error.captureStackTrace(instance, ExtendedError) + Error.captureStackTrace(instance, ExtendedError); } return instance; - }; + } ExtendedError.prototype = Object.create(Error.prototype, { constructor: { value: Error,