Skip to content

Commit f7ae61f

Browse files
committed
minify some switch statements to if-else statement
1 parent f1faf5d commit f7ae61f

3 files changed

Lines changed: 55 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
switch(x){case 0:foo();break;case 1:default:bar()}
2222

2323
// New output (with --minify)
24-
switch(x){case 0:foo();break;default:bar()}
24+
x===0?foo():bar();
2525
```
2626

2727
* Forbid `using` declarations inside `switch` clauses ([#4323](https://github.com/evanw/esbuild/issues/4323))

internal/js_parser/js_parser.go

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11625,26 +11625,59 @@ func (p *parser) minifySwitchStmt(loc logger.Loc, s *js_ast.SSwitch, stmts []js_
1162511625
}
1162611626
}
1162711627

11628-
// Handle switch statements with only one case remaining
11628+
// Handle a switch statement containing only a "default" clause
1162911629
if len(s.Cases) == 1 {
11630-
c := s.Cases[0]
11631-
var isTaken bool
11632-
var ok bool
11633-
if c.ValueOrNil.Data != nil {
11634-
// Non-default case
11635-
isTaken, ok = js_ast.CheckEqualityIfNoSideEffects(s.Test.Data, c.ValueOrNil.Data, js_ast.StrictEquality)
11636-
} else {
11637-
// Default case
11638-
isTaken, ok = true, p.astHelpers.ExprCanBeRemovedIfUnused(s.Test)
11639-
}
11640-
if ok && isTaken {
11630+
if c := s.Cases[0]; c.ValueOrNil.Data == nil && p.astHelpers.ExprCanBeRemovedIfUnused(s.Test) {
1164111631
if body, ok := tryToInlineCaseBody(s.BodyLoc, c.Body, s.CloseBraceLoc); ok {
11642-
// Inline the case body
1164311632
return append(stmts, body...)
1164411633
}
1164511634
}
1164611635
}
1164711636

11637+
// Try to turn this into an if-else statement
11638+
var yesCase js_ast.Case
11639+
var noCase js_ast.Case
11640+
if len(s.Cases) == 1 {
11641+
if yes := s.Cases[0]; yes.ValueOrNil.Data != nil {
11642+
yesCase = yes
11643+
}
11644+
} else if len(s.Cases) == 2 {
11645+
// "switch (x) { case y: a(); break; default: b() }"
11646+
if yes := s.Cases[0]; yes.ValueOrNil.Data != nil && !caseBodyCouldHaveFallThrough(yes.Body) {
11647+
if no := s.Cases[1]; no.ValueOrNil.Data == nil {
11648+
yesCase = yes
11649+
noCase = no
11650+
}
11651+
}
11652+
11653+
// "switch (x) { default: a(); break; case y: b() }"
11654+
if no := s.Cases[0]; no.ValueOrNil.Data == nil && !caseBodyCouldHaveFallThrough(no.Body) {
11655+
if yes := s.Cases[1]; yes.ValueOrNil.Data != nil {
11656+
yesCase = yes
11657+
noCase = no
11658+
}
11659+
}
11660+
}
11661+
if yesCase.ValueOrNil.Data != nil {
11662+
if yesBody, ok := tryToInlineCaseBody(s.BodyLoc, yesCase.Body, s.CloseBraceLoc); ok {
11663+
if noBody, ok := tryToInlineCaseBody(s.BodyLoc, noCase.Body, s.CloseBraceLoc); ok {
11664+
ifElse := js_ast.SIf{
11665+
Test: js_ast.Expr{Loc: s.Test.Loc},
11666+
Yes: stmtsToSingleStmt(yesCase.Loc, yesBody, logger.Loc{}),
11667+
}
11668+
if isEqualToTest, ok := js_ast.CheckEqualityIfNoSideEffects(s.Test.Data, yesCase.ValueOrNil.Data, js_ast.StrictEquality); ok {
11669+
ifElse.Test.Data = &js_ast.EBoolean{Value: isEqualToTest}
11670+
} else {
11671+
ifElse.Test.Data = &js_ast.EBinary{Op: js_ast.BinOpStrictEq, Left: s.Test, Right: yesCase.ValueOrNil}
11672+
}
11673+
if len(noBody) > 0 {
11674+
ifElse.NoOrNil = stmtsToSingleStmt(noCase.Loc, noBody, logger.Loc{})
11675+
}
11676+
return p.mangleIf(stmts, loc, &ifElse)
11677+
}
11678+
}
11679+
}
11680+
1164811681
return append(stmts, js_ast.Stmt{Loc: loc, Data: s})
1164911682
}
1165011683

internal/js_parser/js_parser_test.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,7 +1865,7 @@ func TestSuperCall(t *testing.T) {
18651865
expectPrintedMangleTarget(t, 2015, "class A extends B { x = 1; constructor() { super(); if (c) throw c } }",
18661866
"class A extends B {\n constructor() {\n super();\n __publicField(this, \"x\", 1);\n if (c) throw c;\n }\n}\n")
18671867
expectPrintedMangleTarget(t, 2015, "class A extends B { x = 1; constructor() { super(); switch (c) { case 0: throw c } } }",
1868-
"class A extends B {\n constructor() {\n super();\n __publicField(this, \"x\", 1);\n switch (c) {\n case 0:\n throw c;\n }\n }\n}\n")
1868+
"class A extends B {\n constructor() {\n super();\n __publicField(this, \"x\", 1);\n if (c === 0)\n throw c;\n }\n}\n")
18691869
expectPrintedMangleTarget(t, 2015, "class A extends B { x = 1; constructor() { super(); while (!c) throw c } }",
18701870
"class A extends B {\n constructor() {\n super();\n __publicField(this, \"x\", 1);\n for (; !c; ) throw c;\n }\n}\n")
18711871
expectPrintedMangleTarget(t, 2015, "class A extends B { x = 1; constructor() { super(); return c } }",
@@ -3664,21 +3664,23 @@ func TestMangleBlock(t *testing.T) {
36643664
}
36653665

36663666
func TestMangleSwitch(t *testing.T) {
3667-
expectPrintedMangle(t, "x(); switch (y) { case z: return w; }", "switch (x(), y) {\n case z:\n return w;\n}\n")
3668-
expectPrintedMangle(t, "if (t) { x(); switch (y) { case z: return w; } }", "if (t)\n switch (x(), y) {\n case z:\n return w;\n }\n")
3667+
expectPrintedMangle(t, "x(); switch (y) { case z: return w; }", "if (x(), y === z)\n return w;\n")
3668+
expectPrintedMangle(t, "if (t) { x(); switch (y) { case z: return w; } }", "if (t && (x(), y === z))\n return w;\n")
36693669

36703670
// We potentially need to keep let/const declarations in dead cases
36713671
expectPrintedMangle(t, "switch (1) { case 0: x; case 1: return x }", "return x;\n")
36723672
expectPrintedMangle(t, "switch (1) { case 0: var x; case 1: return x }", "switch (1) {\n case 0:\n var x;\n case 1:\n return x;\n}\n")
36733673
expectPrintedMangle(t, "switch (1) { case 0: let x; case 1: return x }", "switch (1) {\n case 0:\n let x;\n case 1:\n return x;\n}\n")
36743674
expectPrintedMangle(t, "switch (1) { case 0: const x = 0; case 1: return x }", "switch (1) {\n case 0:\n const x = 0;\n case 1:\n return x;\n}\n")
3675-
expectPrintedMangle(t, "switch (2) { case 0: var x; case 1: return x }", "switch (2) {\n case 0:\n var x;\n}\n")
3675+
expectPrintedMangle(t, "switch (2) { case 0: var x; case 1: return x }", "if (0)\n var x;\n")
36763676
expectPrintedMangle(t, "switch (2) { case 0: let x; case 1: return x }", "")
36773677
expectPrintedMangle(t, "switch (2) { case 0: const x = 0; case 1: return x }", "")
36783678

36793679
// https://github.com/evanw/esbuild/issues/4359
3680+
expectPrintedMangle(t, "switch (x) { case 0: a(); break; default: b() }", "x === 0 ? a() : b();\n")
3681+
expectPrintedMangle(t, "switch (x) { default: a(); break; case 0: b() }", "x === 0 ? b() : a();\n")
36803682
expectPrintedMangle(t, "switch (x) { case p: a(); break; case q: default: b() }", "switch (x) {\n case p:\n a();\n break;\n case q:\n default:\n b();\n}\n")
3681-
expectPrintedMangle(t, "switch (x) { case 0: a(); break; case 1: case 2: default: b() }", "switch (x) {\n case 0:\n a();\n break;\n default:\n b();\n}\n")
3683+
expectPrintedMangle(t, "switch (x) { case 0: a(); break; case 1: case 2: default: b() }", "x === 0 ? a() : b();\n")
36823684
expectPrintedMangle(t, "switch (x) { case 0: default: a(); break; case 0: b() }", "switch (x) {\n case 0:\n default:\n a();\n break;\n case 0:\n b();\n}\n")
36833685

36843686
// https://github.com/evanw/esbuild/issues/4176
@@ -5389,7 +5391,7 @@ func TestMangleInlineLocals(t *testing.T) {
53895391
check("let x = arg0; throw x;", "throw arg0;")
53905392
check("let x = arg0; return x;", "return arg0;")
53915393
check("let x = arg0; if (x) return 1;", "if (arg0) return 1;")
5392-
check("let x = arg0; switch (x) { case 0: return 1; }", "switch (arg0) {\n case 0:\n return 1;\n}")
5394+
check("let x = arg0; switch (x) { case 0: return 1; }", "if (arg0 === 0)\n return 1;")
53935395
check("let x = arg0; let y = x; return y + y;", "let y = arg0;\nreturn y + y;")
53945396

53955397
// Loops must not be substituted into because they evaluate multiple times

0 commit comments

Comments
 (0)