This PR supports transforming private `getter` or `setter` methods, and the output is a little different from Babel's output, For example:
Input:
```js
class Cls {
#prop = 0;
get #accessor() {
return this.#prop;
}
set #accessor(v) {
console.log(arguments);
this.#prop = v;
}
constructor() {
console.log(this.#accessor)
[this.#accessor] = [1];
}
}
```
[Babel's output](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=MYGwhgzhAEDCIwN4ChrQMQAcBOB7T0AvNAAwDcyq0A5gKYAuGYwwtUu2AFAJTQpppsDAK7YAdtHoALAJYQAdFjyYKaAL5UIDJizYQOnAG69-A4LjH6QteSFzVOYbNWEBbWmPoRuqgdLmKOPhE0Ia-GlTmlvTYwsD0BiZUaFFWNnYO_grozKzs2NzJ0ADaWYq5ehwAuiHFAIxV4cgaQA&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&evaluate=false&fileSize=false&timeTravel=false&sourceType=script&lineWrap=true&presets=&prettier=false&targets=&version=7.26.4&externalPlugins=%40babel%2Fplugin-transform-class-properties%407.25.9%2C%40babel%2Fplugin-transform-private-methods%407.25.9%2C%40babel%2Fplugin-external-helpers%407.25.9&assumptions=%7B%7D):
```js
var _prop = new WeakMap();
var _Cls_brand = new WeakSet();
class Cls {
constructor() {
babelHelpers.classPrivateMethodInitSpec(this, _Cls_brand);
babelHelpers.classPrivateFieldInitSpec(this, _prop, 0);
console.log(babelHelpers.classPrivateGetter(_Cls_brand, this, _get_accessor));
[babelHelpers.classPrivateGetter(_Cls_brand, this, _get_accessor)] = [1];
}
}
function _get_accessor(_this) {
return babelHelpers.classPrivateFieldGet(_prop, _this);
}
function _set_accessor(_this2, v) {
var _arguments = [].slice.call(arguments, 1);
console.log(_arguments);
babelHelpers.classPrivateFieldSet(_prop, _this2, v);
}
```
Oxc's output:
```js
var _prop = new WeakMap();
var _Cls_brand = new WeakSet();
class Cls {
constructor() {
babelHelpers.classPrivateMethodInitSpec(this, _Cls_brand);
babelHelpers.classPrivateFieldInitSpec(this, _prop, 0);
console.log(_get_accessor.call(babelHelpers.assertClassBrand(_Cls_brand, this)));
[babelHelpers.toSetter(_set_accessor.bind(babelHelpers.assertClassBrand(_Cls_brand, this)), [])._] = [1];
}
}
function _get_accessor() {
return babelHelpers.classPrivateFieldGet2(_prop, this);
}
function _set_accessor(v) {
console.log(arguments);
babelHelpers.classPrivateFieldSet2(_prop, this, v);
}
```
### Main difference
```diff
// Babel
+ console.log(babelHelpers.classPrivateGetter(_Cls_brand, this, _get_accessor));
+ [babelHelpers.classPrivateGetter(_Cls_brand, this, _get_accessor)] = [1];
+ function _get_accessor(_this) {
+ return babelHelpers.classPrivateFieldGet(_prop, _this);
+ }
+ function _set_accessor(_this2, v) {
+ var _arguments = [].slice.call(arguments, 1);
+ console.log(_arguments);
+ babelHelpers.classPrivateFieldSet(_prop, _this2, v);
+ }
// Oxc
- console.log(_get_accessor.call(babelHelpers.assertClassBrand(_Cls_brand, this)));- -
- [babelHelpers.toSetter(_set_accessor.bind(babelHelpers.assertClassBrand(_Cls_brand, this)), [])._] = [1];
- function _get_accessor() {
- return babelHelpers.classPrivateFieldGet2(_prop, this);
- }
- function _set_accessor(v) {
- console.log(arguments);
- babelHelpers.classPrivateFieldSet2(_prop, this, v);
- }
```
From the main differences, we can see that Babel handles `getter` and `setter` methods using `classPrivateGetter` and `classPrivateSetter` helper functions, which causes all use `this` and `arguments` needs to rewrite to use a temp var instead in `getter` and `setter` methods. This is unnecessary and is not an efficient transformation for us.
Instead, I adapt binding a `this` instead of passing in `this`, this way we don't need to rewrite anything. We can't control the `helper` library for now, so I just transformed the AST to bind `this`, in the future, we can create a helper function to do the same thing.
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 creating class constructor for a class which has super class, use UID `_args` for temp var (rather than `args`). This avoids shadowing a var called `args` used in an instance property initializer.
This diverges from Babel. Babel uses `args` unless it finds a var called `args` in an instance property initializer. But searching the AST of initializers can be fairly expensive, so it's better to skip it. The overrides for test fixtures included in this PR are just to account for that difference.
This PR does the following things.
1. Move the override output of the `snapshots` folder to the `overrides` folder.
2. Support `override` mode to replace `takeover` mode
3. The `update_fixtures.js` no longer uses `overrides`'s `output.js` to replace Babel's `output.js`.
### How does `override` mode work?
When running each test, it checks whether an output file for that test exists in the `overrides` directory. If it does, the output file will be used to compare with the transformed code.
In two of the overridden text fixtures for class properties transform, there was no `output.js` file because what was overridden was just `options.json` and `update_fixtures.js` script then generated new output files using Babel with the new options.
That was fine, but doesn't work with #7771. So add `output.js` files to the these overrides too.
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.
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.
Override some transform conformance test fixtures for class properties transform, where:
* Our output differs from Babel in cosmetic manner only.
* Our transform intentionally works differently from Babel.
* Babel's fixtures enable arrow functions transform, which malfunctions in our implementation. But we're not trying to test arrow functions transform here, so disable it.