diff --git a/lib/output.js b/lib/output.js index 1a95dd14..3f9dd78b 100644 --- a/lib/output.js +++ b/lib/output.js @@ -978,12 +978,12 @@ function OutputStream(options) { AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ var self = this; if (!nokeyword) { - if (this.async) { + if (self.async) { output.print("async"); output.space(); } output.print("function"); - if (this.is_generator) { + if (self.is_generator) { output.star(); } if (self.name) { @@ -1038,6 +1038,10 @@ function OutputStream(options) { var needs_parens = parent instanceof AST_Binary || parent instanceof AST_Unary || (parent instanceof AST_Call && self === parent.expression); + if (self.async) { + output.print("async"); + output.space(); + } if (needs_parens) { output.print("(") } if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) { self.argnames[0].print(output); @@ -1053,9 +1057,9 @@ function OutputStream(options) { output.print('=>'); output.space(); if (self.body instanceof AST_Node) { - this.body.print(output); + self.body.print(output); } else { - print_bracketed(this.body, output); + print_bracketed(self.body, output); } if (needs_parens) { output.print(")") } }); diff --git a/lib/parse.js b/lib/parse.js index 57e95a98..c9acca82 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1281,18 +1281,19 @@ function parse($TEXT, options) { }); }; - var arrow_function = function(start, argnames) { + var arrow_function = function(start, argnames, is_async) { if (S.token.nlb) { croak("Unexpected newline before arrow (=>)"); } expect_token("arrow", "=>"); - var body = _function_body(is("punc", "{")); + var body = _function_body(is("punc", "{"), false, is_async); return new AST_Arrow({ start : start, end : body.end, + async : is_async, argnames : argnames, body : body }); @@ -1989,21 +1990,26 @@ function parse($TEXT, options) { } } - var expr_atom = function(allow_calls) { + var expr_atom = function(allow_calls, allow_arrows) { if (is("operator", "new")) { return new_(allow_calls); } var start = S.token; + var async = is("name", "async") && as_atom_node(); if (is("punc")) { - switch (start.value) { + switch (S.token.value) { case "(": + if (async && !allow_calls) break; next(); var exprs = params_or_seq_(); expect(")"); - if (is("arrow", "=>")) { - return arrow_function(start, exprs.map(to_fun_args)); + if (allow_arrows && is("arrow", "=>")) { + return arrow_function(start, exprs.map(to_fun_args), !!async); } - var ex = exprs.length == 1 ? exprs[0] : new AST_Sequence({ + var ex = async ? new AST_Call({ + expression: async, + args: exprs + }) : exprs.length == 1 ? exprs[0] : new AST_Sequence({ expressions: exprs }); ex.start = start; @@ -2014,23 +2020,25 @@ function parse($TEXT, options) { case "{": return subscripts(object_or_destructuring_(), allow_calls); } - unexpected(); + if (!async) unexpected(); } - if (is("name", "async") && is_token(peek(), "keyword", "function")) { + if (allow_arrows && is("name") && is_token(peek(), "arrow")) { + var param = new AST_SymbolFunarg({ + name: S.token.value, + start: start, + end: start, + }); next(); - next(); - var func = function_(AST_Function, false, true); - func.start = start; - func.end = prev(); - return subscripts(func, allow_calls); + return arrow_function(start, [param], !!async); } if (is("keyword", "function")) { next(); - var func = function_(AST_Function); + var func = function_(AST_Function, false, !!async); func.start = start; func.end = prev(); return subscripts(func, allow_calls); } + if (async) return subscripts(async, allow_calls); if (is("keyword", "class")) { next(); var cls = class_(AST_ClassExpression); @@ -2619,7 +2627,7 @@ function parse($TEXT, options) { return args; }); - var maybe_unary = function(allow_calls) { + var maybe_unary = function(allow_calls, allow_arrows) { var start = S.token; if (start.type == "name" && start.value == "await") { if (is_in_async()) { @@ -2637,8 +2645,9 @@ function parse($TEXT, options) { ex.end = prev(); return ex; } - var val = expr_atom(allow_calls); + var val = expr_atom(allow_calls, allow_arrows); while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { + if (val instanceof AST_Arrow) unexpected(); val = make_unary(AST_UnaryPostfix, S.token, val); val.start = start; val.end = S.token; @@ -2686,7 +2695,7 @@ function parse($TEXT, options) { }; function expr_ops(no_in) { - return expr_op(maybe_unary(true), 0, no_in); + return expr_op(maybe_unary(true, true), 0, no_in); }; var maybe_conditional = function(no_in) { @@ -2767,22 +2776,6 @@ function parse($TEXT, options) { } } - if (start.type == "punc" && start.value == "(" && peek().value == ")") { - next(); - next(); - return arrow_function(start, []); - } - - if (is("name") && is_token(peek(), "arrow")) { - var param = new AST_SymbolFunarg({ - name: start.value, - start: start, - end: start, - }); - next(); - return arrow_function(start, [param]); - } - var left = maybe_conditional(no_in); var val = S.token.value; diff --git a/test/compress/arrow.js b/test/compress/arrow.js index 86ee1a84..a0a97e03 100644 --- a/test/compress/arrow.js +++ b/test/compress/arrow.js @@ -202,3 +202,11 @@ arrow_unused_toplevel: { expect_stdout: [ "0", "1", "2", "9" ] node_version: ">=6" } + +no_leading_parentheses: { + input: { + (x,y) => x(y); + async (x,y) => await x(y); + } + expect_exact: "(x,y)=>x(y);async(x,y)=>await x(y);" +} diff --git a/test/compress/async.js b/test/compress/async.js index 5f041d46..7bc47012 100644 --- a/test/compress/async.js +++ b/test/compress/async.js @@ -230,16 +230,26 @@ async_shorthand_property: { node_version: ">=4" } -/* FIXME: add test when supported by parser async_arrow: { input: { let a1 = async x => await foo(x); let a2 = async () => await bar(); let a3 = async (x) => await baz(x); let a4 = async (x, y) => { await far(x, y); } - let a5 = async ({x = [1], y: z = 2}) => { await wow(x, y); } + let a5 = async ({x = [1], y: z = 2}) => { await wow(x, z); } } expect: { + let a1 = async x => await foo(x); + let a2 = async () => await bar(); + let a3 = async (x) => await baz(x); + let a4 = async (x, y) => { await far(x, y); } + let a5 = async ({x = [1], y: z = 2}) => { await wow(x, z); } } } -*/ + +async_arrow_wait: { + input: { + var a = async (x, y) => await x(y); + } + expect_exact: "var a=async(x,y)=>await x(y);" +} diff --git a/test/compress/harmony.js b/test/compress/harmony.js index 45459eee..9d36f429 100644 --- a/test/compress/harmony.js +++ b/test/compress/harmony.js @@ -16,9 +16,12 @@ typeof_arrow_functions: { evaluate: true } input: { - var foo = typeof (x) => null; + var foo = typeof (x => null); + console.log(foo); } - expect_exact: "var foo=\"function\";" + expect_exact: "var foo=\"function\";console.log(foo);" + expect_stdout: "function" + node_version: ">=4" } classes: { diff --git a/test/mocha/arrow.js b/test/mocha/arrow.js index 51744919..35645612 100644 --- a/test/mocha/arrow.js +++ b/test/mocha/arrow.js @@ -62,23 +62,20 @@ describe("Arrow functions", function() { } }); it("Should not accept arrow functions in the middle or end of an expression", function() { - var tests = [ + [ + "0 + x => 0", + "0 + async x => 0", "typeof x => 0", - "0 + x => 0" - ]; - var test = function(code) { - return function() { + "typeof async x => 0", + "typeof (x) => null", + "typeof async (x) => null", + ].forEach(function(code) { + assert.throws(function() { uglify.parse(code); - } - } - var error = function(e) { - return e instanceof uglify.JS_Parse_Error && - e.message === "Unexpected token: arrow (=>)"; - } - - for (var i = 0; i < tests.length; i++) { - assert.throws(test(tests[i]), error); - } + }, function(e) { + return e instanceof uglify.JS_Parse_Error && /^Unexpected /.test(e.message); + }, code); + }); }); it("Should parse a function containing default assignment correctly", function() {