From f152ac9630417be2db375d880afc53a8b262555e Mon Sep 17 00:00:00 2001 From: austaras Date: Wed, 20 Apr 2022 16:53:33 +0800 Subject: [PATCH] empty branch and last break --- .../src/compress/optimize/switches.rs | 63 +++++++------------ .../tests/fixture/issues/2257/full/output.js | 41 +++++------- .../tests/projects/output/react-dom-17.0.2.js | 54 ++++++---------- .../compress/switch/issue_1083_4/output.js | 10 ++- .../compress/switch/issue_1083_5/output.js | 2 + .../compress/switch/issue_1083_6/output.js | 2 +- .../compress/switch/issue_1679/output.js | 2 +- .../compress/switch/issue_1680_2/output.js | 1 - .../compress/switch/issue_1690_2/output.js | 3 +- .../compress/switch/issue_1698/output.js | 3 +- 10 files changed, 74 insertions(+), 107 deletions(-) diff --git a/crates/swc_ecma_minifier/src/compress/optimize/switches.rs b/crates/swc_ecma_minifier/src/compress/optimize/switches.rs index 0d152cb03d4a..604df28aa848 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/switches.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/switches.rs @@ -165,58 +165,40 @@ where /// - drop break at last case /// - merge branch with default at the end pub(super) fn optimize_switch_cases(&mut self, cases: &mut Vec) { - if !self.options.switches || !self.options.dead_code { + if !self.options.switches || !self.options.dead_code || cases.is_empty() { return; } - // If default is not last, we can't remove empty cases. - let default_idx = cases.iter().position(|case| case.test.is_none()); - let all_ends_with_break = cases - .iter() - .all(|case| case.cons.is_empty() || case.cons.last().unwrap().is_break_stmt()); - let mut preserve_cases = false; - if !all_ends_with_break && default_idx.is_some() { - if let Some(last) = cases.last() { - if last.test.is_some() { - preserve_cases = true; - } - } - } - self.merge_cases_with_same_cons(cases); - let last_non_empty = cases.iter().rposition(|case| { - // We should preserve test cases if the test is not a literal. - match case.test.as_deref() { - Some(Expr::Lit(..)) | None => {} - _ => return true, - } + // last case with no empty body + let mut last = cases.len(); - if case.cons.is_empty() { - return false; - } + for (idx, case) in cases.iter_mut().enumerate().rev() { + self.changed |= remove_last_break(&mut case.cons); - if case.cons.len() == 1 { - if let Stmt::Break(BreakStmt { label: None, .. }) = case.cons[0] { - return false; - } + if !case.cons.is_empty() { + last = idx + 1; + break; } + } - true + let has_side_effect = cases.iter().skip(last).rposition(|case| { + case.test + .as_deref() + .map(|test| test.may_have_side_effects()) + .unwrap_or(false) }); - if !preserve_cases { - if let Some(last_non_empty) = last_non_empty { - if last_non_empty + 1 != cases.len() { - report_change!("switches: Removing empty cases at the end"); - self.changed = true; - cases.drain(last_non_empty + 1..); - } - } + if let Some(has_side_effect) = has_side_effect { + last += has_side_effect + 1 } - if let Some(last) = cases.last_mut() { - self.changed |= remove_last_break(&mut last.cons); + // if default is before empty cases, we must ensure empty case is preserved + if last < cases.len() && !cases.iter().take(last).any(|case| case.test.is_none()) { + self.changed = true; + report_change!("switches: Removing empty cases at the end"); + cases.drain(last..); } } @@ -247,6 +229,9 @@ where } for j in (i + 1)..len { + if cases[j].cons.is_empty() { + continue; + } if boundary[j] { // TODO: default break; diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js index 8bbdad444871..b7295450afd0 100644 --- a/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js +++ b/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js @@ -12519,6 +12519,7 @@ if (ie) return "compositionend" === a || !ae && ge(a, b) ? (a = nd(), md = ld = kd = null, ie = !1, a) : null; switch(a){ case "paste": + default: return null; case "keypress": if (!(b.ctrlKey || b.altKey || b.metaKey) || b.ctrlKey && b.altKey) { @@ -12528,8 +12529,6 @@ return null; case "compositionend": return de && "ko" !== b.locale ? null : b.data; - default: - return null; } }(a12, c5)) && 0 < (d = oe(d, "onBeforeInput")).length && (e = new Ld("onBeforeInput", "beforeinput", null, c5, e), g.push({ event: e, @@ -13240,7 +13239,6 @@ case 6: return null !== (b = "" === a.pendingProps || 3 !== b.nodeType ? null : b) && (a.stateNode = b, !0); case 13: - return !1; default: return !1; } @@ -14004,6 +14002,7 @@ case 14: return null; case 1: + case 17: return Ff(b.type) && Gf(), null; case 3: return fh(), H(N), H(M), uh(), (d = b.stateNode).pendingContext && (d.context = d.pendingContext, d.pendingContext = null), (null === a || null === a.child) && (rh(b) ? b.flags |= 4 : d.hydrate || (b.flags |= 256)), Ci(b), null; @@ -14163,8 +14162,6 @@ return fh(), Ci(b), null === a && cf(b.stateNode.containerInfo), null; case 10: return rg(b), null; - case 17: - return Ff(b.type) && Gf(), null; case 19: if (H(P), null === (d = b.memoizedState)) return null; if (f = 0 != (64 & b.flags), null === (g = d.rendering)) { @@ -14358,6 +14355,10 @@ case 11: case 15: case 22: + case 5: + case 6: + case 4: + case 17: return; case 1: if (256 & b.flags && null !== a) { @@ -14368,11 +14369,6 @@ case 3: 256 & b.flags && qf(b.stateNode.containerInfo); return; - case 5: - case 6: - case 4: - case 17: - return; } throw Error(y(163)); } @@ -14417,14 +14413,8 @@ a = c.stateNode, null === b && 4 & c.flags && mf(c.type, c.memoizedProps) && a.focus(); return; case 6: - return; case 4: - return; case 12: - return; - case 13: - null === c.memoizedState && null !== (c = c.alternate) && null !== (c = c.memoizedState) && null !== (c = c.dehydrated) && Cc(c); - return; case 19: case 17: case 20: @@ -14432,6 +14422,9 @@ case 23: case 24: return; + case 13: + null === c.memoizedState && null !== (c = c.alternate) && null !== (c = c.memoizedState) && null !== (c = c.dehydrated) && Cc(c); + return; } throw Error(y(163)); } @@ -14565,8 +14558,6 @@ f = !1; break a; case 3: - e = e.containerInfo, f = !0; - break a; case 4: e = e.containerInfo, f = !0; break a; @@ -14618,6 +14609,8 @@ } return; case 1: + case 12: + case 17: return; case 5: if (null != (c = b.stateNode)) { @@ -14650,16 +14643,12 @@ case 3: (c = b.stateNode).hydrate && (c.hydrate = !1, Cc(c.containerInfo)); return; - case 12: - return; case 13: null !== b.memoizedState && (jj = O(), aj(b.child, !0)), kj(b); return; case 19: kj(b); return; - case 17: - return; case 23: case 24: aj(b, null !== b.memoizedState); @@ -14800,6 +14789,9 @@ case 1: throw Error(y(345)); case 2: + case 5: + Uj(a); + break; case 3: if (Ii(a, c), (62914560 & c) === c && 10 < (d = jj + 500 - O())) { if (0 !== Uc(a, 0)) break; @@ -14824,9 +14816,6 @@ } Uj(a); break; - case 5: - Uj(a); - break; default: throw Error(y(329)); } @@ -15586,7 +15575,6 @@ case 7: return fi(a23, b, b.pendingProps, c), b.child; case 8: - return fi(a23, b, b.pendingProps.children, c), b.child; case 12: return fi(a23, b, b.pendingProps.children, c), b.child; case 10: @@ -15640,7 +15628,6 @@ case 19: return Ai(a23, b, c); case 23: - return mi(a23, b, c); case 24: return mi(a23, b, c); } diff --git a/crates/swc_ecma_minifier/tests/projects/output/react-dom-17.0.2.js b/crates/swc_ecma_minifier/tests/projects/output/react-dom-17.0.2.js index d6520790a5f5..82be1216c301 100644 --- a/crates/swc_ecma_minifier/tests/projects/output/react-dom-17.0.2.js +++ b/crates/swc_ecma_minifier/tests/projects/output/react-dom-17.0.2.js @@ -2380,6 +2380,9 @@ function findUpdateLane(lanePriority, wipLanes) { switch(lanePriority){ case 0: + case 6: + case 5: + break; case 15: return SyncLane; case 14: @@ -2395,9 +2398,6 @@ case 8: var _lane3 = pickArbitraryLane(3584 & ~wipLanes); return _lane3 === NoLane && (_lane3 = pickArbitraryLane(4186112 & ~wipLanes)) === NoLane && (_lane3 = pickArbitraryLane(3584)), _lane3; - case 6: - case 5: - break; case 2: var lane = pickArbitraryLane(805306368 & ~wipLanes); return lane === NoLane && (lane = pickArbitraryLane(805306368)), lane; @@ -3509,6 +3509,7 @@ } switch(domEventName){ case 'paste': + default: return null; case 'keypress': if (!(nativeEvent9 = nativeEvent).ctrlKey && !nativeEvent9.altKey && !nativeEvent9.metaKey || nativeEvent9.ctrlKey && nativeEvent9.altKey) { @@ -3518,8 +3519,6 @@ return null; case 'compositionend': return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data; - default: - return null; } }(domEventName10, nativeEvent8))) return null; var chars1, listeners = accumulateTwoPhaseListeners(targetInst, 'onBeforeInput'); @@ -5174,7 +5173,6 @@ if (null !== textInstance) return fiber.stateNode = textInstance, !0; return !1; case 13: - return !1; default: return !1; } @@ -6947,6 +6945,7 @@ case 14: return null; case 1: + case 17: return isContextProvider(workInProgress.type) && popContext(workInProgress), null; case 3: popHostContainer(workInProgress), popTopLevelContextObject(workInProgress), resetWorkInProgressVersions(); @@ -7197,8 +7196,6 @@ return popHostContainer(workInProgress), updateHostContainer(workInProgress), null === current && listenToAllSupportedEvents(workInProgress.stateNode.containerInfo), null; case 10: return popProvider(workInProgress), null; - case 17: - return isContextProvider(workInProgress.type) && popContext(workInProgress), null; case 19: popSuspenseContext(workInProgress); var renderState = workInProgress.memoizedState; @@ -7536,6 +7533,10 @@ case 11: case 15: case 22: + case 5: + case 6: + case 4: + case 17: return; case 1: if (256 & finishedWork.flags && null !== current) { @@ -7548,11 +7549,6 @@ case 3: 256 & finishedWork.flags && clearContainer(finishedWork.stateNode.containerInfo); return; - case 5: - case 6: - case 4: - case 17: - return; } throw Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue."); } @@ -7619,8 +7615,13 @@ } return; case 6: - return; case 4: + case 19: + case 17: + case 20: + case 21: + case 23: + case 24: return; case 12: var _finishedWork$memoize2 = finishedWork1.memoizedProps, onRender = (_finishedWork$memoize2.onCommit, _finishedWork$memoize2.onRender); @@ -7631,13 +7632,6 @@ case 13: commitSuspenseHydrationCallbacks(finishedRoot, finishedWork1); return; - case 19: - case 17: - case 20: - case 21: - case 23: - case 24: - return; } throw Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue."); } @@ -7710,9 +7704,7 @@ unmountHostComponents(finishedRoot, current); return; case 20: - return; case 18: - return; case 21: return; } @@ -7815,8 +7807,6 @@ currentParent = parentStateNode, currentParentIsContainer = !1; break findParent; case 3: - currentParent = parentStateNode.containerInfo, currentParentIsContainer = !0; - break findParent; case 4: currentParent = parentStateNode.containerInfo, currentParentIsContainer = !0; break findParent; @@ -7870,6 +7860,8 @@ }(3, finishedWork2); return; case 1: + case 12: + case 17: return; case 5: var instance = finishedWork2.stateNode; @@ -7905,16 +7897,12 @@ var _root = finishedWork2.stateNode; _root.hydrate && (_root.hydrate = !1, retryIfBlockedOn(_root.containerInfo)); return; - case 12: - return; case 13: commitSuspenseComponent(finishedWork2), attachSuspenseRetryListeners(finishedWork2); return; case 19: attachSuspenseRetryListeners(finishedWork2); return; - case 17: - return; case 20: case 21: break; @@ -8087,6 +8075,9 @@ case 1: throw Error("Root did not complete. This is a bug in React."); case 2: + case 5: + commitRoot(root4); + break; case 3: if (markRootSuspended$1(root4, lanes5), (62914560 & (lanes3 = lanes5)) === lanes3 && !(actingUpdatesScopeDepth > 0)) { var msUntilTimeout = globalMostRecentFallbackTime + 500 - now(); @@ -8120,9 +8111,6 @@ } commitRoot(root4); break; - case 5: - commitRoot(root4); - break; default: throw Error("Unknown root exit status."); } @@ -8881,8 +8869,6 @@ hostInstances.add(node.stateNode); return; case 4: - hostInstances.add(node.stateNode.containerInfo); - return; case 3: hostInstances.add(node.stateNode.containerInfo); return; diff --git a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_4/output.js b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_4/output.js index b892764257bf..fd37e94c34ea 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_4/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_4/output.js @@ -1,6 +1,12 @@ function test(definitely_true, maybe_true) { - if (true === maybe_true) console.log("maybe"); - else console.log("definitely"); + switch(true){ + case maybe_true: + console.log("maybe"); + break; + case definitely_true: + default: + console.log("definitely"); + } } test(true, false); test(true, true); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_5/output.js b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_5/output.js index 5ee4dcd329fe..7e280683d97b 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_5/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_5/output.js @@ -1,6 +1,8 @@ function test(definitely_true, maybe_true) { switch(true){ default: + console.log("definitely"); + break; case maybe_true: console.log("maybe"); break; diff --git a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_6/output.js b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_6/output.js index 8909a538a1ad..a1327ae6886d 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_6/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1083_6/output.js @@ -1,5 +1,5 @@ function test(definitely_true, maybe_true) { - switch (true) { + switch(true){ case definitely_true: console.log("definitely"); break; diff --git a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1679/output.js b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1679/output.js index 00e78d24bfcb..eefc6f02fd46 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1679/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1679/output.js @@ -3,9 +3,9 @@ function f() { switch(--b){ default: case false: + break; case b--: a--; - break; case a++: } } diff --git a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1680_2/output.js b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1680_2/output.js index 96c4cd443b11..3f19a534b3ef 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1680_2/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1680_2/output.js @@ -5,7 +5,6 @@ switch (b) { break; case b: var c; - break; case a: case a--: } diff --git a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1690_2/output.js b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1690_2/output.js index 5669eec47340..5507d9829d7d 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1690_2/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1690_2/output.js @@ -1 +1,2 @@ -console.log("PASS"); +switch(console.log("PASS")){ +} diff --git a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1698/output.js b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1698/output.js index 6d0620725948..890ec1aa3d8a 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1698/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/switch/issue_1698/output.js @@ -1,5 +1,6 @@ var a = 1; !function() { - a++; + switch(a++){ + } }(); console.log(a);