From b4b9305db0d3c4682848ed0a4214f1fee332a078 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 19 Apr 2017 04:27:13 +0800 Subject: [PATCH] fix parser bugs & CLI reporting (#1827) fixes #1825 --- bin/uglifyjs | 16 +++---- lib/parse.js | 27 +++++------- test/input/invalid/assign_4.js | 1 + test/input/invalid/dot_1.js | 1 + test/input/invalid/dot_2.js | 1 + test/input/invalid/dot_3.js | 1 + test/input/invalid/object.js | 1 + test/mocha/cli.js | 79 +++++++++++++++++++++++++++++++++- 8 files changed, 101 insertions(+), 26 deletions(-) create mode 100644 test/input/invalid/assign_4.js create mode 100644 test/input/invalid/dot_1.js create mode 100644 test/input/invalid/dot_2.js create mode 100644 test/input/invalid/dot_3.js create mode 100644 test/input/invalid/object.js diff --git a/bin/uglifyjs b/bin/uglifyjs index f31529b0..e89b68a7 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -190,19 +190,19 @@ function run() { if (ex instanceof UglifyJS.JS_Parse_Error) { console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col); var col = ex.col; - var line = files[ex.filename].split(/\r?\n/)[ex.line - (col ? 1 : 2)]; + var lines = files[ex.filename].split(/\r?\n/); + var line = lines[ex.line - 1]; + if (!line && !col) { + line = lines[ex.line - 2]; + col = line.length; + } if (line) { if (col > 40) { line = line.slice(col - 40); col = 40; } - if (col) { - console.error(line.slice(0, 80)); - console.error(line.slice(0, col).replace(/\S/g, " ") + "^"); - } else { - console.error(line.slice(-40)); - console.error(line.slice(-40).replace(/\S/g, " ") + "^"); - } + console.error(line.slice(0, 80)); + console.error(line.slice(0, col).replace(/\S/g, " ") + "^"); } } fatal("ERROR: " + ex.message); diff --git a/lib/parse.js b/lib/parse.js index 27351b53..40528df1 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -111,7 +111,7 @@ var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u20 var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029")); -var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:")); +var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:")); var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); @@ -1353,14 +1353,15 @@ function parse($TEXT, options) { function as_property_name() { var tmp = S.token; - next(); switch (tmp.type) { + case "operator": + if (!KEYWORDS(tmp.value)) unexpected(); case "num": case "string": case "name": - case "operator": case "keyword": case "atom": + next(); return tmp.value; default: unexpected(); @@ -1369,16 +1370,9 @@ function parse($TEXT, options) { function as_name() { var tmp = S.token; + if (tmp.type != "name") unexpected(); next(); - switch (tmp.type) { - case "name": - case "operator": - case "keyword": - case "atom": - return tmp.value; - default: - unexpected(); - } + return tmp.value; }; function _make_symbol(type) { @@ -1439,14 +1433,14 @@ function parse($TEXT, options) { if (is("operator") && UNARY_PREFIX(start.value)) { next(); handle_regexp(); - var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls)); + var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(allow_calls)); ex.start = start; ex.end = prev(); return ex; } var val = expr_atom(allow_calls); while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { - val = make_unary(AST_UnaryPostfix, S.token.value, val); + val = make_unary(AST_UnaryPostfix, S.token, val); val.start = start; val.end = S.token; next(); @@ -1454,9 +1448,10 @@ function parse($TEXT, options) { return val; }; - function make_unary(ctor, op, expr) { + function make_unary(ctor, token, expr) { + var op = token.value; if ((op == "++" || op == "--") && !is_assignable(expr)) - croak("Invalid use of " + op + " operator", null, ctor === AST_UnaryPrefix ? expr.start.col - 1 : null); + croak("Invalid use of " + op + " operator", token.line, token.col, token.pos); return new ctor({ operator: op, expression: expr }); }; diff --git a/test/input/invalid/assign_4.js b/test/input/invalid/assign_4.js new file mode 100644 index 00000000..d4d6b113 --- /dev/null +++ b/test/input/invalid/assign_4.js @@ -0,0 +1 @@ +++null diff --git a/test/input/invalid/dot_1.js b/test/input/invalid/dot_1.js new file mode 100644 index 00000000..7c4f3a66 --- /dev/null +++ b/test/input/invalid/dot_1.js @@ -0,0 +1 @@ +a.= diff --git a/test/input/invalid/dot_2.js b/test/input/invalid/dot_2.js new file mode 100644 index 00000000..32c027f8 --- /dev/null +++ b/test/input/invalid/dot_2.js @@ -0,0 +1 @@ +%.a; diff --git a/test/input/invalid/dot_3.js b/test/input/invalid/dot_3.js new file mode 100644 index 00000000..65573828 --- /dev/null +++ b/test/input/invalid/dot_3.js @@ -0,0 +1 @@ +a./(); diff --git a/test/input/invalid/object.js b/test/input/invalid/object.js new file mode 100644 index 00000000..46216d81 --- /dev/null +++ b/test/input/invalid/object.js @@ -0,0 +1 @@ +console.log({%: 1}); diff --git a/test/mocha/cli.js b/test/mocha/cli.js index 7162c816..697c09a3 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -296,14 +296,89 @@ describe("bin/uglifyjs", function () { assert.ok(err); assert.strictEqual(stdout, ""); assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ - "Parse error at test/input/invalid/assign_3.js:1,18", + "Parse error at test/input/invalid/assign_3.js:1,17", "console.log(3 || ++this);", - " ^", + " ^", + "ERROR: Invalid use of ++ operator" + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (++null)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/assign_4.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/assign_4.js:1,0", + "++null", + "^", "ERROR: Invalid use of ++ operator" ].join("\n")); done(); }); }); + it("Should throw syntax error (a.=)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/dot_1.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/dot_1.js:1,2", + "a.=", + " ^", + "ERROR: Unexpected token: operator (=)" + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (%.a)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/dot_2.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/dot_2.js:1,0", + "%.a;", + "^", + "ERROR: Unexpected token: operator (%)" + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (a./();)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/dot_3.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/dot_3.js:1,2", + "a./();", + " ^", + "ERROR: Unexpected token: operator (/)" + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error ({%: 1})", function(done) { + var command = uglifyjscmd + ' test/input/invalid/object.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/object.js:1,13", + "console.log({%: 1});", + " ^", + "ERROR: Unexpected token: operator (%)" + ].join("\n")); + done(); + }); + }); it("Should handle literal string as source map input", function(done) { var command = [ uglifyjscmd, -- 2.34.1