Skip to content

test(transformer/async-to-generator): failing test for async arrow function in class static block#8387

Merged
graphite-app[bot] merged 1 commit intomainfrom
01-09-test_transformer_async-to-generator_failing_test_for_async_arrow_function_in_class_static_block
Jan 14, 2025
Merged

test(transformer/async-to-generator): failing test for async arrow function in class static block#8387
graphite-app[bot] merged 1 commit intomainfrom
01-09-test_transformer_async-to-generator_failing_test_for_async_arrow_function_in_class_static_block

Conversation

@overlookmotel
Copy link
Member

@overlookmotel overlookmotel commented Jan 9, 2025

It's arguable whether we should support this, because async functions is ES2018 and class static blocks is ES2022. So there should not be any browser which needs async-to-generator transform, and not the static block transform too. And when class-static-block transform is enabled, this works fine.

But personally, I think we should support it, because:

  1. We allow user to enable/disable arbitrary transforms via TransformOptions.
  2. We support this in async arrow functions in class static blocks, so it makes sense to complete that support.
  3. I don't think it should be too hard, as the framework for it is already there in the async-to-generator transform.

Babel REPL

@github-actions github-actions bot added A-transformer Area - Transformer / Transpiler C-test Category - Testing. Code is missing test cases, or a PR is adding them labels Jan 9, 2025
Copy link
Member Author

overlookmotel commented Jan 9, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@overlookmotel overlookmotel marked this pull request as ready for review January 9, 2025 12:41
@Dunqing
Copy link
Member

Dunqing commented Jan 9, 2025

Just found that Babel's output is incorrect, super can't be outside a class. It wasted a few hours for me.

@Dunqing
Copy link
Member

Dunqing commented Jan 9, 2025

Even if the async arrow function is not in the class static block, it also needs to transform super, because async-to-generator will transform the async arrow function to a function generator, thus super will be in the wrong place.

But there is a troublesome example

Input:

class C { static f = async () => super.prop;  }

Babel's Output:

var _superprop_getProp = () => super.prop;
class C {
  static f = babelHelpers.asyncToGenerator(function* () {
    return _superprop_getProp();
  });
}

Babel's implementation is wrong because it inserts _superprop_getProp outside the class. But how to fix it? Where is the correct place to insert _superprop_getProp?

@overlookmotel overlookmotel force-pushed the 01-09-test_transformer_async-to-generator_failing_test_for_async_arrow_function_in_class_static_block branch from 46d2acc to 5a81005 Compare January 9, 2025 18:30
@overlookmotel
Copy link
Member Author

Just found that Babel's output is incorrect, super can't be outside a class. It wasted a few hours for me.

Oh bollocks sorry my fault. I posted the Babel REPL link meaning to say "look Babel is wrong again!" but I forgot to actually write that. The test in this PR shows what I think the correct output is for static blocks.

For static blocks, I don't expect it's too hard because there's already a block for the var statement to go in, and the transform already correctly handles this correctly in static blocks: class C { static { async () => this; } }

@overlookmotel
Copy link
Member Author

For properties (instance prop or static prop), it's much more annoying. Babel's solution for instance props looks correct, but for some reason it gets it wrong for static props: Babel REPL

But I'm not sure we should attempt to cover async arrow functions in class properties. It's maybe not worth the work. I only suggested that we might want to deal with static blocks because I thought it'd be quite easy.

We also have this problem with other transforms. e.g. class C { prop = x ?? y; } should probably create the temp var inside the property initializer, not outside the class. Or maybe in this cases it's fine (Babel thinks so), but in other transforms it's probably not fine.

We have other weird cases where we need to create temp vars as part of a transform inside:

  • function f(x = somethingThatNeedsALocalTempVarWhenTransformed) {}
  • for (let [x = somethingThatNeedsALocalTempVarWhenTransformed] of arr) {}
  • Probably more

I put class property initializers in the same category.

Rather than tackling this in individual transforms, I think we should probably try to figure out a way to handle these problems universally with helpers that create temp vars, and handle these edge cases for us.

@overlookmotel
Copy link
Member Author

overlookmotel commented Jan 9, 2025

It may also be easier to deal with when we have our own helper library. e.g. something like:

class C {
  static f = async (x, y) => super.prop;
}

->

class C {
  static f = oxcHelpers.asyncToGenerator(
    function*(x, y, _superprop_getProp) {
      return _superprop_getProp();
    },
    () => super.prop
  );
}

@Dunqing
Copy link
Member

Dunqing commented Jan 10, 2025

It may also be easier to deal with when we have our own helper library. e.g. something like:

class C {
  static f = async (x, y) => super.prop;
}

->

class C {
  static f = oxcHelpers.asyncToGenerator(
    function*(x, y, _superprop_getProp) {
      return _superprop_getProp();
    },
    () => super.prop
  );
}

I just came up with a way by SequenceExpression

