From 4aa4e6bec0852ffab40422409c8a3cacde1a3992 Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Tue, 8 Oct 2024 01:32:05 +0000 Subject: [PATCH] refactor(transformer): exponentiation transform: do not wrap in `SequenceExpression` if not needed (#6343) `left **= right` is transformed to `left = Math.pow(left, right)`. There is no need to wrap it in a `SequenceExpression`. --- .../src/es2016/exponentiation_operator.rs | 24 ++++++++++++------- .../snapshots/oxc.snap.md | 2 +- .../test/fixtures/assign-used-result/input.js | 14 +++++++++++ .../fixtures/assign-used-result/output.js | 15 ++++++++++++ 4 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-used-result/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-used-result/output.js diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs index abe114027ec60..b59343b2fd0d0 100644 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs @@ -37,7 +37,7 @@ use oxc_ast::{ast::*, NONE}; use oxc_semantic::{ReferenceFlags, SymbolFlags}; use oxc_span::SPAN; use oxc_syntax::operator::{AssignmentOperator, BinaryOperator}; -use oxc_traverse::{BoundIdentifier, Traverse, TraverseCtx}; +use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx}; use crate::TransformCtx; @@ -142,7 +142,7 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { /// Get left side of `Math.pow(pow_left, ...)` for identifier fn get_pow_left_identifier( &mut self, - ident: &IdentifierReference<'a>, + ident: &mut IdentifierReference<'a>, ctx: &mut TraverseCtx<'a>, ) -> ( // Left side of `Math.pow(pow_left, ...)` @@ -153,10 +153,16 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { let mut temp_var_inits = ctx.ast.vec(); // Make sure side-effects of evaluating `left` only happen once - let symbol_id = ctx.symbols().get_reference(ident.reference_id().unwrap()).symbol_id(); - let pow_left = if let Some(symbol_id) = symbol_id { + let reference = ctx.scoping.symbols_mut().get_reference_mut(ident.reference_id().unwrap()); + let pow_left = if let Some(symbol_id) = reference.symbol_id() { // This variable is declared in scope so evaluating it multiple times can't trigger a getter. // No need for a temp var. + // `left **= right` is being transformed to `left = Math.pow(left, right)`, + // so if `left` is no longer being read from, update its `ReferenceFlags`. + if matches!(ctx.ancestry.parent(), Ancestor::ExpressionStatementExpression(_)) { + *reference.flags_mut() = ReferenceFlags::Write; + } + ctx.ast.expression_from_identifier_reference(ctx.create_bound_reference_id( SPAN, ident.name.clone(), @@ -447,15 +453,17 @@ impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> { assign_expr.operator = AssignmentOperator::Assign; } - /// Replace expression `expr` with `(temp1, temp2, expr)` (temp1, temp2 etc from `temp_var_inits`) + /// If needs temp var initializers, replace expression `expr` with `(temp1, temp2, expr)`. fn revise_expression( expr: &mut Expression<'a>, mut temp_var_inits: Vec<'a, Expression<'a>>, ctx: &mut TraverseCtx<'a>, ) { - temp_var_inits.reserve_exact(1); - temp_var_inits.push(ctx.ast.move_expression(expr)); - *expr = ctx.ast.expression_sequence(SPAN, temp_var_inits); + if !temp_var_inits.is_empty() { + temp_var_inits.reserve_exact(1); + temp_var_inits.push(ctx.ast.move_expression(expr)); + *expr = ctx.ast.expression_sequence(SPAN, temp_var_inits); + } } /// `Math.pow(left, right)` diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index d9c7f6b7d05d6..3fae10349d2e5 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 3bcfee23 -Passed: 59/68 +Passed: 60/69 # All Passed: * babel-plugin-transform-nullish-coalescing-operator diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-used-result/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-used-result/input.js new file mode 100644 index 0000000000000..6fff73f4faf7d --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-used-result/input.js @@ -0,0 +1,14 @@ +let bound, boundObj, boundProp; + +x = bound **= 1; +x = unbound **= 2; + +x = boundObj.prop **= 3; +x = unboundObj.prop **= 4; +x = boundObj.foo.bar.qux **= 5; +x = unboundObj.foo.bar.qux **= 6; + +x = boundObj[boundProp] **= 7; +x = boundObj[unboundProp] **= 8; +x = unboundObj[boundProp] **= 9; +x = unboundObj[unboundProp] **= 10; diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-used-result/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-used-result/output.js new file mode 100644 index 0000000000000..a8c651aa9b953 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-exponentiation-operator/test/fixtures/assign-used-result/output.js @@ -0,0 +1,15 @@ +var _unbound, _unboundObj, _boundObj$foo$bar, _unboundObj$foo$bar, _boundProp, _unboundProp, _unboundObj2, _boundProp2, _unboundObj3, _unboundProp2; +let bound, boundObj, boundProp; + +x = bound = Math.pow(bound, 1); +x = (_unbound = unbound, unbound = Math.pow(_unbound, 2)); + +x = boundObj["prop"] = Math.pow(boundObj["prop"], 3); +x = (_unboundObj = unboundObj, _unboundObj["prop"] = Math.pow(_unboundObj["prop"], 4)); +x = (_boundObj$foo$bar = boundObj.foo.bar, _boundObj$foo$bar["qux"] = Math.pow(_boundObj$foo$bar["qux"], 5)); +x = (_unboundObj$foo$bar = unboundObj.foo.bar, _unboundObj$foo$bar["qux"] = Math.pow(_unboundObj$foo$bar["qux"], 6)); + +x = (_boundProp = boundProp, boundObj[_boundProp] = Math.pow(boundObj[_boundProp], 7)); +x = (_unboundProp = unboundProp, boundObj[_unboundProp] = Math.pow(boundObj[_unboundProp], 8)); +x = (_unboundObj2 = unboundObj, _boundProp2 = boundProp, _unboundObj2[_boundProp2] = Math.pow(_unboundObj2[_boundProp2], 9)); +x = (_unboundObj3 = unboundObj, _unboundProp2 = unboundProp, _unboundObj3[_unboundProp2] = Math.pow(_unboundObj3[_unboundProp2], 10));