diff --git a/crates/swc_ecma_minifier/src/compress/optimize/ops.rs b/crates/swc_ecma_minifier/src/compress/optimize/ops.rs index 69d0a1b05e27..f8cd64e343e7 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/ops.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/ops.rs @@ -42,7 +42,10 @@ impl Optimizer<'_> { } if e.op == op!("===") || e.op == op!("!==") { - if (e.left.is_ident() || e.left.is_member()) && e.left.eq_ignore_span(&e.right) { + if (e.left.is_ident() || e.left.is_member()) + && e.left.eq_ignore_span(&e.right) + && !contains_update_or_assign(&e.left) + { self.changed = true; report_change!("Reducing comparison of same variable ({})", e.op); @@ -288,3 +291,71 @@ impl Optimizer<'_> { } } } + +/// Check if an expression contains update expressions (++, --) or assignments +/// that would make duplicate evaluations produce different results. +fn contains_update_or_assign(expr: &Expr) -> bool { + match expr { + Expr::Update(..) | Expr::Assign(..) => true, + + Expr::Bin(BinExpr { left, right, .. }) => { + contains_update_or_assign(left) || contains_update_or_assign(right) + } + + Expr::Unary(UnaryExpr { arg, .. }) => contains_update_or_assign(arg), + + Expr::Cond(CondExpr { + test, cons, alt, .. + }) => { + contains_update_or_assign(test) + || contains_update_or_assign(cons) + || contains_update_or_assign(alt) + } + + Expr::Member(MemberExpr { obj, prop, .. }) => { + contains_update_or_assign(obj) + || match prop { + MemberProp::Computed(ComputedPropName { expr, .. }) => { + contains_update_or_assign(expr) + } + _ => false, + } + } + + Expr::Call(CallExpr { + callee: Callee::Expr(callee), + args, + .. + }) => { + contains_update_or_assign(callee) + || args.iter().any(|arg| contains_update_or_assign(&arg.expr)) + } + + Expr::Seq(SeqExpr { exprs, .. }) => { + exprs.iter().any(|expr| contains_update_or_assign(expr)) + } + + Expr::Paren(ParenExpr { expr, .. }) => contains_update_or_assign(expr), + + Expr::OptChain(OptChainExpr { base, .. }) => match &**base { + OptChainBase::Member(member) => { + contains_update_or_assign(&member.obj) + || match &member.prop { + MemberProp::Computed(ComputedPropName { expr, .. }) => { + contains_update_or_assign(expr) + } + _ => false, + } + } + OptChainBase::Call(call) => { + contains_update_or_assign(&call.callee) + || call + .args + .iter() + .any(|arg| contains_update_or_assign(&arg.expr)) + } + }, + + _ => false, + } +} diff --git a/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/config.json b/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/config.json new file mode 100644 index 000000000000..f48cfc8fc3d6 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/config.json @@ -0,0 +1,3 @@ +{ + "comparisons": true +} diff --git a/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/input.js b/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/input.js new file mode 100644 index 000000000000..93881315671a --- /dev/null +++ b/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/input.js @@ -0,0 +1,5 @@ +// Issue #11255: compress.comparisons should not optimize comparisons with side effects +let PC = 0; +const Stack = [0, '']; +const Code = [0, 0, 1]; +console.log(Stack[Code[++PC]] === Stack[Code[++PC]]); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/output.js b/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/output.js new file mode 100644 index 000000000000..93881315671a --- /dev/null +++ b/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/output.js @@ -0,0 +1,5 @@ +// Issue #11255: compress.comparisons should not optimize comparisons with side effects +let PC = 0; +const Stack = [0, '']; +const Code = [0, 0, 1]; +console.log(Stack[Code[++PC]] === Stack[Code[++PC]]); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/output.mangleOnly.js b/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/output.mangleOnly.js new file mode 100644 index 000000000000..1ab999809dd1 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/terser/compress/comparing/issue_11255_side_effects/output.mangleOnly.js @@ -0,0 +1,12 @@ +// Issue #11255: compress.comparisons should not optimize comparisons with side effects +let o = 0; +const c = [ + 0, + '' +]; +const l = [ + 0, + 0, + 1 +]; +console.log(c[l[++o]] === c[l[++o]]);