var _superprop_getProp;
class C {
  static f =
    ((_superprop_getProp = () => super.prop),
    /*#__PURE__*/ (function () {
      var _ref = babelHelpers.asyncToGenerator(function* (x, y) {
        return _superprop_getProp();
      });
      return function (_x, _x2) {
        return _ref.apply(this, arguments);
      };
    })());
}

It doesn't rely on our own helper, so we can support it soon.

@overlookmotel
Copy link
Member Author

Nice! That does work for static props, but it won't work for instance props because you need a different _superprop_getProp for each class instance.

Developing your idea further, I think this works for both static and instance:

Input:

class C {
  f = async (x, y) => [this, super.prop];
  static f = async (x, y) => [this, super.prop];
}

Output:

class C {
  f = (function(_this, _superprop_getProp) {
    var _ref = babelHelpers.asyncToGenerator(function*(x, y) {
      return [_this, _superprop_getProp()];
    });
    return function (_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })(this, () => super.prop);

  static f = (function(_this2, _superprop_getProp2) {
    var _ref = babelHelpers.asyncToGenerator(function*(x, y) {
      return [_this2, _superprop_getProp2()];
    });
    return function (_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })(this, () => super.prop);
}

BUT... Do we want to do this now? I think in the end we can do it better once we have our own helpers lib, so we will end up rewriting it anyway.

And supporting async-to-generator transform without class properties transform is not required for our core purpose which is to support target: 'es2015' or target: 'es2020'.

@Dunqing Dunqing force-pushed the 01-09-test_transformer_async-to-generator_failing_test_for_async_arrow_function_in_class_static_block branch from 5a81005 to 953d1b5 Compare January 11, 2025 14:31
@Dunqing
Copy link
Member

Dunqing commented Jan 11, 2025

I have tried to solve this problem at #8435, luckily it didn't take me a long time.

@graphite-app graphite-app bot added the 0-merge Merge with Graphite Merge Queue label Jan 13, 2025
@graphite-app
Copy link
Contributor

graphite-app bot commented Jan 13, 2025

Merge activity

  • Jan 13, 6:30 PM EST: Graphite disabled "merge when ready" on this PR due to: a merge conflict with the target branch; resolve the conflict and try again..
  • Jan 13, 11:30 PM UTC: The merge label '0-merge' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • Jan 14, 3:34 AM EST: The merge label '0-merge' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • Jan 14, 3:39 AM EST: A user added this pull request to the Graphite merge queue.
  • Jan 14, 3:43 AM EST: A user merged this pull request with the Graphite merge queue.

@overlookmotel overlookmotel removed the 0-merge Merge with Graphite Merge Queue label Jan 13, 2025
@overlookmotel overlookmotel force-pushed the 01-09-test_transformer_async-to-generator_failing_test_for_async_arrow_function_in_class_static_block branch from 953d1b5 to 9b60f5f Compare January 14, 2025 08:34
@overlookmotel overlookmotel added the 0-merge Merge with Graphite Merge Queue label Jan 14, 2025
…nction in class static block (#8387)

It's arguable whether we should support this, because async functions is ES2018 and class static blocks is ES2022. So there should not be any browser which needs async-to-generator transform, and not the static block transform too. And when class-static-block transform is enabled, this works fine.

But personally, I think we should support it, because:

1. We allow user to enable/disable arbitrary transforms via `TransformOptions`.
2. We support `this` in async arrow functions in class static blocks, so it makes sense to complete that support.
3. I don't think it should be too hard, as the framework for it is already there in the async-to-generator transform.

[Babel REPL](https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=MYGwhgzhAEDC0FMAeAXBA7AJjAytA3gFDTQQpgoCWwBxJ0KAFpRAHQBm60AvNJAJ7oaACgCUPAHykArgAcEAJw4B7ZQG46AX0LagA&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=&prettier=true&targets=&version=7.26.4&externalPlugins=%40babel%2Fplugin-transform-async-to-generator%407.25.9%2C%40babel%2Fplugin-external-helpers%407.25.9&assumptions=%7B%7D)
@overlookmotel overlookmotel force-pushed the 01-09-test_transformer_async-to-generator_failing_test_for_async_arrow_function_in_class_static_block branch from 9b60f5f to 0358c6f Compare January 14, 2025 08:38
overlookmotel pushed a commit that referenced this pull request Jan 14, 2025
…ectly in async arrow function (#8435)

close: #8385

This PR is to solve the missing `super` transform in the async arrow function, and `_this = this` inserts to an incorrect place. These problems are all about the async arrow function, which is a part of the init of class property. Learn more at #8387.

The output matches Babel's output except for static prop as Babel transforms incorrectly.
@graphite-app graphite-app bot merged commit 0358c6f into main Jan 14, 2025
16 checks passed
@graphite-app graphite-app bot deleted the 01-09-test_transformer_async-to-generator_failing_test_for_async_arrow_function_in_class_static_block branch January 14, 2025 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

0-merge Merge with Graphite Merge Queue A-transformer Area - Transformer / Transpiler C-test Category - Testing. Code is missing test cases, or a PR is adding them

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants