diff --git a/crates/oxc_transformer/src/common/helper_loader.rs b/crates/oxc_transformer/src/common/helper_loader.rs index 22994314c2be5..2d50fb76c7f90 100644 --- a/crates/oxc_transformer/src/common/helper_loader.rs +++ b/crates/oxc_transformer/src/common/helper_loader.rs @@ -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", } } } diff --git a/crates/oxc_transformer/src/es2022/class_properties/mod.rs b/crates/oxc_transformer/src/es2022/class_properties/mod.rs index 69b0ce71559c3..b507cc3f3d71b 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/mod.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/mod.rs @@ -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; diff --git a/crates/oxc_transformer/src/es2022/class_properties/static_prop_init.rs b/crates/oxc_transformer/src/es2022/class_properties/static_prop_init.rs index 4a91750f1ec4b..a535c85e5f054 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/static_prop_init.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/static_prop_init.rs @@ -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` diff --git a/crates/oxc_transformer/src/es2022/class_properties/supers.rs b/crates/oxc_transformer/src/es2022/class_properties/supers.rs new file mode 100644 index 0000000000000..ed357ffa453ef --- /dev/null +++ b/crates/oxc_transformer/src/es2022/class_properties/supers.rs @@ -0,0 +1,83 @@ +//! 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(), None); + 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) + } +} diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index 0b2777a627811..7a72ff8d9fa86 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -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 diff --git a/tasks/transform_conformance/snapshots/babel_exec.snap.md b/tasks/transform_conformance/snapshots/babel_exec.snap.md index 87fe948a7cec7..42d1055fe368f 100644 --- a/tasks/transform_conformance/snapshots/babel_exec.snap.md +++ b/tasks/transform_conformance/snapshots/babel_exec.snap.md @@ -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)