Skip to content

fix(transformer/async-to-generator): move parameters to the inner generator function when they could throw errors#8500

Merged
graphite-app[bot] merged 1 commit intomainfrom
01-15-fix_transformer_async-to-generator_move_parameters_to_the_inner_generator_function_when_they_could_throw_errors
Jan 16, 2025
Merged

fix(transformer/async-to-generator): move parameters to the inner generator function when they could throw errors#8500
graphite-app[bot] merged 1 commit intomainfrom
01-15-fix_transformer_async-to-generator_move_parameters_to_the_inner_generator_function_when_they_could_throw_errors

Conversation

@Dunqing
Copy link
Copy Markdown
Member

@Dunqing Dunqing commented Jan 15, 2025

The new implementation port from esbuild, before from Babel.

Babel's transform implementation for the async method is incorrect because the async method should return a rejecting promise when it throws an error. Everything is good if the errors are thrown in the async method body, but the following case will throw an error in the parameters which causes the whole program crushed not a rejecting promise. So we should move the parameters to the inner generator function when the parameters could throw an error.

Input:

class Cls {
  // ReferenceError: Cannot access 'b' before initialization
  async method(a = b, b = 0) {}
}

Before output

class Cls {
  method(a = b, b = 0) {
    return babelHelpers.asyncToGenerator(function* () {})();
  }
}

After output:

class Cls {
  method() {
     // ReferenceError: Cannot access 'b' before initialization
    return babelHelpers.asyncToGenerator(function* (a = b, b = 0) {}).apply(this, arguments);
  }
}

No override tests because Babel doesn't cover this case.

@github-actions github-actions Bot added the A-transformer Area - Transformer / Transpiler label Jan 15, 2025
Copy link
Copy Markdown
Member Author

Dunqing commented Jan 15, 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.

@github-actions github-actions Bot added the C-bug Category - Bug label Jan 15, 2025
@Dunqing Dunqing force-pushed the 01-15-fix_transformer_async-to-generator_move_parameters_to_the_inner_generator_function_when_they_could_throw_errors branch from 3e29784 to d480244 Compare January 15, 2025 07:24
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jan 15, 2025

CodSpeed Performance Report

Merging #8500 will not alter performance

Comparing 01-15-fix_transformer_async-to-generator_move_parameters_to_the_inner_generator_function_when_they_could_throw_errors (06ccb51) with main (3789d2f)

Summary

✅ 32 untouched benchmarks

@Dunqing
Copy link
Copy Markdown
Member Author

Dunqing commented Jan 15, 2025

I noticed a potential improvement, we don't need to change this to _this in the async method and async arrow function because both asyncToGenerator and wrapAsyncGenerator are binding the this for the inner generator function. However this will cause a lot of tests to fail, thus we need to override them, this is not a good moment to change this.

@Dunqing Dunqing force-pushed the 01-15-fix_transformer_async-to-generator_move_parameters_to_the_inner_generator_function_when_they_could_throw_errors branch from d480244 to 4fdccfd Compare January 15, 2025 08:00
Copy link
Copy Markdown
Member

@overlookmotel overlookmotel left a comment

Choose a reason for hiding this comment

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

Nice!

@overlookmotel overlookmotel added the 0-merge Merge with Graphite Merge Queue label Jan 16, 2025
Copy link
Copy Markdown
Member

overlookmotel commented Jan 16, 2025

Merge activity

  • Jan 15, 7:01 PM EST: The merge label '0-merge' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • Jan 15, 7:01 PM EST: A user added this pull request to the Graphite merge queue.
  • Jan 15, 7:05 PM EST: The Graphite merge queue couldn't merge this PR because it was not satisfying all requirements (Failed CI: 'Conformance').
  • Jan 15, 7:19 PM EST: The merge label '0-merge' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • Jan 15, 7:22 PM EST: A user added this pull request to the Graphite merge queue.
  • Jan 15, 7:22 PM EST: A user merged this pull request with the Graphite merge queue.

