mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(transformer/class-properties): transform super member expressions that are inside static prop initializer (#7815)
This PR support for transforming `super.prop` to `babelHelpers.superPropGet(_B, "prop", _B)`
Input:
```js
class A {
static prop = 1;
}
class B extends A {
static prop = 2;
static propA = super.prop;
static getPropA = () => super.prop;
}
```
Output:
```js
var _B;
class A {}
babelHelpers.defineProperty(A, "prop", 1);
class B extends A {}
_B = B;
babelHelpers.defineProperty(B, "prop", 2);
babelHelpers.defineProperty(B, "propA", babelHelpers.superPropGet(_B, "prop", _B));
babelHelpers.defineProperty(B, "getPropA", () => babelHelpers.superPropGet(_B, "prop", _B));
```
This commit is contained in:
parent
b290ebd2b0
commit
e727ae9698
6 changed files with 113 additions and 21 deletions
|
|
@ -158,6 +158,7 @@ pub enum Helper {
|
|||
ToSetter,
|
||||
ClassPrivateFieldLooseKey,
|
||||
ClassPrivateFieldLooseBase,
|
||||
SuperPropGet,
|
||||
}
|
||||
|
||||
impl Helper {
|
||||
|
|
@ -181,6 +182,7 @@ impl Helper {
|
|||
Self::ToSetter => "toSetter",
|
||||
Self::ClassPrivateFieldLooseKey => "classPrivateFieldLooseKey",
|
||||
Self::ClassPrivateFieldLooseBase => "classPrivateFieldLooseBase",
|
||||
Self::SuperPropGet => "superPropGet",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@
|
|||
//! * `private_props.rs`: Structures storing details of private properties.
|
||||
//! * `static_prop_init.rs`: Transform of static property initializers.
|
||||
//! * `class_bindings.rs`: Structure containing bindings for class name and temp var.
|
||||
//! * `super.rs`: Transform `super` expressions.
|
||||
//! * `utils.rs`: Utility functions.
|
||||
//!
|
||||
//! ## References
|
||||
|
|
@ -159,6 +160,7 @@ mod constructor;
|
|||
mod private;
|
||||
mod private_props;
|
||||
mod static_prop_init;
|
||||
mod supers;
|
||||
mod utils;
|
||||
use class_bindings::ClassBindings;
|
||||
use private_props::PrivatePropsStack;
|
||||
|
|
|
|||
|
|
@ -200,6 +200,14 @@ impl<'a, 'ctx, 'v> VisitMut<'a> for StaticInitializerVisitor<'a, 'ctx, 'v> {
|
|||
}
|
||||
}
|
||||
}
|
||||
// `super.prop`
|
||||
Expression::StaticMemberExpression(_) => {
|
||||
self.class_properties.transform_static_member_expression(expr, self.ctx);
|
||||
}
|
||||
// `super[prop]`
|
||||
Expression::ComputedMemberExpression(_) => {
|
||||
self.class_properties.transform_computed_member_expression(expr, self.ctx);
|
||||
}
|
||||
// `object.#prop`
|
||||
Expression::PrivateFieldExpression(_) => {
|
||||
self.class_properties.transform_private_field_expression(expr, self.ctx);
|
||||
|
|
@ -453,7 +461,7 @@ impl<'a, 'ctx, 'v> StaticInitializerVisitor<'a, 'ctx, 'v> {
|
|||
}
|
||||
|
||||
impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
||||
fn get_temp_binding(&mut self, ctx: &mut TraverseCtx<'a>) -> &BoundIdentifier<'a> {
|
||||
pub(super) fn get_temp_binding(&mut self, ctx: &mut TraverseCtx<'a>) -> &BoundIdentifier<'a> {
|
||||
// `PrivateProps` is the source of truth for bindings if class has private props
|
||||
// because other visitors which transform private fields may create a temp binding
|
||||
// and store it on `PrivateProps`
|
||||
|
|
|
|||
86
crates/oxc_transformer/src/es2022/class_properties/supers.rs
Normal file
86
crates/oxc_transformer/src/es2022/class_properties/supers.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
//! ES2022: Class Properties
|
||||
//! Transform of `super` expressions.
|
||||
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use crate::Helper;
|
||||
|
||||
use super::ClassProperties;
|
||||
|
||||
impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
||||
/// Transform static member expression where object is `super`.
|
||||
///
|
||||
/// `super.prop` -> `_superPropGet(_Class, "prop", _Class)`
|
||||
//
|
||||
// `#[inline]` so that compiler sees that `expr` is an `Expression::StaticMemberExpression`.
|
||||
#[inline]
|
||||
pub(super) fn transform_static_member_expression(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let Expression::StaticMemberExpression(member) = expr else { unreachable!() };
|
||||
if matches!(member.object, Expression::Super(_)) {
|
||||
*expr = self.transform_static_member_expression_impl(member, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_static_member_expression_impl(
|
||||
&mut self,
|
||||
member: &mut StaticMemberExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let property = &member.property;
|
||||
let property = ctx.ast.expression_string_literal(
|
||||
property.span,
|
||||
property.name.clone(),
|
||||
Some(property.name.clone()),
|
||||
);
|
||||
self.create_super_prop_get(member.span, property, ctx)
|
||||
}
|
||||
|
||||
/// Transform computed member expression where object is `super`.
|
||||
///
|
||||
/// `super[prop]` -> `_superPropGet(_Class, prop, _Class)`
|
||||
//
|
||||
// `#[inline]` so that compiler sees that `expr` is an `Expression::ComputedMemberExpression`.
|
||||
#[inline]
|
||||
pub(super) fn transform_computed_member_expression(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let Expression::ComputedMemberExpression(member) = expr else { unreachable!() };
|
||||
if matches!(member.object, Expression::Super(_)) {
|
||||
*expr = self.transform_computed_member_expression_impl(member, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_computed_member_expression_impl(
|
||||
&mut self,
|
||||
member: &mut ComputedMemberExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let property = ctx.ast.move_expression(&mut member.expression);
|
||||
self.create_super_prop_get(member.span, property, ctx)
|
||||
}
|
||||
|
||||
/// `_superPropGet(_Class, prop, _Class)`
|
||||
fn create_super_prop_get(
|
||||
&mut self,
|
||||
span: Span,
|
||||
property: Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let class_binding = self.get_temp_binding(ctx);
|
||||
// `(_Class, prop, _Class)`
|
||||
let arguments = ctx.ast.vec_from_array([
|
||||
Argument::from(class_binding.create_read_expression(ctx)),
|
||||
Argument::from(property),
|
||||
Argument::from(class_binding.create_read_expression(ctx)),
|
||||
]);
|
||||
// `_superPropGet(_Class, prop, _Class)`
|
||||
self.ctx.helper_call_expr(Helper::SuperPropGet, span, arguments, ctx)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
commit: 54a8389f
|
||||
|
||||
Passed: 562/927
|
||||
Passed: 564/927
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-class-static-block
|
||||
|
|
@ -276,7 +276,7 @@ x Output mismatch
|
|||
x Output mismatch
|
||||
|
||||
|
||||
# babel-plugin-transform-class-properties (176/264)
|
||||
# babel-plugin-transform-class-properties (178/264)
|
||||
* assumption-constantSuper/complex-super-class/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
|
@ -341,9 +341,6 @@ rebuilt : ScopeId(9): Some(ScopeId(8))
|
|||
* assumption-setPublicClassFields/static-infer-name/input.js
|
||||
x Output mismatch
|
||||
|
||||
* assumption-setPublicClassFields/static-super/input.js
|
||||
x Output mismatch
|
||||
|
||||
* assumption-setPublicClassFields/static-super-loose/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
|
@ -645,9 +642,6 @@ rebuilt : ScopeId(9): Some(ScopeId(8))
|
|||
* public/static-infer-name/input.js
|
||||
x Output mismatch
|
||||
|
||||
* public/static-super/input.js
|
||||
x Output mismatch
|
||||
|
||||
* public/super-with-collision/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
|
@ -713,7 +707,15 @@ x Output mismatch
|
|||
x Output mismatch
|
||||
|
||||
* regression/6154/input.js
|
||||
x Output mismatch
|
||||
Scope children mismatch:
|
||||
after transform: ScopeId(3): [ScopeId(4), ScopeId(6)]
|
||||
rebuilt : ScopeId(3): [ScopeId(4)]
|
||||
Scope children mismatch:
|
||||
after transform: ScopeId(6): []
|
||||
rebuilt : ScopeId(4): [ScopeId(5)]
|
||||
Scope parent mismatch:
|
||||
after transform: ScopeId(4): Some(ScopeId(3))
|
||||
rebuilt : ScopeId(5): Some(ScopeId(4))
|
||||
|
||||
* regression/7951/input.mjs
|
||||
x Output mismatch
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ commit: 54a8389f
|
|||
|
||||
node: v22.12.0
|
||||
|
||||
Passed: 186 of 215 (86.51%)
|
||||
Passed: 189 of 215 (87.91%)
|
||||
|
||||
Failures:
|
||||
|
||||
|
|
@ -13,9 +13,6 @@ Failures:
|
|||
AssertionError: expected '_Class' to be 'Foo' // Object.is equality
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-infer-name-exec.test.js:8:19
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-assumption-setPublicClassFields-static-super-exec.test.js
|
||||
Invalid access to super
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-call-in-decorator-exec.test.js
|
||||
AssertionError: expected undefined to be 'hello' // Object.is equality
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-nested-class-super-call-in-decorator-exec.test.js:21:28
|
||||
|
|
@ -98,22 +95,17 @@ AssertionError: expected [Function] to throw error including '@@toPrimitive must
|
|||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-computed-toPrimitive-exec.test.js:37:5
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-delete-super-property-exec.test.js
|
||||
Invalid access to super
|
||||
AssertionError: expected function to throw an error, but it didn't
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-delete-super-property-exec.test.js:25:5
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-infer-name-exec.test.js
|
||||
AssertionError: expected '_Class' to be 'Foo' // Object.is equality
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-infer-name-exec.test.js:8:19
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-loose-static-super-exec.test.js
|
||||
Invalid access to super
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-static-infer-name-exec.test.js
|
||||
AssertionError: expected '_Class' to be 'Foo' // Object.is equality
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-static-infer-name-exec.test.js:9:19
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-static-super-exec.test.js
|
||||
Invalid access to super
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js
|
||||
TypeError: Cannot read properties of undefined (reading 'x')
|
||||
at m (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js:10:16)
|
||||
|
|
|
|||
Loading…
Reference in a new issue