Visitar URL original
Reduce `regenerator` helper size by liuxingbaoyu · Pull Request #17268 · babel/babel · GitHub
Skip to content

Reduce regenerator helper size - #17268

Merged
liuxingbaoyu merged 32 commits into
babel:regenerator-refactorfrom
liuxingbaoyu:regenerator-ctx
May 3, 2025
Merged

liuxingbaoyu merged 32 commits into
babel:regenerator-refactorfrom
liuxingbaoyu:regenerator-ctx

Conversation

@liuxingbaoyu

@liuxingbaoyu liuxingbaoyu commented Apr 25, 2025 •

Copy link
Copy Markdown
Member
Q                       A
Fixed Issues? Fixes #1, Fixes #2
Patch: Bug Fix?
Major: Breaking Change?
Minor: New Feature?
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes?
License MIT

This looks like it would be hard to review, maybe we can use meta/regenerator#21 to reduce the risk of regression.
Although the existing tests look great.

Unfortunately after this our helpers are still much larger than TS's (2700 bytes vs 1083 bytes), but most of them are prototype definitions, which cannot be optimized away unless we decide to remove them in Babel 8.

Special thanks to TS for this PR, without their implementation I didn't even know the generator helper could be so small.

This PR still has some issues with Babel 7 compatibility, I'll fix them later.

@babel-bot

babel-bot commented Apr 25, 2025 •

Copy link
Copy Markdown
Collaborator

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/59264

@nicolo-ribaudo

Copy link
Copy Markdown
Member

Wow

@nicolo-ribaudo

Copy link
Copy Markdown
Member

maybe we can use meta/regenerator#21 to reduce the risk of regression. Although the existing tests look great.

We run test262 tests, and now SpiderMonkey auto-syncs their tests to test262. We just need to make sure we are running test262's staging folder (maybe we already are).

@nicolo-ribaudo nicolo-ribaudo added the PR: Output optimization 🔬 A type of pull request used for our changelog categories label Apr 25, 2025
keep_fnames: mangleFns
? noMangleFns.length
? new RegExp(noMangleFns.join("|"))
? new RegExp("^" + noMangleFns.join("|") + "$")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be with parentheses, otherwise ^/$ will only apply to the first/last of the options


return function invoke(method: "next" | "throw" | "return", arg: any) {
function invoke(
_methodName: "next" | "throw" | "return",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to keep the _methodName parameter here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not used, but we still keep the first argument as a string name for the sake of the shared defineIteratorMethods .

method = _method;
arg = _arg;

while (!done || (_ = undefined)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does moving _ = undefined after the while make the code bigger? It seems like an optimization Terser should be able to make by itself.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we move it to after the while, it needs to be if (done) _ = undefined because we have a break inside the loop.

https://github.com/babel/babel/pull/17268/files#diff-d95a593570d888bc7ef9ff1ec0157fa86ec3808e4b64dfcd80aa78748e2d8b0bR277-R279

// Call delegate.iterator[context.method](context.arg) and handle the result

if (
(_ = delegateIterator[FunctionNameStrings[method as 0 | 1 | 2]])

@nicolo-ribaudo nicolo-ribaudo Apr 27, 2025 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use _methodName here and in a bunch of other places in this function.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that and it results in an error, I'll look into it again. :)

if (
(_ = delegateIterator[FunctionNameStrings[method as 0 | 1 | 2]])
) {
if ((_ = _.call(delegateIterator, arg))) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that this call re-calls one of the iterator method, causing method to be set to something different, and thus the various method checks further down break?

@nicolo-ribaudo nicolo-ribaudo Apr 27, 2025 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this maybe:

const innerIterable = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      return() {
        it.throw();
        return this.next();
      },
      next() {
        if (i++ < 2) return { done: false, value: "Hi 1" };
        return { done: true, value: "Hi 2" };
      }
    };
  }
};

function* gen() {
  yield* innerIterable;
  yield 1;
  yield 2;
}

var it = gen();
console.log(it.next());
console.log(it.return());

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this will throw Generator is already running. :)

@nicolo-ribaudo nicolo-ribaudo Apr 30, 2025 •

Copy link
Copy Markdown
Member

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, thanks! I haven't tried transformed code before.
This seems to be an existing bug, but I will try to fix it anyway.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh then I would be fine with it happening in a separate PR. This PR looks good otherwise.

@liuxingbaoyu

Copy link
Copy Markdown
Member Author

We haven't run the test262 tests in staging yet (I'll open a PR later), but I looked manually and it seems there are no tests for generators there.
Also strangely, test262 has 13 new passing tests. 🤦‍♂️

I'm not sure if we should include more tests (perhaps v8) to increase confidence in this PR.
Generally passing test262 is enough, but changing this helper may result in a behavior change without exception.😞

TAP version 13
ok 10858 test/language/expressions/yield/star-return-is-null.js default # (success)

ok 10859 test/language/expressions/yield/star-return-is-null.js strict mode # (success)

ok 10870 test/language/expressions/yield/star-rhs-iter-nrml-next-get-err.js default # (success)

ok 10871 test/language/expressions/yield/star-rhs-iter-nrml-next-get-err.js strict mode # (success)

ok 10874 test/language/expressions/yield/star-rhs-iter-nrml-res-done-err.js strict mode # (success)

ok 10875 test/language/expressions/yield/star-rhs-iter-nrml-res-done-err.js default # (success)

ok 10878 test/language/expressions/yield/star-rhs-iter-nrml-res-value-err.js strict mode # (success)

ok 10879 test/language/expressions/yield/star-rhs-iter-nrml-res-value-err.js default # (success)

ok 10884 test/language/expressions/yield/star-rhs-iter-rtrn-res-done-err.js default # (success)

ok 10885 test/language/expressions/yield/star-rhs-iter-rtrn-res-done-err.js strict mode # (success)

ok 10888 test/language/expressions/yield/star-rhs-iter-rtrn-res-value-err.js default # (success)

ok 10889 test/language/expressions/yield/star-rhs-iter-rtrn-res-value-err.js strict mode # (success)

ok 1089[6](https://github.com/babel/babel/actions/runs/14696431194/job/41238860147?pr=17268#step:14:7) test/language/expressions/yield/star-rhs-iter-rtrn-rtrn-get-err.js default # (success)

ok 1089[7](https://github.com/babel/babel/actions/runs/14696431194/job/41238860147?pr=17268#step:14:8) test/language/expressions/yield/star-rhs-iter-rtrn-rtrn-get-err.js strict mode # (success)

ok 10900 test/language/expressions/yield/star-rhs-iter-thrw-res-done-err.js default # (success)

ok 10901 test/language/expressions/yield/star-rhs-iter-thrw-res-done-err.js strict mode # (success)

ok 10904 test/language/expressions/yield/star-rhs-iter-thrw-res-value-err.js default # (success)

ok 10905 test/language/expressions/yield/star-rhs-iter-thrw-res-value-err.js strict mode # (success)

ok 10912 test/language/expressions/yield/star-rhs-iter-thrw-thrw-get-err.js default # (success)

ok 10913 test/language/expressions/yield/star-rhs-iter-thrw-thrw-get-err.js strict mode # (success)

ok 10922 test/language/expressions/yield/star-rhs-iter-thrw-violation-rtrn-get-err.js strict mode # (success)

ok 10923 test/language/expressions/yield/star-rhs-iter-thrw-violation-rtrn-get-err.js default # (success)

ok 10924 test/language/expressions/yield/star-rhs-iter-thrw-violation-rtrn-invoke.js default # (success)

ok 10925 test/language/expressions/yield/star-rhs-iter-thrw-violation-rtrn-invoke.js strict mode # (success)

ok 10930 test/language/expressions/yield/star-throw-is-null.js default # (success)

ok 10931 test/language/expressions/yield/star-throw-is-null.js strict mode # (success)

# 44698 tests, 39915 successes
# 0 new failures, 26 new successes
1..26

@nicolo-ribaudo

Copy link
Copy Markdown
Member

V8 has some in this folder: https://github.com/v8/v8/tree/main/test/mjsunit/harmony (search for "generator")

@nicolo-ribaudo

Copy link
Copy Markdown
Member

@liuxingbaoyu You need to rebase for CI to pass, but it seems like all the new tests are passing :)

@liuxingbaoyu liuxingbaoyu mentioned this pull request May 1, 2025
7 of 8 tasks
@liuxingbaoyu
liuxingbaoyu force-pushed the regenerator-refactor branch from 50fa1b7 to d18276c Compare May 1, 2025 13:18
@nicolo-ribaudo

Copy link
Copy Markdown
Member

One day we should figure out why on old Node.js version whenever there is a test failure then there is also that unrelated false error about ?? in JSDOM.

@liuxingbaoyu

ghost commented May 1, 2025

Copy link
Copy Markdown
Member Author

jestjs/jest#10451
This seems to be a bug in older versions of jest.
Also I realized that regeneratorRuntime does not keep compatibility, I will fix it.

toStringTagSymbol,
"GeneratorFunction",
);
GeneratorFunction.displayName = "GeneratorFunction";

ghost May 1, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add an exec test for the displayName here?

ghost left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome!

@liuxingbaoyu
liuxingbaoyu merged commit e9768d3 into babel:regenerator-refactor May 3, 2025
@github-actions github-actions Bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Sep 17, 2025
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Sep 17, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

outdated A closed issue/PR that is archived due to age. Recommended to make a new issue PR: Output optimization 🔬 A type of pull request used for our changelog categories

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants