Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Babel v7: Env Preset + nodent breaks in Babel generator #56

Closed
swernerx opened this issue Apr 25, 2018 · 13 comments
Closed

Babel v7: Env Preset + nodent breaks in Babel generator #56

swernerx opened this issue Apr 25, 2018 · 13 comments

Comments

@swernerx
Copy link

swernerx commented Apr 25, 2018

I have another exception I got in a pretty trivial case.

Code:

async function executeTasks(tasks) {
  for (const taskName of tasks) {
    try {
      await executeCommands()
    } catch (error) {
      console.error("Error")
    }
  }
}

Babelrc:

{
  "presets": [
    [ "@babel/env", { "exclude": ["transform-regenerator", "transform-async-to-generator"] } ]
  ],

  "plugins": [
    "module:./src/transformAsyncToPromises.js"
  ]
}

Command Line:

node_modules/.bin/babel --config-file ./.babelrc-test --no-babelrc async-for-of.js

Exception:

TypeError: Cannot read property 'length' of undefined
    at Buffer._append (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/buffer.js:115:26)
    at Buffer.append (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/buffer.js:81:10)
    at Generator._append (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/printer.js:202:52)
    at Generator.word (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/printer.js:126:10)
    at Generator.Identifier (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/generators/types.js:43:8)
    at /Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/printer.js:315:23
    at Buffer.withSource (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/buffer.js:178:28)
    at Generator.withSource (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/printer.js:182:15)
    at Generator.print (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/printer.js:314:10)
    at Generator.printJoin (/Users/swerner/Workspace/open-source/babel-preset-edge/node_modules/@babel/generator/lib/printer.js:382:12)

