Skip to content

Non-trivial destructuring initializers are broken #8

Closed
@simonbuchan

Description

@simonbuchan

fast-async badly transforms destructuring initializers using anything other than the trivial forms const [a, b, c] = ... or const { a, b, c } = ...; inside any async functions, even if the RHS is not an await expression.
Further, it fails to parse array rest (ES2015, so not #6), and fails while transforming array eliding.

// work
async function trivialArray() {
  const [a, b] = await Promise.resolve([1, 2]);
  console.log(a, b);
}
async function trivialObject() {
  const { a, b } = await Promise.resolve({ a: 1, b: 2 });
  console.log(a, b);
}

// fails in parse
//async function arraySpread() {
//  const [a, ...b, c] = await Promise.resolve([1, 2, 3, 4]);
//  console.log(a, b, c);
//}

// fails in transform
//async function arrayElide() {
//  const [,a,,b] = await Promise.resolve([1, 2, 3, 4]);
//  console.log(a, b);
//}

// generate bad code, only declaring `var a, b`
async function rename() {
  const { a, b: c } = await Promise.resolve({ a: 1, b: 2 });
  console.log(a, c);
}
async function key() {
  const { a, ['b']: c } = await Promise.resolve({ a: 1, b: 2 });
  console.log(a, c);
}
async function nestedArray() {
  const { a, b: [c, d] } = await Promise.resolve({ a: 1, b: [2, 3] });
  console.log(a, c, d);
}
async function nestedObject() {
  const { a, b: { c, d } } = await Promise.resolve({ a: 1, b: { c: 2, d: 3 } });
  console.log(a, c, d);
}
async function indirect() {
  const result = await Promise.resolve({ a: 1, b: 2 });
  const { a, b: c } = result;
  console.log(a, c);
}

Output is:

% babel --preset es2015 --plugins syntax-async-functions,fast-async async.js
Function.prototype.$asyncbind = <snip>

// work
function trivialArray() {
  return new Promise(function ($return, $error) {
    var a, b;
    return Promise.resolve([1, 2]).then(function ($await_1) {
      [a, b] = $await_1;

      console.log(a, b);
      return $return();
    }.$asyncbind(this, $error), $error);
  }.$asyncbind(this));
}
function trivialObject() {
  return new Promise(function ($return, $error) {
    var a, b;
    return Promise.resolve({ a: 1, b: 2 }).then(function ($await_2) {
      ({ a, b } = $await_2);

      console.log(a, b);
      return $return();
    }.$asyncbind(this, $error), $error);
  }.$asyncbind(this));
}

// fails in parse
//async function arraySpread() {
//  const [a, ...b, c] = await Promise.resolve([1, 2, 3, 4]);
//  console.log(a, b, c);
//}

// fails in transform
//async function arrayElide() {
//  const [,a,,b] = await Promise.resolve([1, 2, 3, 4]);
//  console.log(a, b);
//}

// generate bad code, only declaring `var a, b`
function rename() {
  return new Promise(function ($return, $error) {
    var a, b;
    return Promise.resolve({ a: 1, b: 2 }).then(function ($await_3) {
      ({ a, b: c } = $await_3);

      console.log(a, c);
      return $return();
    }.$asyncbind(this, $error), $error);
  }.$asyncbind(this));
}
function key() {
  return new Promise(function ($return, $error) {
    var a, undefined;
    return Promise.resolve({ a: 1, b: 2 }).then(function ($await_4) {
      ({ a, ['b']: c } = $await_4);

      console.log(a, c);
      return $return();
    }.$asyncbind(this, $error), $error);
  }.$asyncbind(this));
}
function nestedArray() {
  return new Promise(function ($return, $error) {
    var a, b;
    return Promise.resolve({ a: 1, b: [2, 3] }).then(function ($await_5) {
      ({ a, b: [c, d] } = $await_5);

      console.log(a, c, d);
      return $return();
    }.$asyncbind(this, $error), $error);
  }.$asyncbind(this));
}
function nestedObject() {
  return new Promise(function ($return, $error) {
    var a, b;
    return Promise.resolve({ a: 1, b: { c: 2, d: 3 } }).then(function ($await_6) {
      ({ a, b: { c, d } } = $await_6);

      console.log(a, c, d);
      return $return();
    }.$asyncbind(this, $error), $error);
  }.$asyncbind(this));
}
function indirect() {
  return new Promise(function ($return, $error) {
    var result, a, b;
    return Promise.resolve({ a: 1, b: 2 }).then(function ($await_7) {
      result = $await_7;
      ({ a, b: c } = result);

      console.log(a, c);
      return $return();
    }.$asyncbind(this, $error), $error);
  }.$asyncbind(this));
}

Array rest gives error:

% babel --preset es2015 --plugins syntax-async-functions,fast-async async.js
SyntaxError: async.js: Unexpected token (10:16)
   8 | }
   9 | async function arraySpread() {
> 10 |   const [a, ...b, c] = await Promise.resolve([1, 2, 3, 4]);
     |                 ^
  11 |   console.log(a, b, c);
  12 | }
  13 | async function rename() {

Without fast-async:

% babel --preset es2015 --plugins syntax-async-functions array_rest.js
async function test() {
  const [a, ...b] = [1, 2, 3];
  console.log(a, b);
}

Array elide gives error:

% babel --preset es2015 --plugins syntax-async-functions,fast-async async.js
TypeError: async.js: Cannot read property 'type' of null
    at getDeclNames (/home/simon/code/xxx/node_modules/nodent/lib/arboriculture.js:2010:19)
    at /home/simon/code/xxx/node_modules/nodent/lib/arboriculture.js:2014:70
    at Array.reduce (native)
    at getDeclNames (/home/simon/code/xxx/node_modules/nodent/lib/arboriculture.js:2014:32)
    at /home/simon/code/xxx/node_modules/nodent/lib/arboriculture.js:2061:29
    at treeWalker (/home/simon/code/xxx/node_modules/nodent/lib/parser.js:156:5)
    at goDown (/home/simon/code/xxx/node_modules/nodent/lib/parser.js:122:9)
    at down (/home/simon/code/xxx/node_modules/nodent/lib/parser.js:146:25)
    at Object.base.Function (/home/simon/code/xxx/node_modules/acorn/dist/walk.js:269:4)
    at down (/home/simon/code/xxx/node_modules/nodent/lib/parser.js:133:60)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions