mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
feat(minifier): invert !0 and !1 in place for boolean context to 1 and 0 (#26050)
improve negation of 1 and 0 to 0 and 1 in boolean context previously only `!!0` and `!!1` could be inlined to `1` and `0` and now `!0` and `!1` will also be inlined, this change allows us to flip numeric values in-place instead of having to first to create unary and remove it afterwards ``` !0 was converted before to 1 in some cases: 1. negate 1 2. !1 - add unary 3. 0 - remove unary now: 1. negate 1 2. 0 ```
This commit is contained in:
1 parent
1e902cc1ad
commit
784e9faad2
8 files changed
+50
-40
No files matched your search
@@ -46,6 +46,12 @@ impl<'a> PeepholeOptimizations {
|
||||
binary_expr.operator = binary_expr.operator.equality_inverse_operator().unwrap();
|
||||
true
|
||||
}
|
||||
// `!0` => `1`
|
||||
// `!1` => `0`
|
||||
Expression::NumericLiteral(num) if boolean_context => {
|
||||
num.value = if num.value.is_nan() || num.value == 0.0 { 1.0 } else { 0.0 };
|
||||
true
|
||||
}
|
||||
// `!(a == b || c == d)` => `a != b && c != d`
|
||||
// `!(a == b && c == d)` => `a != b || c != d`
|
||||
// De Morgan's law, only when every comparison in the `&&`/`||` chain
|
||||
|
||||
@@ -1059,9 +1059,10 @@ fn test_remove_dead_expr_nullish_related() {
|
||||
fn test_mangle_boolean_with_side_effects() {
|
||||
let falsy_no_side_effects = ["!1", "\"\"", "0", "0n", "null", "void 0"];
|
||||
for value in falsy_no_side_effects {
|
||||
let negated = if value == "!1" { "0" } else { value };
|
||||
test(&format!("y(x && {value})"), &format!("y(x && {value});"));
|
||||
test(&format!("y(x || {value})"), &format!("y(x || {value});"));
|
||||
test(&format!("y(!(x && {value}))"), &format!("y(!(x && {value}));"));
|
||||
test(&format!("y(!(x && {value}))"), &format!("y(!(x && {negated}));"));
|
||||
test(&format!("y(!(x || {value}))"), "y(!x);");
|
||||
test(&format!("if (x && {value}) y"), "x;");
|
||||
test(&format!("if (x || {value}) y"), "x && y;");
|
||||
@@ -1076,10 +1077,11 @@ fn test_mangle_boolean_with_side_effects() {
|
||||
let truthy_no_side_effects =
|
||||
["!0", "\" \"", "1", "1n", "/./", "(() => {\n})", "function() {\n}", "[1, 2]", "{ a: 0 }"];
|
||||
for value in truthy_no_side_effects {
|
||||
let negated = if value == "!0" { "1" } else { value };
|
||||
test(&format!("y(x && {value})"), &format!("y(x && {value});"));
|
||||
test(&format!("y(x || {value})"), &format!("y(x || {value});"));
|
||||
test(&format!("y(!(x && {value}))"), "y(!x);");
|
||||
test(&format!("y(!(x || {value}))"), &format!("y(!(x || {value}));"));
|
||||
test(&format!("y(!(x || {value}))"), &format!("y(!(x || {negated}));"));
|
||||
test(&format!("if (x && {value}) y"), "x && y;");
|
||||
test(&format!("if (x || {value}) y"), "x, y;");
|
||||
test(&format!("if (x && {value}) y; else z"), "x ? y : z;");
|
||||
|
||||
@@ -197,54 +197,51 @@ fn test_while_continue_optimization() {
|
||||
|
||||
#[test]
|
||||
fn test_do_continue_optimization() {
|
||||
test("do{if(x)continue; x=3; continue; }while(true)", "do x||=3;while(!0);");
|
||||
test("do{a();continue;b()}while(true)", "do a();while(!0)");
|
||||
test("do{if(true){a();continue;}else;b();}while(true)", "do a();while(!0)");
|
||||
test("do{if(false){a();continue;}else;b();continue;}while(true)", "do b();while(!0)");
|
||||
test(
|
||||
"do{if(a()){b();continue;}else;c();}while(true)",
|
||||
"do{if(a()){b();continue}c()}while(!0);",
|
||||
); // do a()?b():c();while(!0)
|
||||
test("do{if(x)continue; x=3; continue; }while(true)", "do x||=3;while(1);");
|
||||
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();}else{c();continue;}}while(true)",
|
||||
"do if(a())b();else{c();continue}while(!0);",
|
||||
); // do a()?b():c();while(!0)
|
||||
test("do{if(a()){b();continue;}else;}while(true)", "do if(a()){b();continue}while(!0);"); // do a()&&b();while(!0)
|
||||
test("do{if(a()){continue;}else{continue;} continue;}while(true)", "do a();while(!0)");
|
||||
test("do{if(a()){continue;}else{continue;} b();}while(true)", "do a();while(!0)");
|
||||
"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()){continue;}else{continue;} continue;}while(true)", "do a();while(1)");
|
||||
test("do{if(a()){continue;}else{continue;} b();}while(true)", "do a();while(1)");
|
||||
|
||||
test("do{while(a())continue;}while(true)", "do for(;a();)continue;while(!0);"); // do for(;a(););while(!0)
|
||||
test("do{for(x in a())continue}while(true)", "do for(x in a())continue;while(!0);"); // do for(x in a());while(!0)
|
||||
test("do{while(a())continue;}while(true)", "do for(;a();)continue;while(1);"); // do for(;a(););while(1)
|
||||
test("do{for(x in a())continue}while(true)", "do for(x in a())continue;while(1);"); // do for(x in a());while(1)
|
||||
|
||||
test("do{while(a())break;}while(true)", "do for(;a();)break;while(!0)");
|
||||
test("do for(x in a())break;while(true)", "do for(x in a())break;while(!0)");
|
||||
test("do{while(a())break;}while(true)", "do for(;a();)break;while(1)");
|
||||
test("do for(x in a())break;while(true)", "do for(x in a())break;while(1)");
|
||||
|
||||
test(
|
||||
"do{try{continue;}catch(e){continue;}}while(true)",
|
||||
"do try{continue}catch{continue}while(!0);",
|
||||
); // do;while(!0)
|
||||
"do try{continue}catch{continue}while(1);",
|
||||
); // do;while(1)
|
||||
test(
|
||||
"do{try{if(a()){continue;}else{continue;} continue;}catch(e){}}while(true)",
|
||||
"do try{if(a())continue;continue}catch{}while(!0);",
|
||||
); // do try{a()}catch{}while(!0);
|
||||
"do try{if(a())continue;continue}catch{}while(1);",
|
||||
); // do try{a()}catch{}while(1);
|
||||
|
||||
test("do{g:continue}while(true)", "do g:continue;while(!0);"); // do;while(!0);
|
||||
test("do{g:continue}while(true)", "do g:continue;while(1);"); // do;while(1);
|
||||
// This case could be improved.
|
||||
test(
|
||||
"do{g:if(a()){continue;}else{continue;} continue;}while(true)",
|
||||
"do g:if(a())continue;else continue;while(!0);",
|
||||
); // do g:a();while(!0);
|
||||
"do g:if(a())continue;else continue;while(1);",
|
||||
); // do g:a();while(1);
|
||||
|
||||
test("do { foo(); continue; } while(false)", "do foo();while(!1)");
|
||||
test("do { foo(); break; } while(false)", "do foo();while(!1)");
|
||||
test("do { foo(); continue; } while(false)", "do foo();while(0)");
|
||||
test("do { foo(); break; } while(false)", "do foo();while(0)");
|
||||
|
||||
test("do{break}while(fn());", "do break; while(fn());");
|
||||
test("do{break}while(true);", "do break; while(!0);"); // do while(!1);
|
||||
test("do{break}while(!new Date());", "do;while(!1);");
|
||||
test("do{break}while(true);", "do break; while(1);"); // do while(0);
|
||||
test("do{break}while(!new Date());", "do;while(0);");
|
||||
|
||||
test(
|
||||
"do { foo(); switch (x) { case 1: break; default: f()} } while(false)",
|
||||
"do switch (foo(),x) { case 1: break; default: f() } while(!1);",
|
||||
"do switch (foo(),x) { case 1: break; default: f() } while(0);",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,11 @@ fn minimize_duplicate_nots() {
|
||||
test_same("function k () { return !!x; }");
|
||||
test("var k = () => { return !!x; }", "var k = () => !!x");
|
||||
test_same("var k = () => !!x;");
|
||||
// Negation of a `1`, `0` is boolean context.
|
||||
test("var v = !(!0);", "var v = !1;");
|
||||
test("var v = !(!1);", "var v = !0;");
|
||||
test("var v = !(a || !0);", "var v = !(a || 1);");
|
||||
test("var v = !(a && !1);", "var v = !(a && 0);");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -285,8 +285,8 @@ fn test_loop_optimization_edge_cases() {
|
||||
test("while (true) { infiniteLoop(); }", "for (;;) infiniteLoop();"); // optimized loop form
|
||||
|
||||
// Test do-while loops - false becomes !1, braces may be removed, true becomes !0
|
||||
test("do { executedOnce(); } while (false);", "do\n\texecutedOnce();\nwhile (!1);");
|
||||
test("do { body(); } while (true);", "do\n\tbody();\nwhile (!0);");
|
||||
test("do { executedOnce(); } while (false);", "do\n\texecutedOnce();\nwhile (0);");
|
||||
test("do { body(); } while (true);", "do\n\tbody();\nwhile (1);");
|
||||
|
||||
// Test for loops with analyzable bounds
|
||||
test(
|
||||
|
||||
@@ -38,7 +38,7 @@ fn test_fold_block() {
|
||||
// test("for (x of y) {x}", "for(x of y);");
|
||||
test("for (let x = 1; x <10; x++ ) {}", "for (let x = 1; x <10; x++ );");
|
||||
test("for (var x = 1; x <10; x++ ) {}", "for (var x = 1; x <10; x++ );");
|
||||
test("do { } while (true)", "do;while(!0)");
|
||||
test("do { } while (true)", "do;while(1)");
|
||||
test(
|
||||
"function z(a) {
|
||||
{
|
||||
@@ -85,7 +85,7 @@ fn test_fold_useless_for() {
|
||||
test("for (var se = [1, 2]; false;);", "var se = [1, 2];");
|
||||
test("for (var se = [1, 2]; false;) { var a = 0; }", "var se = [1, 2], a;");
|
||||
|
||||
test("for (foo = bar; false;) {}", "for (foo = bar; !1;);");
|
||||
test("for (foo = bar; false;) {}", "for (foo = bar; 0;);");
|
||||
// test("l1:for(;false;) { }", "");
|
||||
}
|
||||
|
||||
|
||||
@@ -866,7 +866,7 @@ fn keep_script_root_var_in_nested_statement_after_cycle_removed() {
|
||||
let source = "function outer() { function d1() { return x + d2() } function d2() { return d1() } return 1 } outer(); switch (1) { case 1: var x = 42; }";
|
||||
// Script globals can be rebound through global-object properties without a
|
||||
// resolved write reference, so the call cannot reuse a pure summary.
|
||||
let expected = "function outer() { return 1 } if (outer(), !0) var x = 42;";
|
||||
let expected = "function outer() { return 1 } if (outer(), 1) var x = 42;";
|
||||
test_options_source_type(source, expected, SourceType::script(), &options);
|
||||
|
||||
// CommonJS top-level vars are wrapper-local, so ordinary counts may remove them.
|
||||
|
||||
@@ -16,7 +16,7 @@ App.tsx:
|
||||
sys deallocs: 61
|
||||
sys alloc bytes: 2128555 # 2.13 MB
|
||||
sys peak growth: 1612427 # 1.61 MB
|
||||
arena allocs: 15676
|
||||
arena allocs: 15675
|
||||
arena reallocs: 2721
|
||||
arena size: 2880752 # 2.88 MB
|
||||
|
||||
@@ -49,9 +49,9 @@ antd.js:
|
||||
sys deallocs: 1038
|
||||
sys alloc bytes: 29976031 # 29.98 MB
|
||||
sys peak growth: 19934725 # 19.93 MB
|
||||
arena allocs: 351701
|
||||
arena allocs: 351645
|
||||
arena reallocs: 76248
|
||||
arena size: 48772472 # 48.77 MB
|
||||
arena size: 48770232 # 48.77 MB
|
||||
|
||||
binder.ts:
|
||||
file size: 193077 # 193.08 kB
|
||||
@@ -71,6 +71,6 @@ kitchen-sink.tsx:
|
||||
sys deallocs: 1854
|
||||
sys alloc bytes: 5307512 # 5.31 MB
|
||||
sys peak growth: 3707425 # 3.71 MB
|
||||
arena allocs: 64963
|
||||
arena allocs: 64956
|
||||
arena reallocs: 12252
|
||||
arena size: 9393120 # 9.39 MB
|
||||
Reference in new issue
Block a user