overlookmotel pushed a commit that referenced this pull request Jan 16, 2025
…erator function when they could throw errors (#8500)

The new implementation port from [esbuild](https://github.com/evanw/esbuild/blob/df815ac27b84f8b34374c9182a93c94718f8a630/internal/js_parser/js_parser_lower.go#L355-L467), before from `Babel`.

Babel's transform implementation for the async method is incorrect because the async method should return a rejecting promise when it throws an error. Everything is good if the errors are thrown in the async method body, but the following case will throw an error in the parameters which causes the whole program crushed not a rejecting promise. So we should move the parameters to the inner generator function when the parameters could throw an error.

Input:
```js
class Cls {
  // ReferenceError: Cannot access 'b' before initialization
  async method(a = b, b = 0) {}
}
```

Before output
```js
class Cls {
  method(a = b, b = 0) {
    return babelHelpers.asyncToGenerator(function* () {})();
  }
}
```

After output:
```js
class Cls {
  method() {
     // ReferenceError: Cannot access 'b' before initialization
    return babelHelpers.asyncToGenerator(function* (a = b, b = 0) {}).apply(this, arguments);
  }
}
```

No override tests because Babel doesn't cover this case.
@overlookmotel overlookmotel force-pushed the 01-15-fix_transformer_async-to-generator_move_parameters_to_the_inner_generator_function_when_they_could_throw_errors branch from 4fdccfd to 2ae322b Compare January 16, 2025 00:01
@graphite-app graphite-app Bot removed the 0-merge Merge with Graphite Merge Queue label Jan 16, 2025
@overlookmotel
Copy link
Copy Markdown
Member

overlookmotel commented Jan 16, 2025

@Dunqing One thing for follow-up: There are more things beyond AssignmentPattern which can throw. e.g.:

const constant = 1;

Object.defineProperty(global, 'foo', { get() { throw new Error('mwuhahaha!'); } });

class C {
    // Destructuring throws error if method called with no arguments
    async method1( [a] ) {}
    async method2( {a} ) {}
    async method3( ...[{a}] ) {}

    // Maybe you handle these already
    async method4( a = foo ) {} // Just trying to read `global.foo` throws an error!
    async method5( a = constant = 2 ) {}
    async method6( a = C = 2 ) {} // Assigning to class name within class is an error
}

Any accesses of unbound variables (e.g. foo above) can throw, and no way to know statically if they will or not.

…erator function when they could throw errors (#8500)

The new implementation port from [esbuild](https://github.com/evanw/esbuild/blob/df815ac27b84f8b34374c9182a93c94718f8a630/internal/js_parser/js_parser_lower.go#L355-L467), before from `Babel`.

Babel's transform implementation for the async method is incorrect because the async method should return a rejecting promise when it throws an error. Everything is good if the errors are thrown in the async method body, but the following case will throw an error in the parameters which causes the whole program crushed not a rejecting promise. So we should move the parameters to the inner generator function when the parameters could throw an error.

Input:
```js
class Cls {
  // ReferenceError: Cannot access 'b' before initialization
  async method(a = b, b = 0) {}
}
```

Before output
```js
class Cls {
  method(a = b, b = 0) {
    return babelHelpers.asyncToGenerator(function* () {})();
  }
}
```

After output:
```js
class Cls {
  method() {
     // ReferenceError: Cannot access 'b' before initialization
    return babelHelpers.asyncToGenerator(function* (a = b, b = 0) {}).apply(this, arguments);
  }
}
```

No override tests because Babel doesn't cover this case.
@overlookmotel overlookmotel force-pushed the 01-15-fix_transformer_async-to-generator_move_parameters_to_the_inner_generator_function_when_they_could_throw_errors branch from 2ae322b to 06ccb51 Compare January 16, 2025 00:18
@overlookmotel overlookmotel added the 0-merge Merge with Graphite Merge Queue label Jan 16, 2025
@overlookmotel
Copy link
Copy Markdown
Member

Force pushed to resolve merge conflicts in snapshots.

@graphite-app graphite-app Bot merged commit 06ccb51 into main Jan 16, 2025
@graphite-app graphite-app Bot deleted the 01-15-fix_transformer_async-to-generator_move_parameters_to_the_inner_generator_function_when_they_could_throw_errors branch January 16, 2025 00:22
Boshen added a commit that referenced this pull request Jan 18, 2025
## [0.47.0] - 2025-01-18

- fae4cd2 allocator: [**BREAKING**] Remove `Vec::into_string` (#8571)
(overlookmotel)

- 95bc0d7 allocator: [**BREAKING**] `Allocator` do not deref to
`bumpalo::Bump` (#8569) (overlookmotel)

- 19d3677 ast: [**BREAKING**] Always return
`Array<ImportDeclarationSpecifier>` for `ImportDeclaration.specifiers`
(#8560) (sapphi-red)

- 4ce6329 semantic: [**BREAKING**] Ensure program outlives semantic
(#8455) (Valentinas Janeiko)

- 7066d1c ast, span, syntax, regular_expression: [**BREAKING**] Remove
`ContentHash` (#8512) (overlookmotel)

### Features

- bf4e5e1 allocator: Add `HashMap` (#8553) (overlookmotel)
- a6d71f8 ast: Add `AstKind::ty` method (#8521) (overlookmotel)
- 4d4e805 minifier: Collapse if stmt with empty consequent (#8577)
(camc314)
- 991a22f minifier: Fold `Array::concat` into literal (#8442)
(sapphi-red)
- 3dc2d8b minifier: Fold string concat chaining (#8441) (sapphi-red)
- a4ae450 minifier: Fold array concat chaining (#8440) (sapphi-red)
- 7cc81ef minifier: Fold invalid typeof comparisons (#8550) (camc314)
- 927f43f minifier: Improve `.charCodeAt(arg)` when arg is valid (#8534)
(Boshen)
- 06f14d5 minifier: Remove empty class static block `class Foo { static
{} }` (#8525) (Boshen)
- 1860411 minifier: Remove last redundant return statement (#8523)
(Boshen)
- c479a58 napi/parser: Expose dynamic import expressions (#8540)
(Boshen)
- 2f0314e npm/oxc-minify: Npm package and publish script (#8579)
(Boshen)
- f413bb5 transformer/optional-chaining: Change parent scope for
expression when it wrapped with an arrow function (#8511) (Dunqing)

### Bug Fixes

- e87c001 allocator: Statically prevent memory leaks in allocator
(#8570) (overlookmotel)
- 855c839 codegen: Shorthand assignment target identifier consider
mangled names (#8536) (Boshen)
- 65c596d minifer: Keep idents if not in scope when minimizing array
exprs (#8551) (camc314)
- f57aac2 minifier: Incorrect folding of expr in bool ctx (#8542)
(camc314)
- 946ad76 minifier: `(-Infinity).toString()` -> `'-Infinity'` (#8535)
(Boshen)
- b1d0186 minifier: Do not fold `!!void b` (#8533) (Boshen)
- 53adde5 minifier: `x['-2147483648']` -> `x[-2147483648]` (#8528)
(Boshen)
- 405b73d minifier: Do not change `delete undefined` to `delete void 0`
(#8527) (Boshen)
- 92e44cb minifier: Do not remove `undefined` in `var x = undefined`
(#8526) (Boshen)
- 209e313 minifier: `class C { ['-1']() {} }` cannot be minifized
(#8516) (Boshen)
- 6585463 minifier: Always keep the last value of sequence expression
(#8490) (Boshen)
- b552f5c transformer: `wrap_in_arrow_function_iife` take span of input
`Expression` (#8547) (overlookmotel)
- 9963533 transformer/arrow-functions: Visit arguments to `super()` call
(#8494) (overlookmotel)
- 06ccb51 transformer/async-to-generator: Move parameters to the inner
generator function when they could throw errors (#8500) (Dunqing)
- 356f0c1 transformer/class-properties: Handle nested `super()` calls
(#8506) (overlookmotel)
- a048337 transformer/class-static-blocks: Static block converted to
IIFE use span of original block (#8549) (overlookmotel)

### Performance

- 76ea52b allocator: Inline `Box` methods (#8572) (overlookmotel)
- 93df57f allocator: `#[inline(always)]` methods of `Vec` which just
delegate to `allocator_api2` (#8567) (overlookmotel)
- 5a28d68 allocator: `#[inline(always)]` methods of `HashMap` which just
delegate to `hashbrown` (#8565) (overlookmotel)
- d17021c mangler: Optimize `base54` function (#8557) (overlookmotel)
- 6b52d7a mangler: Use a single allocation space for temporary vecs
(#8495) (Boshen)
- 30a869e semantic: Use `oxc_allocator::HashMap` in `ScopeTree` (#8554)
(overlookmotel)
- 63eb298 span: Compare `Span`s as single `u64`s (#8300) (overlookmotel)
- a43560c span: Hash `Span` as a single `u64` (#8299) (overlookmotel)
- 3fff7d2 span: Align `Span` same as `usize` (#8298) (overlookmotel)
- 53ef263 transformer/arrow-functions: Bail out of visiting early when
inserting `_this = this` after `super()` (#8482) (overlookmotel)

### Documentation

- fa1a6d5 allocator: Update docs for `Vec` (#8555) (overlookmotel)

### Refactor

- ac05134 allocator: `String` type (#8568) (overlookmotel)
- 68fab81 allocator: Rename inner `Vec` type (#8566) (overlookmotel)
- fcbca32 ast: Rename `#[estree(with)]` to `#[estree(via)]` (#8564)
(overlookmotel)
- 007e8c0 ast, regular_expression: Shorten `ContentEq` implementations
(#8519) (overlookmotel)
- b4c87e2 linter: Move DiagnosticsReporters to oxlint (#8454) (Alexander
S.)
- 8f57929 minifier: Merge `try_compress_type_of_equal_string` into
`try_minimize_binary` (#8561) (sapphi-red)
- 2857ae1 parser: Refactor visitor in regexp example (#8524)
(overlookmotel)
- b5ed58e span: All methods take owned `Span` (#8297) (overlookmotel)
- 712633f transformer: `wrap_statements_in_arrow_function_iife` utility
function (#8548) (overlookmotel)
- 5206c6a transformer: Rename `wrap_in_arrow_function_iife` (#8546)
(overlookmotel)
- 61077ca transformer: `wrap_arrow_function_iife` receive an owned
`Expression` (#8545) (overlookmotel)
- 6820d24 transformer: Move `wrap_arrow_function_iife` to root utils
module (#8529) (Dunqing)
- 52bd0b1 transformer: Move common utils functions to the root module
(#8513) (Dunqing)
- c30654a transformer/arrow-function: Wrapping arrow function iife by
using `wrap_arrow_function_iife` (#8530) (Dunqing)
- 2bc5175 transformer/arrow-functions: Rename method (#8481)
(overlookmotel)
- 72f425f transformer/class-properties: Fix lint warning in release mode
(#8539) (overlookmotel)
- 7e61b23 transformer/typescript: Shorten code (#8504) (overlookmotel)
- 04bc259 traverse: Remove unnecessary `#[allow]` (#8518)
(overlookmotel)
- a368726 traverse: Harden soundness of `Traverse` and document safety
invariants better (#8507) (overlookmotel)

### Testing

- e0f5d6c minifier: Update esbuild test (Boshen)
- 629c417 minifier: Port esbuild minification tests (#8497) (Boshen)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
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-bug Category - Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants