Uglifyjs: [ES8] async arrow functions not supported

Created on 15 Jun 2017  路  24Comments  路  Source: mishoo/UglifyJS

Follow up issue to #1789

$ bin/uglifyjs -V
uglify-es 3.0.16
$ echo 'async()=>1;' | bin/uglifyjs
Parse error at 0:1,7
async()=>1;
       ^
ERROR: Unexpected token: arrow (=>)
harmony

All 24 comments

@alexlamsl If only async were not a valid identifier and was a keyword instead then this would be trivial be implement.

Indeed - I thought the whole point of having all these reserved but unused keywords was to allow for future language expansion. And then when they needed one... :smirk:

Now that I'm looking into it - there isn't any mention of async arrow functions on MDN

You can see it in the ECMAScript 2018 Language Specification:

which would make it ES9 rather than ES8 as I first thought. Edit: see https://github.com/mishoo/UglifyJS2/issues/2102#issuecomment-310721938

However it happens to run on acorn with the ecma2017 flag:

$ echo 'let a =  async (x, y) => await x - y;' | node_modules/.bin/acorn --ecma2017
{
  "type": "Program",
  "start": 0,
  "end": 38,
  "body": [
    {
      "type": "VariableDeclaration",
      "start": 0,
      "end": 37,
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 4,
          "end": 36,
          "id": {
            "type": "Identifier",
            "start": 4,
            "end": 5,
            "name": "a"
          },
          "init": {
            "type": "ArrowFunctionExpression",
            "start": 9,
            "end": 36,
            "id": null,
            "generator": false,
            "expression": true,
            "async": true,
...

Numerous acorn test cases:

FireFox 54 and Chrome 59 allow it:

let a =  async (x, y) => await x - y;
alert(a);

babel allows it:

$ echo 'let a = async (x, y) => await x - y;' | babel
"use strict";

var _this = this;

var a = function a(x, y) {
  return regeneratorRuntime.async(function a$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        context$1$0.next = 2;
        return regeneratorRuntime.awrap(x);

      case 2:
        context$1$0.t0 = context$1$0.sent;
        context$1$0.t1 = y;
        return context$1$0.abrupt("return", context$1$0.t0 - context$1$0.t1);

      case 5:
      case "end":
        return context$1$0.stop();
    }
  }, null, _this);
};

According to this link, the ECMAScript 8th Edition which introduces async/await is not yet finished:

https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_.28not_yet_finished.29

which is the reason for the confusion over the ES201x name.

I'm currently half way through this mess - past the async hurdle, but lib/parse.js doesn't like await within the arrow function body, so... joy!

The once proposed alternate async/await syntax would have been much easier to implement:

https://github.com/tc39/ecmascript-asyncawait#surface-syntax

Does Node.js 8 not support await?

$ echo 'await x()' | node
[stdin]:1
await x()
      ^

SyntaxError: Unexpected identifier
    at createScript (vm.js:74:10)
    at Object.runInThisContext (vm.js:116:10)

await may only appear directly within in an async function.

Search for await in https://github.com/ternjs/acorn/blob/master/test/tests-asyncawait.js for both the valid an invalid await use cases.

Oh, okay, even acorn needs a special flag to run.

@kzc at least I have your commented-out test cases to work with, so that's been very helpful :wink:

$ echo 'function foo(x) { return await x(); }' | node_modules/.bin/acorn --ecma2017
Unexpected token (1:31)

$ echo 'async function foo(x) { return await x(); }' | node_modules/.bin/acorn --ecma2017 | head -18
{
  "type": "Program",
  "start": 0,
  "end": 44,
  "body": [
    {
      "type": "FunctionDeclaration",
      "start": 0,
      "end": 43,
      "id": {
        "type": "Identifier",
        "start": 15,
        "end": 18,
        "name": "foo"
      },
      "generator": false,
      "expression": false,
      "async": true,

Almost there:

--- async.js
    Running test [await_precedence]
    Running test [async_function_declaration]
    Running test [async_function_expression]
    Running test [async_class]
    Running test [async_object_literal]
    Running test [async_export]
    Running test [async_inline]
    Running test [async_identifiers]
    Running test [async_shorthand_property]
    Running test [async_arrow]
!!! Test matched expected result but cannot parse output
---INPUT---
{
    let a1 = x => await foo(x);
    let a2 = () => await bar();
    let a3 = x => await baz(x);
    let a4 = (x, y) => {
        await far(x, y);
    };
    let a5 = ({x: x = [ 1 ], y: z = 2}) => {
        await wow(x, y);
    };
}
---OUTPUT---
let a1=x=>await foo(x);let a2=()=>await bar();let a3=x=>await baz(x);let a4=(x,y)=>{await far(x,y)};let a5=({x:x=[1],y:z=2})=>{await wow(x,y)};
--REPARSE ERROR--
SyntaxError: Unexpected token: name (foo)

That one async_arrow test case I wrote:

        let a5 = async ({x = [1], y: z = 2}) => { await wow(x, y); }

should probably be:

        let a5 = async ({x = [1], y: z = 2}) => { await wow(x, z); }

Since z is a an arrow function parameter, not y.

--REPARSE ERROR--

That test runner feature saved me a lot of grief implementing new stuff.

The REPARSE error is correct as the await operator is not allowed in a non-async function:

let a1=x=>await foo(x);let a2=()=>await bar();let a3=x=>await baz(x);let a4=(x,y)=>{await far(x,y)};let a5=({x:x=[1],y:z=2})=>{await wow(x,y)};
--REPARSE ERROR--
SyntaxError: Unexpected token: name (foo)

But how did it parse in the first place?

But how did it parse in the first place?

lib/output.js does not handle AST_Arrow.async at all, hence the fireworks.

Currently stuck on this test:

$ type test.js
typeof (x) => 0;

$ uglifyjs test.js
typeof(x=>0);

$ type test.js | node
[stdin]:1
typeof (x) => 0;
^^^^^^

SyntaxError: Unexpected token typeof
    at createScript (vm.js:74:10)

test/mocha/arrow.js only tested the case without parenthesis, i.e. typeof x => 0;, which does correctly fail on current harmony

Which has higher precedence - typeof or =>?

Presumably typeof.

=> is an operator?

=> is an operator?

You're correct that it's not an operator, but I don't know what to call it.

Should typeof x => 0 be parsed as (typeof x) => 0 or typeof (x => 0)?

Since acorn errors out with:

$ echo 'typeof x => 0' | node_modules/.bin/acorn 
Unexpected token (1:9)

typeof appears to have higher precedence.

This may also be worth testing:

$ echo 'typeof async x => 0' | node_modules/.bin/acorn --ecma2017
Unexpected token (1:13)
$ echo 'typeof (async x => 0)' | node_modules/.bin/acorn --ecma2017
{
  "type": "Program",
...valid...

In case that sounded pedantic - was just confused as I only know precedence in the context of operators :sweat_smile:

Your cases above should be taken care of in the upcoming PR - have to fix up test/compress/harmony.js with invalid syntax. If only they have expect_stdout back then... :smirk:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

diegocr picture diegocr  路  3Comments

GrosSacASac picture GrosSacASac  路  3Comments

kzc picture kzc  路  5Comments

Havunen picture Havunen  路  5Comments

JoeUX picture JoeUX  路  3Comments