From e010b6a7a0db0db85d25f223e59f7da90d442d4d Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Tue, 10 Dec 2024 02:28:28 +0000 Subject: [PATCH] feat(transformer/logical-assignment-operators): no temp vars for literals (#7759) `TransformCtx::duplicate_expression` (introduced in #7754) don't create temp vars for literals. This produces more compact output for the logical assignment operators transform. This diverges from Babel (it's better!) so add an override for one of Babel's fixtures. Also add further tests for all literal types. --- .../oxc_transformer/src/common/duplicate.rs | 25 +++++++++++++++---- .../snapshots/semantic_typescript.snap | 4 +-- .../null-coalescing-without-other/output.js | 5 ++++ .../snapshots/oxc.snap.md | 2 +- .../fixtures/computed-prop-literal/input.js | 15 +++++++++++ .../fixtures/computed-prop-literal/output.js | 22 ++++++++++++++++ .../computed-prop-template-literal/input.js | 7 ++++++ .../computed-prop-template-literal/output.js | 12 +++++++++ .../literal-member-expression/input.js | 5 ++++ .../literal-member-expression/output.js | 7 ++++++ 10 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 tasks/transform_conformance/overrides/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing-without-other/output.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/output.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/output.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/output.js diff --git a/crates/oxc_transformer/src/common/duplicate.rs b/crates/oxc_transformer/src/common/duplicate.rs index 5728f03441d99..8a8fc73f34dd3 100644 --- a/crates/oxc_transformer/src/common/duplicate.rs +++ b/crates/oxc_transformer/src/common/duplicate.rs @@ -5,6 +5,7 @@ use std::{ ptr, }; +use oxc_allocator::CloneIn; use oxc_ast::ast::{AssignmentOperator, Expression}; use oxc_span::SPAN; use oxc_syntax::reference::ReferenceFlags; @@ -109,13 +110,27 @@ impl<'a> TransformCtx<'a> { self.var_declarations.create_uid_var(&ident.name, ctx) } - // Reading `this` or `super` cannot have side effects, so no need for temp var - Expression::ThisExpression(this) => { - let references = create_array(|| ctx.ast.expression_this(this.span)); + // Reading any of these cannot have side effects, so no need for temp var + Expression::ThisExpression(_) + | Expression::Super(_) + | Expression::BooleanLiteral(_) + | Expression::NullLiteral(_) + | Expression::NumericLiteral(_) + | Expression::BigIntLiteral(_) + | Expression::RegExpLiteral(_) + | Expression::StringLiteral(_) => { + let references = create_array(|| expr.clone_in(ctx.ast.allocator)); return (expr, references); } - Expression::Super(super_expr) => { - let references = create_array(|| ctx.ast.expression_super(super_expr.span)); + // Template literal can have no side effects if it has no expressions + Expression::TemplateLiteral(lit) if lit.expressions.is_empty() => { + let references = create_array(|| { + ctx.ast.expression_template_literal( + lit.span, + lit.quasis.clone_in(ctx.ast.allocator), + ctx.ast.vec(), + ) + }); return (expr, references); } // Anything else requires temp var diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index 00c03b7933478..dfda5632274e6 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -42409,8 +42409,8 @@ rebuilt : ["a", "b", "c", "d", "e", "f", "g", "h", "i"] tasks/coverage/typescript/tests/cases/conformance/es2021/logicalAssignment/logicalAssignment2.ts semantic error: Bindings mismatch: -after transform: ScopeId(0): ["A", "_a$foo", "_a$foo$bar", "_b$foo", "_b$foo$bar", "_baz", "_baz2", "_baz3", "_c$baz", "_c$foo", "_c$foo$_baz", "_c$foo$bar", "_c$foo$bar$baz", "a", "b", "c", "result"] -rebuilt : ScopeId(0): ["_a$foo", "_a$foo$bar", "_b$foo", "_b$foo$bar", "_baz", "_baz2", "_baz3", "_c$baz", "_c$foo", "_c$foo$_baz", "_c$foo$bar", "_c$foo$bar$baz"] +after transform: ScopeId(0): ["A", "_a$foo", "_a$foo$bar", "_b$foo", "_b$foo$bar", "_c$baz", "_c$foo", "_c$foo$bar", "_c$foo$bar$baz", "_c$foo$baz", "a", "b", "c", "result"] +rebuilt : ScopeId(0): ["_a$foo", "_a$foo$bar", "_b$foo", "_b$foo$bar", "_c$baz", "_c$foo", "_c$foo$bar", "_c$foo$bar$baz", "_c$foo$baz"] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1)] rebuilt : ScopeId(0): [] diff --git a/tasks/transform_conformance/overrides/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing-without-other/output.js b/tasks/transform_conformance/overrides/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing-without-other/output.js new file mode 100644 index 0000000000000..b9be03c46d009 --- /dev/null +++ b/tasks/transform_conformance/overrides/babel-plugin-transform-logical-assignment-operators/test/fixtures/logical-assignment/null-coalescing-without-other/output.js @@ -0,0 +1,5 @@ +var _o, _o2; +let o; +o ?? (o = {}); +(_o = o).a ?? (_o.a = 1); +(_o2 = o)["b"] ?? (_o2["b"] = 2); diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index 9b3374feec861..13a74d86e731b 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 54a8389f -Passed: 99/111 +Passed: 102/114 # All Passed: * babel-plugin-transform-class-static-block diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/input.js new file mode 100644 index 0000000000000..4b53b708e9612 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/input.js @@ -0,0 +1,15 @@ +let boundObj; + +boundObj[true] &&= 1; +boundObj[null] &&= 2; +boundObj[123] &&= 3; +boundObj[123n] &&= 4; +boundObj[/abc/g] &&= 5; +boundObj["abc"] &&= 6; + +unboundObj[true] &&= 7; +unboundObj[null] &&= 8; +unboundObj[123] &&= 9; +unboundObj[123n] &&= 10; +unboundObj[/abc/g] &&= 11; +unboundObj["abc"] &&= 12; diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/output.js new file mode 100644 index 0000000000000..ba899a272fb42 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-literal/output.js @@ -0,0 +1,22 @@ +var _unboundObj, + _unboundObj2, + _unboundObj3, + _unboundObj4, + _unboundObj5, + _unboundObj6; + +let boundObj; + +boundObj[true] && (boundObj[true] = 1); +boundObj[null] && (boundObj[null] = 2); +boundObj[123] && (boundObj[123] = 3); +boundObj[123n] && (boundObj[123n] = 4); +boundObj[/abc/g] && (boundObj[/abc/g] = 5); +boundObj["abc"] && (boundObj["abc"] = 6); + +(_unboundObj = unboundObj)[true] && (_unboundObj[true] = 7); +(_unboundObj2 = unboundObj)[null] && (_unboundObj2[null] = 8); +(_unboundObj3 = unboundObj)[123] && (_unboundObj3[123] = 9); +(_unboundObj4 = unboundObj)[123n] && (_unboundObj4[123n] = 10); +(_unboundObj5 = unboundObj)[/abc/g] && (_unboundObj5[/abc/g] = 11); +(_unboundObj6 = unboundObj)["abc"] && (_unboundObj6["abc"] = 12); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/input.js new file mode 100644 index 0000000000000..42d116ad6b4b6 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/input.js @@ -0,0 +1,7 @@ +let boundObj; + +boundObj[`abc`] &&= 1; +unboundObj[`abc`] &&= 2; + +boundObj[`abc${foo}def`] &&= 3; +unboundObj[`abc${foo}def`] &&= 4; diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/output.js new file mode 100644 index 0000000000000..0bbabb7520930 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/computed-prop-template-literal/output.js @@ -0,0 +1,12 @@ +var _unboundObj, + _ref, + _unboundObj2, + _ref2; + +let boundObj; + +boundObj[`abc`] && (boundObj[`abc`] = 1); +(_unboundObj = unboundObj)[`abc`] && (_unboundObj[`abc`] = 2); + +boundObj[_ref = `abc${foo}def`] && (boundObj[_ref] = 3); +(_unboundObj2 = unboundObj)[_ref2 = `abc${foo}def`] && (_unboundObj2[_ref2] = 4); diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/input.js new file mode 100644 index 0000000000000..b68995d026eab --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/input.js @@ -0,0 +1,5 @@ +let boundProp; + +"abc".length &&= 1; +"abc"[boundProp] &&= 2; +"abc"[unboundProp] &&= 3; diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/output.js new file mode 100644 index 0000000000000..aeeafa7c6736f --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-logical-assignment-operators/test/fixtures/literal-member-expression/output.js @@ -0,0 +1,7 @@ +var _unboundProp; + +let boundProp; + +"abc".length && ("abc".length = 1); +"abc"[boundProp] && ("abc"[boundProp] = 2); +"abc"[_unboundProp = unboundProp] && ("abc"[_unboundProp] = 3);