From ccb7bdcc5682eebe3cc5f9ebf19670af0e8df7d7 Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Sun, 6 Oct 2024 23:08:08 +0000 Subject: [PATCH] fix(transformer): exponentiation transform: do not replace object when private property (#6313) Fix exponentiation operator transform to bail out early if a private class property. We can't transform this: ```js class C { #p; method() { this.#p **= 2; } } ``` But we should at least leave it alone. Previously `get_obj_ref` called `ast.move_expression(expr)` on the member expression's object before bailing out, so example above was transformed to: ```js class C { #p; method() { null.#p **= 2; // <-- `null` } } ``` This PR makes it spot this case early and bail out *before* calling `ast.move_expression(expr)`. --- .../src/es2016/exponentiation_operator.rs | 29 ++++++++++--------- .../snapshots/oxc.snap.md | 2 +- .../fixtures/bail-private-properties/input.js | 9 ++++++ .../bail-private-properties/output.js | 9 ++++++ 4 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/bail-private-properties/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/bail-private-properties/output.js diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs index 768846dba27e1..731436df140c2 100644 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs @@ -171,10 +171,10 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { (ident, obj) } match_member_expression!(AssignmentTarget) => { - let obj = self.get_obj_ref(node, nodes, ctx).unwrap(); + let obj = self.get_obj_ref(node, nodes, ctx)?; let member_expr = node.to_member_expression_mut(); let computed = member_expr.is_computed(); - let prop = self.get_prop_ref(member_expr, nodes, ctx)?; + let prop = self.get_prop_ref(member_expr, nodes, ctx); let optional = false; let obj_clone = Self::clone_expression(&obj, ctx); let (reference, uid) = match &prop { @@ -248,7 +248,11 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { let expr = match node.to_member_expression_mut() { MemberExpression::ComputedMemberExpression(e) => &mut e.object, MemberExpression::StaticMemberExpression(e) => &mut e.object, - MemberExpression::PrivateFieldExpression(e) => &mut e.object, + // From Babel: "We can't generate property ref for private name, please install + // `@babel/plugin-transform-class-properties`". + // TODO: Ensure this plugin interacts correctly with class private properties + // transform, so the property is transformed before this transform. + MemberExpression::PrivateFieldExpression(_) => return None, }; let expr = ctx.ast.move_expression(expr); // the object reference that we need to save is locally declared @@ -277,24 +281,21 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { node: &mut MemberExpression<'a>, nodes: &mut Vec<'a, Expression<'a>>, ctx: &mut TraverseCtx<'a>, - ) -> Option> { - let expr = match node { + ) -> Expression<'a> { + match node { MemberExpression::ComputedMemberExpression(expr) => { let expr = ctx.ast.move_expression(&mut expr.expression); if expr.is_literal() { - return Some(expr); + return expr; } - expr + self.add_new_reference(expr, nodes, ctx) } MemberExpression::StaticMemberExpression(expr) => { - return Some(ctx.ast.expression_string_literal(SPAN, expr.property.name.clone())); - } - MemberExpression::PrivateFieldExpression(_) => { - // From babel: "We can't generate property ref for private name, please install `@babel/plugin-transform-class-properties`" - return None; + ctx.ast.expression_string_literal(SPAN, expr.property.name.clone()) } - }; - Some(self.add_new_reference(expr, nodes, ctx)) + // This possibility is ruled out in earlier call to `get_obj_ref` + MemberExpression::PrivateFieldExpression(_) => unreachable!(), + } } fn add_new_reference( diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index a148b5a3b5a9a..8eec97bd90bd8 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 3bcfee23 -Passed: 56/65 +Passed: 57/66 # All Passed: * babel-plugin-transform-nullish-coalescing-operator diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/bail-private-properties/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/bail-private-properties/input.js new file mode 100644 index 0000000000000..b61b2daf9cda1 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/bail-private-properties/input.js @@ -0,0 +1,9 @@ +class C { + #p = 1; + + method(obj) { + obj.x **= 2; // Transform + obj['y'] **= 3; // Transform + obj.#p **= 4; // Bail + } +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/bail-private-properties/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/bail-private-properties/output.js new file mode 100644 index 0000000000000..def6e695e4bb5 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/bail-private-properties/output.js @@ -0,0 +1,9 @@ +class C { + #p = 1; + + method(obj) { + obj["x"] = Math.pow(obj["x"], 2); + obj["y"] = Math.pow(obj["y"], 3); + obj.#p **= 4; + } +}