From ac51643dc1eef8b23ce8d8d76e6148f0b6124b57 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Fri, 13 Dec 2024 16:51:35 +0800 Subject: [PATCH] feat(transformer/class-properties): transform super call expression that is inside static prop initializer --- .../class_properties/static_prop_init.rs | 1 + .../src/es2022/class_properties/supers.rs | 84 ++++++++++++++++--- .../snapshots/oxc.snap.md | 4 +- .../public-static-super-call/input.js | 5 ++ .../public-static-super-call/output.js | 7 ++ 5 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/output.js 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 a535c85e5f0542..cab38c7e479655 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 ed357ffa453efd..fbbb79c0e0eb60 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; @@ -22,19 +24,20 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ) { let Expression::StaticMemberExpression(member) = expr else { unreachable!() }; if matches!(member.object, Expression::Super(_)) { - *expr = self.transform_static_member_expression_impl(member, ctx); + *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`. @@ -50,34 +53,91 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { ) { let Expression::ComputedMemberExpression(member) = expr else { unreachable!() }; if matches!(member.object, Expression::Super(_)) { - *expr = self.transform_computed_member_expression_impl(member, ctx); + *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( + &mut self, + arguments: &mut ArenaVec<'a, Argument<'a>>, + ctx: &mut TraverseCtx<'a>, + ) { + let arguments_owner = ctx.ast.move_vec(arguments); + let elements = + ctx.ast.vec_from_iter(arguments_owner.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 arguments = if is_callee { + // `(_Class, prop, _Class, 2)` + let two = ctx.ast.expression_numeric_literal(SPAN, 2.0, None, NumberBase::Decimal); + let array = [ + Argument::from(class_binding.create_read_expression(ctx)), + Argument::from(property), + Argument::from(class_binding.create_read_expression(ctx)), + Argument::from(two), + ]; + ctx.ast.vec_from_array(array) + } else { + // `(_Class, prop, _Class)` + 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)` 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 be452dd4654e3e..7ed7ea1b6816f0 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 00000000000000..9136d3ae62d45d --- /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 A { + static foo = super.method(); + static bar = super.method(1); + constructor(force) {} +} 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 00000000000000..d1775ddd434dbd --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-call/output.js @@ -0,0 +1,7 @@ +var _A; +class A { + constructor(force) {} +} +_A = A; +babelHelpers.defineProperty(A, "foo", babelHelpers.superPropGet(_A, "method", _A, 2)([])); +babelHelpers.defineProperty(A, "bar", babelHelpers.superPropGet(_A, "method", _A, 2)([1])); \ No newline at end of file