The new implementation port from [esbuild](df815ac27b/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.
Follow-on after #8482.
Fix an edge case in arrow functions transform when inserting `_this = this` after `super()`. It is possible (though bizarre) for `super()` to contain another `super()` call. This will throw an error when evaluating the outer `super()`, but it can still be observable in some cases.
```js
let f;
class S {}
class C extends S {
constructor(x) {
super(super(), this.x = x, f = async () => this);
}
}
try { new C(123) } catch {}
const c = await f();
assert(c.x === 123);
```
So, before bailing out from searching for more `super()`s in class constructor, visit the `super()` call's arguments.
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.
This `super.value` belongs to the nested class, we shouldn't transform it.
```js
class Outer {
async method() {
class Inner extends Outer {
normal() {
// `super.value` should not be transformed, because it is not in an async method
super.value
}
}
}
}
```
No temp var is required for class if it contains static private method which is not referenced within class. e.g.:
```js
let C = class {
static #method() {}
};
```
->
```js
let C = class {};
function _method() {}
```
We don't transform private methods yet, but in class properties transform, still need to record private methods that classes have, so can correctly resolve private fields.
```js
class Outer {
#foo = 123;
method() {
class Inner {
#foo() {}
// Refers to `Inner`'s `#foo` method, not `Outer`'s `#foo` property
prop = this.#foo;
}
}
}
```
Add failing test for nullish coalescing operator. The output is correct, but temp var is created in wrong scope.
My guess is that it needs to use `current_hoist_scope_id`, not `current_scope_id`.
This test from class properties also shows the same problem: ac097e9160/tasks/transform_conformance/snapshots/oxc.snap.md (L20-L35)
Large re-architecting of class properties transform. Split transform into 3 phases:
1. Transform instance properties when entering class.
2. Transform private fields during traversal of class body.
3. Transform static properties and static blocks when exiting class.
This ensures that code which has to be moved outside of the class (static property initializers, static blocks, computed keys) can get transformed by other transforms before they're moved out.
Also fixes a problem where we previously registered private properties too early - on entering the class, rather than the class *body*, so private fields in `extends` clause of a nested class were misinterpretted.
When inserting instance property initializers into class constructor, need to search for and transform `super()`. Exit that visit earlier in some cases, for better performance and smaller output.
Follow-on after #7997.
Generate "base name" for temp var using `temp_var_name_base` and then create the 2 temp bindings from it.
This is a bit more efficient than creating 2nd temp binding from name of the first temp binding, because the first binding's name has `_` added to start, and may have digits on the end, which have to be trimmed off again. Whereas the "base name" is ready to go.
Incidentally, changing the timing of when temp bindings are created also aligns output with Babel.
Amend test added in #7991 to test transform of `super[prop] = value` where `prop` is not bound.
We should ideally have the `_unbound` temp vars *inside* the arrow function rather than outside, as Babel does, but that's not possible with our double-visitor scheme at present, and I think current output will operate correctly anyway.
Probably these temp vars could be hoisted even higher up - to very top level of the file, even if the class and `super[prop]` were deeply nested in many functions - and it'd still be correct. That'd be good for transformer performance as less `var` statements to insert, and also slightly smaller output size - less `var`s in code. But I don't know if that would be worse for runtime performance, as it makes the arrow function more impure. 🤔
Alternative of #7956 and #7959. Unlike the previous method, adapting duplicating the same logic rather than making the same logic transform function to be generic
I just found that we don't need to transform `TaggedTemplateExpression` because its `tag` can be transformed by `transform_static_member_expression` and `transform_computed_member_expression`
Don't transform `super` in static property initializers if it's nested in another method, so is a *different* `super`.
```js
class C {
static prop = () => {
const object = {
method() {
// `super` here refers to prototype of `object`, not class `C`
return super.foo;
}
};
};
}
```
Add a fast path for inserting instance property initializers into constructor, when no existing constructor or constructor has no bindings. This should be reasonably common.
The `Scope flags mismatch` errors are due to #7900.
#7872 implements renaming symbols in constructor which shadow references in instance property initializers. But we don't need to rename where the reference in initializer references a symbol which is bound within the initializer itself.
Input:
```js
class C {
double = n => n * 2;
constructor(n) {
console.log(n);
}
}
```
Output:
```js
class C {
constructor(n) { // <-- not renamed
this.double = n => n * 2; // <-- moved into constructor
console.log(n); // <-- not renamed
}
}
```
This produces better output, and avoids a traversal of constructor's AST renaming symbols.
The root cause is due to transform wrongly a PrivateFieldExpression that doesn't contain any optional expression, so call `to_member_expression_mut` causes unwrap to fail. I have fixed the incorrect transform and changed `to_member_expression_mut` to `as_member_expression_mut`.
Code in static property initializers moves from inside the class to outside. If environment outside the class is not strict mode, then scopes within the initializer become sloppy mode. Update `ScopeFlags` for scopes in static prop initializers accordingly.
We're following Babel for now, but this isn't actually correct. The initializers should be wrapped in a strict mode IIFE to maintain their strict mode behavior. But at least semantic data is now correct for the output.
Support `private_fields_as_properties` assumption in class properties transform. This assumption is also enabled by the transform's `loose` option.
Optional chain (e.g. `this?.#prop`) is not yet implemented, but all other usages of private fields are supported. We'll handle optional chain in a follow-on PR.
`TransformCtx::duplicate_expression` (introduced in #7754) don't create temp vars for literals. This produces more compact output for the logical assignment operators transform.
This diverges from Babel (it's better!) so add an override for one of Babel's fixtures. Also add further tests for all literal types.
Use `TransformCtx::duplicate_expression` (introduced in #7754) to decide when to create temp vars for member expression object and computed property.
This fixes a bug where `IdentifierReference`s created when transforming `key` in `object[key] &&= value` were created without a `ReferenceId` (due to `clone_in`).
We didn't catch this before because Babel's test fixtures only cover `object[key++] &&= value` not the simpler `object[key] &&= value`. Add tests for this.
Babel's tests only cover transforming `this.#prop &&= value` with logical assignment operators transform also enabled. Add tests for just class properties transform alone.
When create a `_super` function outside class, ensure it's always strict mode. The code it contains was previously inside the class, so was strict mode.
Fix due to this plugin transforming the async method and async arrow function, it caused arguments no longer point the original function.
For example:
Before
```js
class Cls {
async method() {
() => {
console.log(arguments)
}
}
}
```
After:
```js
class Cls {
method() {
var _arguments = arguments;
return babelHelpers.asyncToGenerator(function* () {
() => {
console.log(_arguments);
};
})();
}
}
```
In this way, the `_arguments` is its original function's arguments
### For performance regression
It seems we need to check the IdentifierReference and BindingIdentifier if it's an `arguments`, that causes a significant regression, we may need a cheap way to do checking
After transformation, super expressions have moved to unexpected places. This PR replaces super expression to call expression, and then inserts the super methods to the top of the method body.
For example:
Before:
```js
class G {
async method() {
super.foo()
}
}
```
After:
```js
class G {
method() {
var _superprop_getFoo = () => super.foo,
_this = this;
return _asyncToGenerator(function* () {
_superprop_getFoo().call(_this);
})();
}
}```
Support for inferring function name from ObjectPropertyValue's key
For example:
```js
({ foo: async function() {} })
```
After this, we will able to infer `foo` for the object method