mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
feat(minifier): merge statements after terminating if block branches (#26667)
extend minification of exit-point to handle `if` consequent that are
block statements ending in a terminating statement.
```js
if (a()) { b(); return; } c();
// ^~~~~~~ ^~~~ move remaining stmts to else branch and remove jump stmt
if (a()) b(); else c();
// now we can collapse it to ternary if enabled
a() ? b() : c();
```
fixes #25489
This commit is contained in:
1 parent
8f61129126
commit
d1a0bcc21b
5 files changed
+65
-47
No files matched your search
@@ -582,9 +582,13 @@ impl<'a> PeepholeOptimizations {
|
||||
ctx.drop_statement(&previous.consequent);
|
||||
}
|
||||
|
||||
if if_stmt.consequent.is_jump_statement()
|
||||
&& Self::can_remove_termination_statement(&if_stmt.consequent, ctx)
|
||||
{
|
||||
let can_merge_with_alternate = match &if_stmt.consequent {
|
||||
Statement::BlockStatement(block) => block.body.last().is_some_and(|last_stmt| {
|
||||
Self::can_remove_termination_statement(last_stmt, ctx)
|
||||
}),
|
||||
stmt => Self::can_remove_termination_statement(stmt, ctx),
|
||||
};
|
||||
if can_merge_with_alternate {
|
||||
// Don't do this transformation if the branch condition could
|
||||
// potentially access symbols declared later on on this scope below.
|
||||
// If so, inverting the branch condition and nesting statements after
|
||||
@@ -609,20 +613,34 @@ impl<'a> PeepholeOptimizations {
|
||||
} else {
|
||||
ArenaVec::from_iter_in(drained_stmts, ctx)
|
||||
};
|
||||
|
||||
let span = if body.is_empty() { SPAN } else { body[0].span() };
|
||||
Self::minimize_statements(&mut body, ctx);
|
||||
let span =
|
||||
if body.is_empty() { if_stmt.consequent.span() } else { body[0].span() };
|
||||
ctx.replace_expression_with(&mut if_stmt.test, |test, ctx| {
|
||||
Self::minimize_not(test.span(), test, ctx, true)
|
||||
});
|
||||
let consequent = if body.len() == 1 {
|
||||
let alternate = if body.len() == 1 {
|
||||
body.remove(0)
|
||||
} else {
|
||||
let scope_id = ctx.create_child_scope_of_current(ScopeFlags::empty());
|
||||
Statement::new_block_statement_with_scope_id(span, body, scope_id, ctx)
|
||||
};
|
||||
ctx.replace_statement(&mut if_stmt.consequent, consequent);
|
||||
|
||||
if let Statement::BlockStatement(block) = &mut if_stmt.consequent {
|
||||
ctx.drop_statement(&block.body.pop().unwrap());
|
||||
// after removal check if there is one remaining stmt and if it requires block
|
||||
// IfStatement is excluded as we would add it afterward in `try_minimize_if`
|
||||
if block.body.len() == 1
|
||||
&& !matches!(&block.body[0], Statement::IfStatement(_))
|
||||
&& !Self::statement_cares_about_scope(&block.body[0])
|
||||
{
|
||||
let new_stmt = block.body.remove(0);
|
||||
ctx.replace_statement(&mut if_stmt.consequent, new_stmt);
|
||||
}
|
||||
if_stmt.alternate = Some(alternate);
|
||||
} else {
|
||||
ctx.replace_expression_with(&mut if_stmt.test, |test, ctx| {
|
||||
Self::minimize_not(test.span(), test, ctx, true)
|
||||
});
|
||||
ctx.replace_statement(&mut if_stmt.consequent, alternate);
|
||||
}
|
||||
|
||||
let mut if_stmt = Statement::IfStatement(if_stmt);
|
||||
Self::try_minimize_if(&mut if_stmt, ctx);
|
||||
ctx.notice_change();
|
||||
|
||||
@@ -211,7 +211,7 @@ fn js_parser_test() {
|
||||
test("while (x) { debugger; if (1) { if (1) continue; z() } }", "for (; x; ) debugger;");
|
||||
// test("while (x()) continue", "for (; x(); ) ;");
|
||||
test("while (x) { y(); continue }", "for (; x; ) y();");
|
||||
test("while (x) { if (y) { z(); continue } }", "for (; x; ) if (y) { z(); continue; }");
|
||||
test("while (x) { if (y) { z(); continue } }", "for (; x;) y && z();");
|
||||
test(
|
||||
"label: while (x) while (y) { z(); continue label }",
|
||||
"label: for (; x; ) for (; y; ) { z(); continue label;}",
|
||||
|
||||
@@ -62,7 +62,7 @@ fn test_function_return_optimization() {
|
||||
test("function f(){if(x)return; x=3; return; }", "function f(){ x||=3;}");
|
||||
test("function f(){if(true){a();return;}else;b();}", "function f(){a();}");
|
||||
test("function f(){if(false){a();return;}else;b();return;}", "function f(){b();}");
|
||||
test("function f(){if(a()){b();return;}else;c();}", "function f(){if(a()){b();return}c()}"); // function f(){a()?b():c()}
|
||||
test("function f(){if(a()){b();return;}else;c();}", "function f(){a()?b():c()}");
|
||||
test(
|
||||
"function f(){if(a()){b()}else{c();return;}}",
|
||||
"function f(){if(a())b();else{c();return}}",
|
||||
@@ -73,13 +73,13 @@ fn test_function_return_optimization() {
|
||||
);
|
||||
test(
|
||||
"function f(){if(a()){if(b()){d();return;}else{return;}}else{return;} c();}",
|
||||
"function f(){if(a()){if(b()){d();return}return}}",
|
||||
); // function f(){a()&&b()&&d()}
|
||||
"function f(){a()&&b()&&d()}",
|
||||
);
|
||||
test(
|
||||
"function f(a,b,c){if(a){}else if(b){x();return}else if(c){y();return}z()}",
|
||||
"function f(a,b,c){if(!a){if(b){x();return}if(c){y();return}}z()}",
|
||||
);
|
||||
test("function f(){if(a()){b();return;}else;}", "function f(){if(a()){b();return}}"); // function f(){a()&&b()}
|
||||
test("function f(){if(a()){b();return;}else;}", "function f(){a()&&b()}");
|
||||
test("function f(){if(a()){return;}else{return;} return;}", "function f(){a();}");
|
||||
test("function f(){if(a()){return;}else{return;} b();}", "function f(){a()}");
|
||||
test(
|
||||
@@ -170,15 +170,15 @@ fn test_while_continue_optimization() {
|
||||
test("while(true){a();continue;b();}", "for(;;)a();");
|
||||
test("while(true){if(true){a();continue;}else;b();}", "for(;;)a();");
|
||||
test("while(true){if(false){a();continue;}else;b();continue;}", "for(;;)b();");
|
||||
test("while(true){if(a()){b();continue;}else;c();}", "for(;;){if(a()){b();continue}c()}"); // for(;;)a()?b():c();
|
||||
test("while(true){if(a()){b();continue;}else;c();}", "for(;;)a()?b():c()");
|
||||
test("while(true){if(a()){b();}else{c();continue;}}", "for(;;)if(a())b();else{c();continue}"); // for(;;)a()?b():c();
|
||||
test("while(true){if(a()){b();continue;}else;}", "for (;;) if(a()){b();continue;}"); // for(;;)a()&&b();
|
||||
test("while(true){if(a()){b();continue;}else;}", "for(;;)a()&&b()");
|
||||
test("while(true){if(a()){continue;}else{continue;} continue;}", "for(;;)a();");
|
||||
test("while(true){if(a()){continue;}else{continue;} b();}", "for(;;)a();");
|
||||
test(
|
||||
"while(true){d();if(a()){continue;}else if(b()){c();continue;}else{continue;}}",
|
||||
"for(;;)if(d(),!a()&&b()){c();continue}",
|
||||
); // for(;;)d(),!a()&&b()&&c();
|
||||
"for(;;)d(),!a()&&b()&&c()",
|
||||
);
|
||||
|
||||
test("while(true)while(a())continue;", "for(;;)for(;a();)continue;"); // for(;;)for(;a(););
|
||||
test("while(true)for(x in a())continue", "for(;;)for(x in a())continue;"); // for(;;)for(x in a());
|
||||
@@ -215,12 +215,12 @@ fn test_do_continue_optimization() {
|
||||
test("do{a();continue;b()}while(true)", "do a();while(1)");
|
||||
test("do{if(true){a();continue;}else;b();}while(true)", "do a();while(1)");
|
||||
test("do{if(false){a();continue;}else;b();continue;}while(true)", "do b();while(1)");
|
||||
test("do{if(a()){b();continue;}else;c();}while(true)", "do{if(a()){b();continue}c()}while(1);"); // do a()?b():c();while(1)
|
||||
test("do{if(a()){b();continue;}else;c();}while(true)", "do a()?b():c();while(1)");
|
||||
test(
|
||||
"do{if(a()){b();}else{c();continue;}}while(true)",
|
||||
"do if(a())b();else{c();continue}while(1);",
|
||||
); // do a()?b():c();while(1)
|
||||
test("do{if(a()){b();continue;}else;}while(true)", "do if(a()){b();continue}while(1);"); // do a()&&b();while(1)
|
||||
test("do{if(a()){b();continue;}else;}while(true)", "do a()&&b();while(1)");
|
||||
test("do{if(a()){continue;}else{continue;} continue;}while(true)", "do a();while(1)");
|
||||
test("do{if(a()){continue;}else{continue;} b();}while(true)", "do a();while(1)");
|
||||
|
||||
@@ -265,7 +265,7 @@ fn test_for_continue_optimization() {
|
||||
test("for(x in y){a();continue;b()}", "for(x in y)a()");
|
||||
test("for(x in y){if(true){a();continue;}else;b();}", "for(x in y)a()");
|
||||
test("for(x in y){if(false){a();continue;}else;b();continue;}", "for(x in y)b()");
|
||||
test("for(x in y){if(a()){b();continue;}else;c();}", "for(x in y){if(a()){b();continue}c()}"); // for(x in y)a()?b():c()
|
||||
test("for(x in y){if(a()){b();continue;}else;c();}", "for(x in y)a()?b():c()"); // for(x in y)a()?b():c()
|
||||
test(
|
||||
"for(x in y){if(a()){b();}else{c();continue;}}",
|
||||
"for(x in y)if(a())b();else{c();continue}",
|
||||
@@ -275,7 +275,7 @@ fn test_for_continue_optimization() {
|
||||
test("for(x of y){a();continue;b()}", "for(x of y)a()");
|
||||
test("for(x of y){if(true){a();continue;}else;b();}", "for(x of y)a()");
|
||||
test("for(x of y){if(false){a();continue;}else;b();continue;}", "for(x of y)b()");
|
||||
test("for(x of y){if(a()){b();continue;}else;c();}", "for(x of y){if(a()){b();continue}c()}"); // for(x of y)a()?b():c()
|
||||
test("for(x of y){if(a()){b();continue;}else;c();}", "for(x of y)a()?b():c()");
|
||||
test(
|
||||
"for(x of y){if(a()){b();}else{c();continue;}}",
|
||||
"for(x of y)if(a())b();else{c();continue}",
|
||||
@@ -299,14 +299,14 @@ fn test_for_continue_optimization() {
|
||||
);
|
||||
test(
|
||||
"r=async () => { for await (x of y){if(a()){b();continue;}else;c();}}",
|
||||
"r=async () => { for await (x of y){if(a()){b();continue}c()}};",
|
||||
); // r=async () => { for await(x of y)a()?b():c()};
|
||||
"r=async () => { for await(x of y)a()?b():c()}",
|
||||
);
|
||||
test(
|
||||
"r=async () => { for await (x of y){if(a()){b();}else{c();continue;}}}",
|
||||
"r=async() => { for await(x of y)if(a())b();else{c();continue}};",
|
||||
"r=async() => { for await(x of y)if(a())b();else{c();continue}}",
|
||||
); // r=async () => { for await (x of y) a() ? b() : c() };
|
||||
|
||||
test("for(x=0;x<y;x++){if(a()){b();continue;}else;}", "for(x=0;x<y;x++)if(a()){b();continue}"); // for(x=0;x<y;x++)a()&&b()
|
||||
test("for(x=0;x<y;x++){if(a()){b();continue;}else;}", "for(x=0;x<y;x++)a()&&b()");
|
||||
test("for(x=0;x<y;x++){if(a()){continue;}else{continue;} continue;}", "for(x=0;x<y;x++)a()");
|
||||
test("for(x=0;x<y;x++){if(a()){continue;}else{continue;} b();}", "for(x=0;x<y;x++)a();");
|
||||
|
||||
|
||||
+11
-11
@@ -3,25 +3,25 @@ Original | minified | minified | gzip | gzip | Iterations | Fi
|
||||
-------------------------------------------------------------------------------------
|
||||
72.14 kB | 22.48 kB | 23.70 kB | 8.19 kB | 8.54 kB | 2 | react.development.js
|
||||
|
||||
173.90 kB | 55.50 kB | 59.82 kB | 18.77 kB | 19.33 kB | 2 | moment.js
|
||||
173.90 kB | 55.49 kB | 59.82 kB | 18.78 kB | 19.33 kB | 2 | moment.js
|
||||
|
||||
287.63 kB | 89.01 kB | 90.07 kB | 30.86 kB | 31.95 kB | 2 | jquery.js
|
||||
287.63 kB | 88.98 kB | 90.07 kB | 30.85 kB | 31.95 kB | 2 | jquery.js
|
||||
|
||||
342.15 kB | 114.80 kB | 118.14 kB | 42.70 kB | 44.37 kB | 2 | vue.js
|
||||
342.15 kB | 114.76 kB | 118.14 kB | 42.69 kB | 44.37 kB | 2 | vue.js
|
||||
|
||||
544.10 kB | 69.88 kB | 72.48 kB | 25.58 kB | 26.20 kB | 2 | lodash.js
|
||||
544.10 kB | 69.87 kB | 72.48 kB | 25.58 kB | 26.20 kB | 2 | lodash.js
|
||||
|
||||
555.77 kB | 262.75 kB | 270.13 kB | 87.66 kB | 90.80 kB | 2 | d3.js
|
||||
555.77 kB | 262.73 kB | 270.13 kB | 87.66 kB | 90.80 kB | 2 | d3.js
|
||||
|
||||
1.01 MB | 435.99 kB | 458.89 kB | 121.78 kB | 126.71 kB | 2 | bundle.min.js
|
||||
1.01 MB | 435.91 kB | 458.89 kB | 121.78 kB | 126.71 kB | 2 | bundle.min.js
|
||||
|
||||
1.25 MB | 634.38 kB | 646.76 kB | 158.43 kB | 163.73 kB | 2 | three.js
|
||||
1.25 MB | 634.24 kB | 646.76 kB | 158.44 kB | 163.73 kB | 2 | three.js
|
||||
|
||||
2.14 MB | 707.63 kB | 724.14 kB | 160.08 kB | 181.07 kB | 2 | victory.js
|
||||
2.14 MB | 707.61 kB | 724.14 kB | 160.07 kB | 181.07 kB | 2 | victory.js
|
||||
|
||||
3.20 MB | 958.82 kB | 1.01 MB | 317.06 kB | 331.56 kB | 2 | echarts.js
|
||||
3.20 MB | 958.47 kB | 1.01 MB | 317.02 kB | 331.56 kB | 2 | echarts.js
|
||||
|
||||
6.69 MB | 2.12 MB | 2.31 MB | 453.41 kB | 488.28 kB | 4 | antd.js
|
||||
6.69 MB | 2.12 MB | 2.31 MB | 453.40 kB | 488.28 kB | 4 | antd.js
|
||||
|
||||
10.95 MB | 3.31 MB | 3.49 MB | 849.97 kB | 915.50 kB | 4 | typescript.js
|
||||
10.95 MB | 3.31 MB | 3.49 MB | 849.87 kB | 915.50 kB | 4 | typescript.js
|
||||
|
||||
@@ -5,7 +5,7 @@ checker.ts:
|
||||
sys deallocs: 122
|
||||
sys alloc bytes: 14996287 # 15.00 MB
|
||||
sys peak growth: 10169560 # 10.17 MB
|
||||
arena allocs: 56201
|
||||
arena allocs: 56225
|
||||
arena reallocs: 5084
|
||||
arena size: 16317568 # 16.32 MB
|
||||
|
||||
@@ -16,7 +16,7 @@ App.tsx:
|
||||
sys deallocs: 61
|
||||
sys alloc bytes: 2128539 # 2.13 MB
|
||||
sys peak growth: 1612427 # 1.61 MB
|
||||
arena allocs: 7449
|
||||
arena allocs: 7474
|
||||
arena reallocs: 920
|
||||
arena size: 2683792 # 2.68 MB
|
||||
|
||||
@@ -38,9 +38,9 @@ pdf.mjs:
|
||||
sys deallocs: 2427
|
||||
sys alloc bytes: 5313795 # 5.31 MB
|
||||
sys peak growth: 3693355 # 3.69 MB
|
||||
arena allocs: 19495
|
||||
arena allocs: 19590
|
||||
arena reallocs: 2574
|
||||
arena size: 5708304 # 5.71 MB
|
||||
arena size: 5710976 # 5.71 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
@@ -49,7 +49,7 @@ antd.js:
|
||||
sys deallocs: 860
|
||||
sys alloc bytes: 29973448 # 29.97 MB
|
||||
sys peak growth: 19934725 # 19.93 MB
|
||||
arena allocs: 181457
|
||||
arena allocs: 181540
|
||||
arena reallocs: 34406
|
||||
arena size: 43039192 # 43.04 MB
|
||||
|
||||
@@ -60,7 +60,7 @@ binder.ts:
|
||||
sys deallocs: 33
|
||||
sys alloc bytes: 963008 # 963.01 kB
|
||||
sys peak growth: 657404 # 657.40 kB
|
||||
arena allocs: 2919
|
||||
arena allocs: 2922
|
||||
arena reallocs: 244
|
||||
arena size: 1074912 # 1.07 MB
|
||||
|
||||
@@ -71,6 +71,6 @@ kitchen-sink.tsx:
|
||||
sys deallocs: 1854
|
||||
sys alloc bytes: 5304252 # 5.30 MB
|
||||
sys peak growth: 3707425 # 3.71 MB
|
||||
arena allocs: 31107
|
||||
arena allocs: 31037
|
||||
arena reallocs: 3012
|
||||
arena size: 8443456 # 8.44 MB
|
||||
arena size: 8439864 # 8.44 MB
|
||||
Reference in new issue
Block a user