I modified the generator a bit to catch this case:

  _proto._append = function _append(str, line, column, identifierName, filename) {
    if (this._map && str[0] !== "\n") {
      this._map.mark(this._position.line, this._position.column, line, column, identifierName, filename);
    }

    // Hot fix
    if (str == null) {
      str = "[XXXXX]"
    }

   ...

Generated output with fix:

"use strict";

function executeTasks(tasks) {
  return new Promise(function ($return, $error) {
    var $Try_1_Finally = function ($Try_1_Exit) {
      return function ($Try_1_Value) {
        try {
          var $Try_3_Finally = function ($Try_3_Exit) {
            return function ($Try_3_Value) {
              try {
                if (_didIteratorError) {
                  throw _iteratorError;
                }

                return $Try_3_Exit && $Try_3_Exit.call(this, $Try_3_Value);
              } catch ($boundEx) {
                return $error($boundEx);
              }
            }.bind(this);
          }.bind(this);

          var $Try_3_Catch = function ($exception_4) {
            try {
              throw $exception_4;
            } catch ($boundEx) {
              return $Try_3_Finally($error)($boundEx);
            }
          }.bind(this);

          try {
            if (!_iteratorNormalCompletion && _iterator.return != null) {
              _iterator.return();
            }

            return $Try_3_Finally([XXXXX])();
          } catch ($exception_4) {
            $Try_3_Catch($exception_4)
          }

          return $Try_1_Exit && $Try_1_Exit.call(this, $Try_1_Value);
        } catch ($boundEx) {
          return $error($boundEx);
        }
      }.bind(this);
    }.bind(this);

    var _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, taskName;

    _iteratorNormalCompletion = true;
    _didIteratorError = false;
    _iteratorError = undefined;

    var $Try_1_Post = function () {
      try {
        return $return();
      } catch ($boundEx) {
        return $error($boundEx);
      }
    };

    var $Try_1_Catch = function (err) {
      try {
        _didIteratorError = true;
        _iteratorError = err;
        return $Try_1_Finally($Try_1_Post)();
      } catch ($boundEx) {
        return $Try_1_Finally($error)($boundEx);
      }
    };

    try {
      _iterator = tasks[Symbol.iterator]();
      var $Loop_5_trampoline;

      function $Loop_5_step() {
        _iteratorNormalCompletion = true;
        return $Loop_5;
      }

      function $Loop_5() {
        if (!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) {
          taskName = _step.value;

          var $Try_2_Post = function () {
            try {
              return $Loop_5_step;
            } catch ($boundEx) {
              return $Try_1_Catch($boundEx);
            }
          };

          var $Try_2_Catch = function (error) {
            try {
              console.error("Error");
              return $Try_2_Post();
            } catch ($boundEx) {
              return $Try_1_Catch($boundEx);
            }
          };

          try {
            return Promise.resolve(executeCommands()).then(function ($await_7) {
              try {
                return $Try_2_Post();
              } catch ($boundEx) {
                return $Try_2_Catch($boundEx);
              }
            }, $Try_2_Catch);
          } catch (error) {
            $Try_2_Catch(error)
          }
        } else return [1];
      }

      return ($Loop_5_trampoline = function (q) {
        while (q) {
          if (q.then) return void q.then($Loop_5_trampoline, $Try_1_Catch);

          try {
            if (q.pop) {
              if (q.length) return q.pop() ? $Loop_5_exit.call(this) : q;else q = $Loop_5_step;
            } else q = q.call(this);
          } catch (_exception) {
            return $Try_1_Catch(_exception);
          }
        }
      }.bind(this))($Loop_5);

      function $Loop_5_exit() {
        return $Try_1_Finally($Try_1_Post)();
      }
    } catch (err) {
      $Try_1_Catch(err)
    }
  });
}

The problematic code is at line 35:

          try {
            if (!_iteratorNormalCompletion && _iterator.return != null) {
              _iterator.return();
            }

            return $Try_3_Finally([XXXXX])();
          } catch ($exception_4) {
            $Try_3_Catch($exception_4)
          }

Do you have any idea what's wrong?

@MatAtBread
Copy link
Owner

Can you try with fast-async in your plugins first and last, and let me know if it's different?

@swernerx
Copy link
Author

There is just one plugin. The one used is from the PR of you to Babel. Should be pretty much identical to fast-async. Plus I use the latest nodent transform.

@swernerx
Copy link
Author

Hi Mat!

I have uploaded a small extracted demo case to a gist together with some results being produced:
https://gist.github.com/swernerx/7ca748fcddab4c672193b614e62cc73e

Please have a look. Should be pretty much reproducible for you as well. Hopefully this helps to track the issue down.

@matAtWork
Copy link
Collaborator

I was having a few issues pushing changes to my Babel fork, which I eventually resolved by pulling the latest Babel source and re-merging before make clean and retest. My changes to transform-async-to-promises now pass all the tests (babel/babel#7076 is green again).

Can I suggest you pull the changes again and retry? Mixing Babel beta-versions (in this case .44 and .46) is clearly not a good base from which to test.

@matAtWork
Copy link
Collaborator

@swernerx - thanks for the gist. It would be really good if you could just generate a repo I could clone and npm test. There are so many versions of Babel, etc., trying to reproduce an exact error is proving difficult.

@swernerx
Copy link
Author

@swernerx
Copy link
Author

Unfortunately it seems that the env-preset is always executed before fast-async.

See also: babel/babel#7815

@matAtWork
Copy link
Collaborator

Thanks for the repo :) The issue seems to be the Babel implementation of for await (....), which is probably fighting with fast-async. The exception occurs while Babel tries to create a template internally:

  async function wrapper() {
    var ITERATOR_COMPLETION = true;
    var ITERATOR_HAD_ERROR_KEY = false;
    var ITERATOR_ERROR_KEY;
    try {
      for (
        var ITERATOR_KEY = GET_ITERATOR(OBJECT), STEP_KEY, STEP_VALUE;
        (
          STEP_KEY = await ITERATOR_KEY.next(),
          ITERATOR_COMPLETION = STEP_KEY.done,
          STEP_VALUE = await STEP_KEY.value,
          !ITERATOR_COMPLETION
        );
        ITERATOR_COMPLETION = true) {
      }
    } catch (err) {
      ITERATOR_HAD_ERROR_KEY = true;
      ITERATOR_ERROR_KEY = err;
    } finally {
      try {
        if (!ITERATOR_COMPLETION && ITERATOR_KEY.return != null) {
          await ITERATOR_KEY.return();
        }
      } finally {
        if (ITERATOR_HAD_ERROR_KEY) {
          throw ITERATOR_ERROR_KEY;
        }
      }
    }
  }

I'll see if I can work out why. If you're not using the for await construct, it might be easier to simply disable it for now.

@swernerx
Copy link
Author

What do you mean by disabling?

I am updating a generally used preset. I don't think I can communicate to all why for-of loops with await shouldn't be used. So it's not only about me.

@matAtWork
Copy link
Collaborator

I meant if you want to continue your development while I look into it!

@matAtWork
Copy link
Collaborator

Confirmed: this is a bug in nodent-transform - it produces an illegal Identifier transpiling the output of babel's for-of transform. I hope to be able to find/fix it soon. Thanks for the help in identifying and reproducing it!

swernerx added a commit to sebastian-software/babel-preset-edge that referenced this issue Apr 26, 2018
matAtWork added a commit to MatAtBread/nodent-compiler that referenced this issue Apr 30, 2018
matAtWork added a commit to MatAtBread/nodent-transform that referenced this issue Apr 30, 2018
MatAtBread added a commit to MatAtBread/nodent that referenced this issue Apr 30, 2018
- Fix issue where `catch` body was incompletely transformed (see
#109)

- Fix issue where unreachable continuation generated an illegal
Identifer after try-catch (see
MatAtBread/fast-async#56)
swernerx added a commit to sebastian-software/babel-preset-edge that referenced this issue Apr 30, 2018
… transforms. Removed the hack which worked-around the previous bug. See also: MatAtBread/fast-async#56
@MatAtBread
Copy link
Owner

@swernerx - please let me know if this fixes the issue as expected

@swernerx
Copy link
Author

swernerx commented May 2, 2018

Looks good for me! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants