diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index cfe8a373c56ea..11ded278c0805 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -244,6 +244,11 @@ impl<'a> Expression<'a> { matches!(self, Expression::CallExpression(_)) } + #[allow(missing_docs)] + pub fn is_super(&self) -> bool { + matches!(self, Expression::Super(_)) + } + #[allow(missing_docs)] pub fn is_super_call_expression(&self) -> bool { matches!(self, Expression::CallExpression(expr) if matches!(&expr.callee, Expression::Super(_))) 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 a535c85e5f054..cab38c7e47965 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 @@ -214,6 +214,7 @@ impl<'a, 'ctx, 'v> VisitMut<'a> for StaticInitializerVisitor<'a, 'ctx, 'v> { } // `object.#prop()` Expression::CallExpression(_) => { + self.class_properties.transform_super_call_expression(expr, self.ctx); self.class_properties.transform_call_expression(expr, self.ctx); } // `object.#prop = value`, `object.#prop += value`, `object.#prop ??= value` etc diff --git a/crates/oxc_transformer/src/es2022/class_properties/supers.rs b/crates/oxc_transformer/src/es2022/class_properties/supers.rs index ed357ffa453ef..40e8168cda976 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/supers.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/supers.rs @@ -1,7 +1,9 @@ //! ES2022: Class Properties //! Transform of `super` expressions. +use oxc_allocator::Vec as ArenaVec; use oxc_ast::ast::*; +use oxc_span::SPAN; use oxc_traverse::TraverseCtx; use crate::Helper; @@ -21,20 +23,21 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { 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); + if member.object.is_super() { + *expr = self.transform_static_member_expression_impl(member, false, ctx); } } fn transform_static_member_expression_impl( &mut self, member: &mut StaticMemberExpression<'a>, + is_callee: bool, 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) + self.create_super_prop_get(member.span, property, is_callee, ctx) } /// Transform computed member expression where object is `super`. @@ -49,35 +52,87 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { 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); + if member.object.is_super() { + *expr = self.transform_computed_member_expression_impl(member, false, ctx); } } fn transform_computed_member_expression_impl( &mut self, member: &mut ComputedMemberExpression<'a>, + is_callee: bool, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { let property = ctx.ast.move_expression(&mut member.expression); - self.create_super_prop_get(member.span, property, ctx) + self.create_super_prop_get(member.span, property, is_callee, ctx) } - /// `_superPropGet(_Class, prop, _Class)` + /// Transform call expression where callee contains `super`. + /// + /// `super.method()` -> `_superPropGet(_Class, "method", _Class, 2)([])` + /// `super.method(1)` -> `_superPropGet(_Class, "method", _Class, 2)([1])` + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::CallExpression`. + #[inline] + pub(super) fn transform_super_call_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::CallExpression(call) = expr else { unreachable!() }; + let callee = &mut call.callee; + match callee { + Expression::StaticMemberExpression(member) if member.object.is_super() => { + *callee = self.transform_static_member_expression_impl(member, true, ctx); + } + Expression::ComputedMemberExpression(member) if member.object.is_super() => { + *callee = self.transform_computed_member_expression_impl(member, true, ctx); + } + _ => return, + }; + Self::transform_super_call_expression_arguments(&mut call.arguments, ctx); + } + + /// [A, B, C] -> [[A, B, C]] + pub(super) fn transform_super_call_expression_arguments( + arguments: &mut ArenaVec<'a, Argument<'a>>, + ctx: &mut TraverseCtx<'a>, + ) { + let owned_arguments = ctx.ast.move_vec(arguments); + let elements = + ctx.ast.vec_from_iter(owned_arguments.into_iter().map(ArrayExpressionElement::from)); + let array = ctx.ast.expression_array(SPAN, elements, None); + arguments.push(Argument::from(array)); + } + + /// Member: + /// `_superPropGet(_Class, prop, _Class)` + /// + /// Callee: + /// `_superPropGet(_Class, prop, _Class, 2)` fn create_super_prop_get( &mut self, span: Span, property: Expression<'a>, + is_callee: bool, 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)` + + let ident1 = Argument::from(class_binding.create_read_expression(ctx)); + let ident2 = Argument::from(class_binding.create_read_expression(ctx)); + let property = Argument::from(property); + + let arguments = if is_callee { + // `(_Class, prop, _Class, 2)` + let two = ctx.ast.expression_numeric_literal(SPAN, 2.0, None, NumberBase::Decimal); + ctx.ast.vec_from_array([ident1, property, ident2, Argument::from(two)]) + } else { + // `(_Class, prop, _Class)` + ctx.ast.vec_from_array([ident1, property, ident2]) + }; + + // `_superPropGet(_Class, prop, _Class)` or `_superPropGet(_Class, prop, _Class, 2)` self.ctx.helper_call_expr(Helper::SuperPropGet, span, arguments, ctx) } } diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index be452dd4654e3..7ed7ea1b6816f 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 108/122 +Passed: 109/123 # All Passed: * babel-plugin-transform-class-static-block @@ -16,7 +16,7 @@ Passed: 108/122 * regexp -# babel-plugin-transform-class-properties (9/11) +# babel-plugin-transform-class-properties (10/12) * typescript/optional-call/input.ts Symbol reference IDs mismatch for "X": after transform: SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), ReferenceId(11), ReferenceId(16)] diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/input.js new file mode 100644 index 0000000000000..11315bb75a9c1 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/input.js @@ -0,0 +1,5 @@ +class C { + static foo = super.method(); + static bar = super.method(1); + static bar = super.method(1, 2, 3); +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/output.js new file mode 100644 index 0000000000000..826a603ec6ae1 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/output.js @@ -0,0 +1,6 @@ +var _C; +class C {} +_C = C; +babelHelpers.defineProperty(C, "foo", babelHelpers.superPropGet(_C, "method", _C, 2)([])); +babelHelpers.defineProperty(C, "bar", babelHelpers.superPropGet(_C, "method", _C, 2)([1])); +babelHelpers.defineProperty(C, "bar", babelHelpers.superPropGet(_C, "method", _C, 2)([1, 2, 3]));