From a9492811af9e62ed2d3f7d1e0b745e7ef5710c33 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Tue, 17 Dec 2024 17:12:25 +0800 Subject: [PATCH] feat(transformer/class-properties): transform super assignment expressions that are inside static prop initializer --- crates/oxc_ast/src/ast_impl/js.rs | 6 ++ .../src/common/helper_loader.rs | 2 + .../es2022/class_properties/private_field.rs | 11 ++- .../class_properties/static_prop_init.rs | 1 + .../src/es2022/class_properties/supers.rs | 93 ++++++++++++++++++- .../snapshots/oxc.snap.md | 4 +- .../input.js | 17 ++++ .../output.js | 33 +++++++ 8 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-assignment-expression/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-assignment-expression/output.js diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index 98ac44e7c6c7e..65430ab02e64d 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -1515,3 +1515,9 @@ impl ImportPhase { } } } + +impl<'a> From> for StringLiteral<'a> { + fn from(ident: IdentifierName<'a>) -> Self { + Self { span: ident.span, value: ident.name.clone(), raw: None } + } +} diff --git a/crates/oxc_transformer/src/common/helper_loader.rs b/crates/oxc_transformer/src/common/helper_loader.rs index 2d50fb76c7f90..26213a55c3326 100644 --- a/crates/oxc_transformer/src/common/helper_loader.rs +++ b/crates/oxc_transformer/src/common/helper_loader.rs @@ -159,6 +159,7 @@ pub enum Helper { ClassPrivateFieldLooseKey, ClassPrivateFieldLooseBase, SuperPropGet, + SuperPropSet, } impl Helper { @@ -183,6 +184,7 @@ impl Helper { Self::ClassPrivateFieldLooseKey => "classPrivateFieldLooseKey", Self::ClassPrivateFieldLooseBase => "classPrivateFieldLooseBase", Self::SuperPropGet => "superPropGet", + Self::SuperPropSet => "superPropSet", } } } diff --git a/crates/oxc_transformer/src/es2022/class_properties/private_field.rs b/crates/oxc_transformer/src/es2022/class_properties/private_field.rs index 68cafcf3766c4..8ab1e7eb6e73f 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/private_field.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/private_field.rs @@ -614,7 +614,16 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { let AssignmentExpression { span, operator, right: value, left } = assign_expr; let object = match left { AssignmentTarget::PrivateFieldExpression(field_expr) => field_expr.unbox().object, - _ => unreachable!(), + // For super assignment expression + AssignmentTarget::StaticMemberExpression(member_expr) => Expression::StringLiteral( + ctx.ast.alloc(StringLiteral::from(member_expr.property.clone())), + ), + AssignmentTarget::ComputedMemberExpression(member_expr) => { + member_expr.unbox().expression + } + _ => { + unreachable!(); + } }; if operator == AssignmentOperator::Assign { 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 5dce0179336d0..2e63c4e69a7c6 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 @@ -221,6 +221,7 @@ impl<'a, 'ctx, 'v> VisitMut<'a> for StaticInitializerVisitor<'a, 'ctx, 'v> { // `object.#prop = value`, `object.#prop += value`, `object.#prop ??= value` etc Expression::AssignmentExpression(_) => { self.class_properties.transform_assignment_expression(expr, self.ctx); + self.class_properties.transform_super_assignment_expression(expr, self.ctx); } // `object.#prop++`, `--object.#prop` Expression::UpdateExpression(_) => { diff --git a/crates/oxc_transformer/src/es2022/class_properties/supers.rs b/crates/oxc_transformer/src/es2022/class_properties/supers.rs index 1616c6810d6bd..11889d75c9bfe 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/supers.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/supers.rs @@ -34,9 +34,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { 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); + let property = Expression::StringLiteral(ctx.ast.alloc(member.property.clone().into())); self.create_super_prop_get(member.span, property, is_callee, ctx) } @@ -125,6 +123,71 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { arguments.push(Argument::from(array)); } + /// Transform assignment expression where left-hand side contains `super`. + /// + /// * `object.#prop = value` + /// -> `_superPropSet(_Class, prop, value, _Class)` + /// * `object.#prop += value` + /// -> `_superPropSet(_Class, prop, _superPropGet(_Class, prop, _Class) + value, _Class)` + /// * `object.#prop &&= value` + /// -> `_superPropGet(_Class, prop, _Class) && _superPropSet(_Class, prop, value, _Class)` + // + // `#[inline]` so that compiler sees that `expr` is an `Expression::AssignmentExpression` + pub(super) fn transform_super_assignment_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::AssignmentExpression(assign_expr) = expr else { unreachable!() }; + match &assign_expr.left { + AssignmentTarget::StaticMemberExpression(member) if member.object.is_super() => { + self.transform_assignment_expression_for_super_static_member_expr(expr, ctx); + } + AssignmentTarget::ComputedMemberExpression(member) if member.object.is_super() => { + self.transform_assignment_expression_for_super_computed_member_expr(expr, ctx); + } + _ => {} + }; + } + + fn transform_assignment_expression_for_super_static_member_expr( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let getter = + |s: &mut Self, object: Expression<'a>, span: Span, ctx: &mut TraverseCtx<'a>| { + s.create_super_prop_get(span, object, false, ctx) + }; + let setter = |s: &mut Self, + object: Expression<'a>, + value: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>| { + s.create_super_prop_set(span, object, value, ctx) + }; + self.transform_instance_assignment_expression(expr, getter, setter, ctx); + } + + fn transform_assignment_expression_for_super_computed_member_expr( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let getter = + |s: &mut Self, object: Expression<'a>, span: Span, ctx: &mut TraverseCtx<'a>| { + s.create_super_prop_get(span, object, false, ctx) + }; + let setter = |s: &mut Self, + object: Expression<'a>, + value: Expression<'a>, + span: Span, + ctx: &mut TraverseCtx<'a>| { + s.create_super_prop_set(span, object, value, ctx) + }; + self.transform_instance_assignment_expression(expr, getter, setter, ctx); + } + /// Member: /// `_superPropGet(_Class, prop, _Class)` /// @@ -155,4 +218,28 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> { // `_superPropGet(_Class, prop, _Class)` or `_superPropGet(_Class, prop, _Class, 2)` self.ctx.helper_call_expr(Helper::SuperPropGet, span, arguments, ctx) } + + /// `_superPropSet(_Class, prop, value, _Class)` + fn create_super_prop_set( + &mut self, + span: Span, + property: Expression<'a>, + value: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let temp_binding = self.current_class_mut().bindings.get_or_init_temp_binding(ctx); + let arguments = ctx.ast.vec_from_array([ + Argument::from(temp_binding.create_read_expression(ctx)), + Argument::from(property), + Argument::from(value), + Argument::from(temp_binding.create_read_expression(ctx)), + Argument::from(ctx.ast.expression_numeric_literal( + SPAN, + 1.0, + None, + NumberBase::Decimal, + )), + ]); + self.ctx.helper_call_expr(Helper::SuperPropSet, span, arguments, ctx) + } } diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index 19612c36acd99..8ae859d65a050 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 111/125 +Passed: 113/127 # All Passed: * babel-plugin-transform-class-static-block @@ -16,7 +16,7 @@ Passed: 111/125 * regexp -# babel-plugin-transform-class-properties (12/14) +# babel-plugin-transform-class-properties (14/16) * 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-assignment-expression/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-assignment-expression/input.js new file mode 100644 index 0000000000000..e8e0238c3297c --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-assignment-expression/input.js @@ -0,0 +1,17 @@ +const ident = "A"; +class Outer { + static A = 0; + static B = () => { + super.A += 1; + super.A -= 1; + super.A &&= 1; + super.A ||= 1; + super.A = 1; + + super[ident] += 1; + super[ident] -= 1; + super[ident] &&= 1; + super[ident] ||= 1; + super[ident] = 1; + }; +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-assignment-expression/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-assignment-expression/output.js new file mode 100644 index 0000000000000..f407f5c1279b7 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-class-properties/test/fixtures/public-static-super-assignment-expression/output.js @@ -0,0 +1,33 @@ +var _Outer; +const ident = "A"; +class Outer {} +_Outer = Outer; +babelHelpers.defineProperty(Outer, "A", 0); +babelHelpers.defineProperty(Outer, "B", () => { + babelHelpers.superPropSet(_Outer, "A", babelHelpers.superPropGet(_Outer, "A", _Outer) + 1, _Outer, 1); + babelHelpers.superPropSet(_Outer, "A", babelHelpers.superPropGet(_Outer, "A", _Outer) - 1, _Outer, 1); + babelHelpers.superPropGet(_Outer, "A", _Outer) && + babelHelpers.superPropSet(_Outer, "A", 1, _Outer, 1); + babelHelpers.superPropGet(_Outer, "A", _Outer) || + babelHelpers.superPropSet(_Outer, "A", 1, _Outer, 1); + babelHelpers.superPropSet(_Outer, "A", 1, _Outer, 1); + babelHelpers.superPropSet( + _Outer, + ident, + babelHelpers.superPropGet(_Outer, ident, _Outer) + 1, + _Outer, + 1, + ); + babelHelpers.superPropSet( + _Outer, + ident, + babelHelpers.superPropGet(_Outer, ident, _Outer) - 1, + _Outer, + 1, + ); + babelHelpers.superPropGet(_Outer, ident, _Outer) && + babelHelpers.superPropSet(_Outer, ident, 1, _Outer, 1); + babelHelpers.superPropGet(_Outer, ident, _Outer) || + babelHelpers.superPropSet(_Outer, ident, 1, _Outer, 1); + babelHelpers.superPropSet(_Outer, ident, 1, _Outer, 1); +});