[ES6] support async arrow functions (#2153)

fixes #2102
This commit is contained in:
Alex Lam S.L
2017-06-24 05:26:35 +08:00
committed by GitHub
parent 137e4c4753
commit 7b95b63ca1
6 changed files with 73 additions and 58 deletions

View File

@@ -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(")") }
});

View File

@@ -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;

View File

@@ -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);"
}

View File

@@ -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);"
}

View File

@@ -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: {

View File

@@ -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() {