From 712cae034c873af69c8a5e311892678bc7f44423 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Mon, 20 Jan 2025 08:11:11 +0000 Subject: [PATCH] refactor(minifier): run the compressor on all test cases (#8604) Running individual plugins causes too much confusion. - [x] fix 110 failed tests :-) --- .../collapse_variable_declarations.rs | 49 +- .../convert_to_dotted_properties.rs | 30 +- .../src/ast_passes/exploit_assigns.rs | 14 +- .../src/ast_passes/minimize_exit_points.rs | 275 +++-- .../oxc_minifier/src/ast_passes/normalize.rs | 25 +- .../src/ast_passes/peephole_fold_constants.rs | 736 +++++++------- .../peephole_minimize_conditions.rs | 732 +++++++------- .../ast_passes/peephole_remove_dead_code.rs | 251 +++-- .../peephole_replace_known_methods.rs | 936 +++++++++--------- .../peephole_substitute_alternate_syntax.rs | 103 +- .../src/ast_passes/remove_unused_code.rs | 15 +- .../src/ast_passes/statement_fusion.rs | 128 +-- crates/oxc_minifier/src/tester.rs | 52 +- 13 files changed, 1576 insertions(+), 1770 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs b/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs index 65ccc811f346b..2a76966dade95 100644 --- a/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs +++ b/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs @@ -235,19 +235,7 @@ impl<'a> CollapseVariableDeclarations { /// #[cfg(test)] mod test { - use oxc_allocator::Allocator; - - use crate::tester; - - fn test(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let mut pass = super::CollapseVariableDeclarations::new(); - tester::test(&allocator, source_text, expected, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } + use crate::tester::{test, test_same}; mod join_vars { use super::{test, test_same}; @@ -286,7 +274,7 @@ mod test { test( "var x = 2; foo(x); x = 3; x = 1; var y = 2; var z = 4; x = 5", - "var x = 2; foo(x); x = 3; x = 1; var y = 2, z = 4; x = 5", + "var x = 2; foo(x), x = 3, x = 1; var y = 2, z = 4; x = 5", ); } @@ -304,9 +292,9 @@ mod test { #[test] fn test_aggressive_redeclaration_in_for() { - test_same("for(var x = 1; x = 2; x = 3) {x = 4}"); + test_same("for(var x = 1; x = 2; x = 3) x = 4"); test_same("for(var x = 1; y = 2; z = 3) {var a = 4}"); - test_same("var x; for(x = 1; x = 2; z = 3) {x = 4}"); + test_same("var x; for(x = 1; x = 2; z = 3) x = 4"); } #[test] @@ -354,9 +342,9 @@ mod test { #[test] fn test_aggressive_redeclaration_of_let_in_for() { - test_same("for(let x = 1; x = 2; x = 3) {x = 4}"); + test_same("for(let x = 1; x = 2; x = 3) x = 4"); test_same("for(let x = 1; y = 2; z = 3) {let a = 4}"); - test_same("let x; for(x = 1; x = 2; z = 3) {x = 4}"); + test_same("let x; for(x = 1; x = 2; z = 3) x = 4"); } #[test] @@ -374,16 +362,19 @@ mod test { // do not redeclare function parameters // incompatible with strict mode - test_same("function f(x) { let y = 3; x = 4; x + y; }"); + test_same("function f(x) { let y = 3; x = 4, x + y; }"); } #[test] fn test_arrow_function() { - test("() => {let x = 1; let y = 2; x + y; }", "() => {let x = 1, y = 2; x + y; }"); + test( + "(() => { let x = 1; let y = 2; x + y; })()", + "(() => { let x = 1, y = 2; x + y; })()", + ); // do not redeclare function parameters // incompatible with strict mode - test_same("(x) => {x = 4; let y = 2; x + y; }"); + test_same("((x) => { x = 4; let y = 2; x + y; })()"); } #[test] @@ -430,7 +421,7 @@ mod test { // Verify FOR inside IFs. test( "if(x){var a = 0; for(; c < b; c++) foo()}", - "if(x){for(var a = 0; c < b; c++) foo()}", + "if(x)for(var a = 0; c < b; c++) foo()", ); // Any other expression. @@ -441,7 +432,7 @@ mod test { "function f(){ var a; for(; a < 2 ; a++) foo() }", "function f(){ for(var a; a < 2 ; a++) foo() }", ); - test_same("function f(){ return; for(; a < 2 ; a++) foo() }"); + test_same("function f(){ for(; a < 2 ; a++) foo() }"); // TODO // Verify destructuring assignments are moved. @@ -459,7 +450,7 @@ mod test { #[test] fn test_for_in() { test("var a; for(a in b) foo()", "for (var a in b) foo()"); - test_same("a = 0; for(a in b) foo()"); + test("a = 0; for(a in b) foo()", "for (a in a = 0, b) foo();"); test_same("var a = 0; for(a in b) foo()"); // We don't handle labels yet. @@ -467,13 +458,13 @@ mod test { test_same("var a; a:b:for(a in b) foo()"); // Verify FOR inside IFs. - test("if(x){var a; for(a in b) foo()}", "if(x){for(var a in b) foo()}"); + test("if(x){var a; for(a in b) foo()}", "if(x) for(var a in b) foo()"); // Any other expression. - test_same("init(); for(a in b) foo()"); + test("init(); for(a in b) foo()", "for (a in init(), b) foo();"); // Other statements are left as is. - test_same("function f(){ return; for(a in b) foo() }"); + test_same("function f(){ for(a in b) foo() }"); // We don't handle destructuring patterns yet. test("var a; var b; for ([a, b] in c) foo();", "var a, b; for ([a, b] in c) foo();"); @@ -490,13 +481,13 @@ mod test { test_same("var a; a: b: for (a of b) foo()"); // Verify FOR inside IFs. - test("if (x) { var a; for (a of b) foo() }", "if (x) { for (var a of b) foo() }"); + test("if (x) { var a; for (a of b) foo() }", "if (x) for (var a of b) foo()"); // Any other expression. test_same("init(); for (a of b) foo()"); // Other statements are left as is. - test_same("function f() { return; for (a of b) foo() }"); + test_same("function f() { for (a of b) foo() }"); // We don't handle destructuring patterns yet. test("var a; var b; for ([a, b] of c) foo();", "var a, b; for ([a, b] of c) foo();"); diff --git a/crates/oxc_minifier/src/ast_passes/convert_to_dotted_properties.rs b/crates/oxc_minifier/src/ast_passes/convert_to_dotted_properties.rs index 1fe050b916ea3..9564e6d964fc3 100644 --- a/crates/oxc_minifier/src/ast_passes/convert_to_dotted_properties.rs +++ b/crates/oxc_minifier/src/ast_passes/convert_to_dotted_properties.rs @@ -67,19 +67,7 @@ impl<'a> ConvertToDottedProperties { #[cfg(test)] mod test { - use oxc_allocator::Allocator; - - use crate::tester; - - fn test(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let mut pass = super::ConvertToDottedProperties::new(false); - tester::test(&allocator, source_text, expected, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } + use crate::tester::{test, test_same}; #[test] fn test_computed_to_member_expression() { @@ -117,7 +105,7 @@ mod test { test_same("a[':']"); test_same("a['.']"); test_same("a['p ']"); - test_same("a['p' + '']"); + test("a['p' + '']", "a.p"); test_same("a[p]"); test_same("a[P]"); test_same("a[$]"); @@ -134,10 +122,10 @@ mod test { #[test] fn test_convert_to_dotted_properties_quoted_props() { - test_same("({'':0})"); - test_same("({'1.0':0})"); - test_same("({'\\u1d17A':0})"); - test_same("({'a\\u0004b':0})"); + test("({'':0})", ""); + test("({'1.0':0})", ""); + test("({'\\u1d17A':0})", ""); + test("({'a\\u0004b':0})", ""); } #[test] @@ -160,7 +148,7 @@ mod test { test_same("a?.['.']"); test_same("a?.['0']"); test_same("a?.['p ']"); - test_same("a?.['p' + '']"); + test("a?.['p' + '']", "a?.p"); test_same("a?.[p]"); test_same("a?.[P]"); test_same("a?.[$]"); @@ -296,8 +284,8 @@ mod test { test_same("x?.['y z']"); test("x?.['y']()", "x?.y();"); test_same("x?.['y z']()"); - test_same("x['y' + 'z']"); - test_same("x?.['y' + 'z']"); + test("x['y' + 'z']", "x.yz"); + test("x?.['y' + 'z']", "x?.yz"); test("x['0']", "x[0];"); test("x['123']", "x[123];"); test("x['-123']", "x[-123];"); diff --git a/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs b/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs index 074f9a2f98cad..492d023a7f96d 100644 --- a/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs +++ b/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs @@ -32,19 +32,7 @@ impl ExploitAssigns { /// #[cfg(test)] mod test { - use oxc_allocator::Allocator; - - use crate::tester; - - fn test(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let mut pass = super::ExploitAssigns::new(); - tester::test(&allocator, source_text, expected, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } + use crate::tester::{test, test_same}; #[test] #[ignore] diff --git a/crates/oxc_minifier/src/ast_passes/minimize_exit_points.rs b/crates/oxc_minifier/src/ast_passes/minimize_exit_points.rs index 8b7c7255f6146..244a51d7739ae 100644 --- a/crates/oxc_minifier/src/ast_passes/minimize_exit_points.rs +++ b/crates/oxc_minifier/src/ast_passes/minimize_exit_points.rs @@ -43,111 +43,99 @@ impl<'a> MinimizeExitPoints { #[cfg(test)] mod test { - use oxc_allocator::Allocator; - - use crate::tester; - - fn fold(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let mut pass = super::MinimizeExitPoints::new(); - tester::test(&allocator, source_text, expected, &mut pass); - } - - fn fold_same(source_text: &str) { - fold(source_text, source_text); - } + use crate::tester::{test, test_same}; #[test] #[ignore] fn test_break_optimization() { - fold("f:{if(true){a();break f;}else;b();}", "f:{if(true){a()}else{b()}}"); - fold("f:{if(false){a();break f;}else;b();break f;}", "f:{if(false){a()}else{b()}}"); - fold("f:{if(a()){b();break f;}else;c();}", "f:{if(a()){b();}else{c();}}"); - fold("f:{if(a()){b()}else{c();break f;}}", "f:{if(a()){b()}else{c();}}"); - fold("f:{if(a()){b();break f;}else;}", "f:{if(a()){b();}else;}"); - fold("f:{if(a()){break f;}else;}", "f:{if(a()){}else;}"); + test("f:{if(true){a();break f;}else;b();}", "f:{if(true){a()}else{b()}}"); + test("f:{if(false){a();break f;}else;b();break f;}", "f:{if(false){a()}else{b()}}"); + test("f:{if(a()){b();break f;}else;c();}", "f:{if(a()){b();}else{c();}}"); + test("f:{if(a()){b()}else{c();break f;}}", "f:{if(a()){b()}else{c();}}"); + test("f:{if(a()){b();break f;}else;}", "f:{if(a()){b();}else;}"); + test("f:{if(a()){break f;}else;}", "f:{if(a()){}else;}"); - fold("f:while(a())break f;", "f:while(a())break f"); - fold_same("f:for(x in a())break f"); + test("f:while(a())break f;", "f:while(a())break f"); + test_same("f:for(x in a())break f"); - fold_same("f:{while(a())break;}"); - fold_same("f:{for(x in a())break}"); + test_same("f:{while(a())break;}"); + test_same("f:{for(x in a())break}"); - fold("f:try{break f;}catch(e){break f;}", "f:try{}catch(e){}"); - fold( + test("f:try{break f;}catch(e){break f;}", "f:try{}catch(e){}"); + test( "f:try{if(a()){break f;}else{break f;} break f;}catch(e){}", "f:try{if(a()){}else{}}catch(e){}", ); - fold("f:g:break f", ""); - fold("f:g:{if(a()){break f;}else{break f;} break f;}", "f:g:{if(a()){}else{}}"); - fold("function f() { a: break a; }", "function f() {}"); - fold("function f() { a: { break a; } }", "function f() { a: {} }"); + test("f:g:break f", ""); + test("f:g:{if(a()){break f;}else{break f;} break f;}", "f:g:{if(a()){}else{}}"); + test("function f() { a: break a; }", "function f() {}"); + test("function f() { a: { break a; } }", "function f() { a: {} }"); } #[test] fn test_function_return_optimization1() { - fold("function f(){return}", "function f(){}"); + test("function f(){return}", "function f(){}"); } #[test] #[ignore] fn test_function_return_optimization2() { - fold("function f(){if(a()){b();if(c())return;}}", "function f(){if(a()){b();if(c());}}"); - fold("function f(){if(x)return; x=3; return; }", "function f(){if(x); else x=3}"); - fold( + test("function f(){if(a()){b();if(c())return;}}", "function f(){if(a()){b();if(c());}}"); + test("function f(){if(x)return; x=3; return; }", "function f(){if(x); else x=3}"); + test( "function f(){if(true){a();return;}else;b();}", "function f(){if(true){a();}else{b();}}", ); - fold( + test( "function f(){if(false){a();return;}else;b();return;}", "function f(){if(false){a();}else{b();}}", ); - fold( + test( "function f(){if(a()){b();return;}else;c();}", "function f(){if(a()){b();}else{c();}}", ); - fold("function f(){if(a()){b()}else{c();return;}}", "function f(){if(a()){b()}else{c();}}"); - fold("function f(){if(a()){b();return;}else;}", "function f(){if(a()){b();}else;}"); - fold( + test("function f(){if(a()){b()}else{c();return;}}", "function f(){if(a()){b()}else{c();}}"); + test("function f(){if(a()){b();return;}else;}", "function f(){if(a()){b();}else;}"); + test( "function f(){if(a()){return;}else{return;} return;}", "function f(){if(a()){}else{}}", ); - fold( + test( "function f(){if(a()){return;}else{return;} b();}", "function f(){if(a()){}else{return;b()}}", ); - fold( + test( "function f(){ if (x) return; if (y) return; if (z) return; w(); }", "function f() { if (x) {} else { if (y) {} else { if (z) {} else w(); }} }", ); - fold("function f(){while(a())return;}", "function f(){while(a())return}"); - fold_same("function f(){for(x in a())return}"); + test("function f(){while(a())return;}", "function f(){while(a())return}"); + test_same("function f(){for(x in a())return}"); - fold("function f(){while(a())break;}", "function f(){while(a())break}"); - fold_same("function f(){for(x in a())break}"); + test("function f(){while(a())break;}", "function f(){while(a())break}"); + test_same("function f(){for(x in a())break}"); - fold( + test( "function f(){try{return;}catch(e){throw 9;}finally{return}}", "function f(){try{}catch(e){throw 9;}finally{return}}", ); - fold_same("function f(){try{throw 9;}finally{return;}}"); + test_same("function f(){try{throw 9;}finally{return;}}"); - fold("function f(){try{return;}catch(e){return;}}", "function f(){try{}catch(e){}}"); - fold( + test("function f(){try{return;}catch(e){return;}}", "function f(){try{}catch(e){}}"); + test( "function f(){try{if(a()){return;}else{return;} return;}catch(e){}}", "function f(){try{if(a()){}else{}}catch(e){}}", ); - fold("function f(){g:return}", "function f(){}"); - fold( + test("function f(){g:return}", "function f(){}"); + test( "function f(){g:if(a()){return;}else{return;} return;}", "function f(){g:if(a()){}else{}}", ); - fold( + test( "function f(){try{g:if(a()){throw 9;} return;}finally{return}}", "function f(){try{g:if(a()){throw 9;}}finally{return}}", ); @@ -155,7 +143,7 @@ mod test { #[test] fn test_function_return_scoped() { - fold_same( + test( "function f(a) { if (a) { const a = Math.random(); @@ -165,55 +153,62 @@ mod test { } return a; }", + "function f(a) { + if (a) { + const a = Math.random(); + if (a < 0.5) return a; + } + return a; + }", ); } #[test] #[ignore] fn test_while_continue_optimization() { - fold("while(true){if(x)continue; x=3; continue; }", "while(true)if(x);else x=3"); - fold_same("while(true){a();continue;b();}"); - fold( + test("while(true){if(x)continue; x=3; continue; }", "while(true)if(x);else x=3"); + test_same("while(true){a();continue;b();}"); + test( "while(true){if(true){a();continue;}else;b();}", "while(true){if(true){a();}else{b()}}", ); - fold( + test( "while(true){if(false){a();continue;}else;b();continue;}", "while(true){if(false){a()}else{b();}}", ); - fold( + test( "while(true){if(a()){b();continue;}else;c();}", "while(true){if(a()){b();}else{c();}}", ); - fold( + test( "while(true){if(a()){b();}else{c();continue;}}", "while(true){if(a()){b();}else{c();}}", ); - fold("while(true){if(a()){b();continue;}else;}", "while(true){if(a()){b();}else;}"); - fold( + test("while(true){if(a()){b();continue;}else;}", "while(true){if(a()){b();}else;}"); + test( "while(true){if(a()){continue;}else{continue;} continue;}", "while(true){if(a()){}else{}}", ); - fold( + test( "while(true){if(a()){continue;}else{continue;} b();}", "while(true){if(a()){}else{continue;b();}}", ); - fold("while(true)while(a())continue;", "while(true)while(a());"); - fold("while(true)for(x in a())continue", "while(true)for(x in a());"); + test("while(true)while(a())continue;", "while(true)while(a());"); + test("while(true)for(x in a())continue", "while(true)for(x in a());"); - fold("while(true)while(a())break;", "while(true)while(a())break"); - fold_same("while(true)for(x in a())break"); + test("while(true)while(a())break;", "while(true)while(a())break"); + test_same("while(true)for(x in a())break"); - fold("while(true){try{continue;}catch(e){continue;}}", "while(true){try{}catch(e){}}"); - fold( + test("while(true){try{continue;}catch(e){continue;}}", "while(true){try{}catch(e){}}"); + test( "while(true){try{if(a()){continue;}else{continue;} continue;}catch(e){}}", "while(true){try{if(a()){}else{}}catch(e){}}", ); - fold("while(true){g:continue}", "while(true){}"); + test("while(true){g:continue}", "while(true){}"); // This case could be improved. - fold( + test( "while(true){g:if(a()){continue;}else{continue;} continue;}", "while(true){g:if(a());else;}", ); @@ -222,148 +217,148 @@ mod test { #[test] #[ignore] fn test_do_continue_optimization() { - fold("do{if(x)continue; x=3; continue; }while(true)", "do if(x); else x=3; while(true)"); - fold_same("do{a();continue;b()}while(true)"); - fold( + test("do{if(x)continue; x=3; continue; }while(true)", "do if(x); else x=3; while(true)"); + test_same("do{a();continue;b()}while(true)"); + test( "do{if(true){a();continue;}else;b();}while(true)", "do{if(true){a();}else{b();}}while(true)", ); - fold( + test( "do{if(false){a();continue;}else;b();continue;}while(true)", "do{if(false){a();}else{b();}}while(true)", ); - fold( + test( "do{if(a()){b();continue;}else;c();}while(true)", "do{if(a()){b();}else{c()}}while(true)", ); - fold( + test( "do{if(a()){b();}else{c();continue;}}while(true)", "do{if(a()){b();}else{c();}}while(true)", ); - fold("do{if(a()){b();continue;}else;}while(true)", "do{if(a()){b();}else;}while(true)"); - fold( + test("do{if(a()){b();continue;}else;}while(true)", "do{if(a()){b();}else;}while(true)"); + test( "do{if(a()){continue;}else{continue;} continue;}while(true)", "do{if(a()){}else{}}while(true)", ); - fold( + test( "do{if(a()){continue;}else{continue;} b();}while(true)", "do{if(a()){}else{continue; b();}}while(true)", ); - fold("do{while(a())continue;}while(true)", "do while(a());while(true)"); - fold("do{for(x in a())continue}while(true)", "do for(x in a());while(true)"); + test("do{while(a())continue;}while(true)", "do while(a());while(true)"); + test("do{for(x in a())continue}while(true)", "do for(x in a());while(true)"); - fold("do{while(a())break;}while(true)", "do while(a())break;while(true)"); - fold_same("do for(x in a())break;while(true)"); + test("do{while(a())break;}while(true)", "do while(a())break;while(true)"); + test_same("do for(x in a())break;while(true)"); - fold("do{try{continue;}catch(e){continue;}}while(true)", "do{try{}catch(e){}}while(true)"); - fold( + test("do{try{continue;}catch(e){continue;}}while(true)", "do{try{}catch(e){}}while(true)"); + test( "do{try{if(a()){continue;}else{continue;} continue;}catch(e){}}while(true)", "do{try{if(a()){}else{}}catch(e){}}while(true)", ); - fold("do{g:continue}while(true)", "do{}while(true)"); + test("do{g:continue}while(true)", "do{}while(true)"); // This case could be improved. - fold( + test( "do{g:if(a()){continue;}else{continue;} continue;}while(true)", "do{g:if(a());else;}while(true)", ); - fold("do { foo(); continue; } while(false)", "do { foo(); } while(false)"); - fold("do { foo(); break; } while(false)", "do { foo(); } while(false)"); + test("do { foo(); continue; } while(false)", "do { foo(); } while(false)"); + test("do { foo(); break; } while(false)", "do { foo(); } while(false)"); - fold("do{break}while(!new Date());", "do{}while(!new Date());"); + test("do{break}while(!new Date());", "do{}while(!new Date());"); - fold_same("do { foo(); switch (x) { case 1: break; default: f()}; } while(false)"); + test_same("do { foo(); switch (x) { case 1: break; default: f()}; } while(false)"); } #[test] #[ignore] fn test_for_continue_optimization() { - fold("for(x in y){if(x)continue; x=3; continue; }", "for(x in y)if(x);else x=3"); - fold_same("for(x in y){a();continue;b()}"); - fold("for(x in y){if(true){a();continue;}else;b();}", "for(x in y){if(true)a();else b();}"); - fold( + test("for(x in y){if(x)continue; x=3; continue; }", "for(x in y)if(x);else x=3"); + test_same("for(x in y){a();continue;b()}"); + test("for(x in y){if(true){a();continue;}else;b();}", "for(x in y){if(true)a();else b();}"); + test( "for(x in y){if(false){a();continue;}else;b();continue;}", "for(x in y){if(false){a();}else{b()}}", ); - fold( + test( "for(x in y){if(a()){b();continue;}else;c();}", "for(x in y){if(a()){b();}else{c();}}", ); - fold( + test( "for(x in y){if(a()){b();}else{c();continue;}}", "for(x in y){if(a()){b();}else{c();}}", ); - fold("for(x of y){if(x)continue; x=3; continue; }", "for(x of y)if(x);else x=3"); - fold_same("for(x of y){a();continue;b()}"); - fold("for(x of y){if(true){a();continue;}else;b();}", "for(x of y){if(true)a();else b();}"); - fold( + test("for(x of y){if(x)continue; x=3; continue; }", "for(x of y)if(x);else x=3"); + test_same("for(x of y){a();continue;b()}"); + test("for(x of y){if(true){a();continue;}else;b();}", "for(x of y){if(true)a();else b();}"); + test( "for(x of y){if(false){a();continue;}else;b();continue;}", "for(x of y){if(false){a();}else{b()}}", ); - fold( + test( "for(x of y){if(a()){b();continue;}else;c();}", "for(x of y){if(a()){b();}else{c();}}", ); - fold( + test( "for(x of y){if(a()){b();}else{c();continue;}}", "for(x of y){if(a()){b();}else{c();}}", ); - fold( + test( "async () => { for await (x of y){if(x)continue; x=3; continue; }}", "async () => { for await (x of y)if(x);else x=3 }", ); - fold_same("async () => { for await (x of y){a();continue;b()}}"); - fold( + test_same("async () => { for await (x of y){a();continue;b()}}"); + test( "async () => { for await (x of y){if(true){a();continue;}else;b();}}", "async () => { for await (x of y){if(true)a();else b();}}", ); - fold( + test( "async () => { for await (x of y){if(false){a();continue;}else;b();continue;}}", "async () => { for await (x of y){if(false){a();}else{b()}}}", ); - fold( + test( "async () => { for await (x of y){if(a()){b();continue;}else;c();}}", "async () => { for await (x of y){if(a()){b();}else{c();}}}", ); - fold( + test( "async () => { for await (x of y){if(a()){b();}else{c();continue;}}}", "async () => { for await (x of y){if(a()){b();}else{c();}}}", ); - fold( + test( "for(x=0;x a; }", "function f() { if (x) {} else { var a = 3; var b = () => a;} }", ); - fold( + test( "function f() { if (x) { if (y) {return;} let c = 3; } }", "function f() { if (x) { if (y) {} else { var c = 3; } } }", ); @@ -441,11 +436,11 @@ mod test { #[test] #[ignore] - fn test_dont_fold_block_scoped_variables_in_loops() { + fn test_dont_test_block_scoped_variables_in_loops() { // Don't move block-scoped declarations into inner blocks inside a loop, since converting // let/const declarations to vars in a loop can cause incorrect semantics. // See the following test case for an example. - fold_same( + test_same( "function f(param) { let arr = []; for (let x of param) { @@ -458,10 +453,10 @@ mod test { ); // Additional tests for different kinds of loops. - fold_same("function f() { while (true) { if (true) {return;} let c = 3; } }"); - fold_same("function f() { do { if (true) {return;} let c = 3; } while (x); }"); - fold_same("function f() { for (;;) { if (true) { return; } let c = 3; } }"); - fold_same("function f(y) { for(x in []){ if(x) { return; } let c = 3; } }"); - fold_same("async function f(y) { for await (x in []){ if(x) { return; } let c = 3; } }"); + test_same("function f() { while (true) { if (true) {return;} let c = 3; } }"); + test_same("function f() { do { if (true) {return;} let c = 3; } while (x); }"); + test_same("function f() { for (;;) { if (true) { return; } let c = 3; } }"); + test_same("function f(y) { for(x in []){ if(x) { return; } let c = 3; } }"); + test_same("async function f(y) { for await (x in []){ if(x) { return; } let c = 3; } }"); } } diff --git a/crates/oxc_minifier/src/ast_passes/normalize.rs b/crates/oxc_minifier/src/ast_passes/normalize.rs index a8e847a2a30bc..c9a096f54f122 100644 --- a/crates/oxc_minifier/src/ast_passes/normalize.rs +++ b/crates/oxc_minifier/src/ast_passes/normalize.rs @@ -175,22 +175,7 @@ impl<'a> Normalize { #[cfg(test)] mod test { - use oxc_allocator::Allocator; - - use super::NormalizeOptions; - use crate::{tester, CompressOptions}; - - fn test(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let compress_options = CompressOptions { - drop_debugger: true, - drop_console: true, - ..CompressOptions::default() - }; - let options = NormalizeOptions { convert_while_to_fors: true }; - let mut pass = super::Normalize::new(options, compress_options); - tester::test(&allocator, source_text, expected, &mut pass); - } + use crate::tester::test; #[test] fn test_while() { @@ -200,8 +185,8 @@ mod test { #[test] fn test_void_ident() { - test("var x; void x", "var x; void 0"); - test("void x", "void x"); // reference error + test("var x; void x", "var x"); + test("void x", "x"); // reference error } #[test] @@ -212,8 +197,8 @@ mod test { #[test] fn drop_console() { - test("console.log()", "void 0;\n"); - test("() => console.log()", "() => void 0"); + test("console.log()", ""); + test("(() => console.log())()", "(() => void 0)()"); } #[test] diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index cc7c8d0ff649a..e9218d0f6c80b 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -726,8 +726,6 @@ impl<'a, 'b> PeepholeFoldConstants { /// #[cfg(test)] mod test { - use oxc_allocator::Allocator; - static MAX_SAFE_FLOAT: f64 = 9_007_199_254_740_991_f64; static NEG_MAX_SAFE_FLOAT: f64 = -9_007_199_254_740_991_f64; @@ -736,10 +734,11 @@ mod test { use crate::tester; + // wrap with a function call so it doesn't get removed. fn test(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let mut pass = super::PeepholeFoldConstants::new(); - tester::test(&allocator, source_text, expected, &mut pass); + let source_text = format!("NOOP({source_text})"); + let expected = format!("NOOP({expected})"); + tester::test(&source_text, &expected); } fn test_same(source_text: &str) { @@ -748,7 +747,7 @@ mod test { #[test] fn test_comparison() { - test("(1, 2) !== 2", "false"); + test("(1, 2) !== 2", "!1"); test_same("({} <= {})"); test_same("({} >= {})"); test_same("({} > {})"); @@ -761,218 +760,218 @@ mod test { #[test] fn undefined_comparison1() { - test("undefined == undefined", "true"); - test("undefined == null", "true"); - test("undefined == void 0", "true"); - - test("undefined == 0", "false"); - test("undefined == 1", "false"); - test("undefined == 'hi'", "false"); - test("undefined == true", "false"); - test("undefined == false", "false"); - - test("undefined === undefined", "true"); - test("undefined === null", "false"); - test("undefined === void 0", "true"); - - test_same("undefined == this"); - test_same("undefined == x"); - - test("undefined != undefined", "false"); - test("undefined != null", "false"); - test("undefined != void 0", "false"); - - test("undefined != 0", "true"); - test("undefined != 1", "true"); - test("undefined != 'hi'", "true"); - test("undefined != true", "true"); - test("undefined != false", "true"); - - test("undefined !== undefined", "false"); - test("undefined !== void 0", "false"); - test("undefined !== null", "true"); - - test_same("undefined != this"); - test_same("undefined != x"); - - test("undefined < undefined", "false"); - test("undefined > undefined", "false"); - test("undefined >= undefined", "false"); - test("undefined <= undefined", "false"); - - test("0 < undefined", "false"); - test("true > undefined", "false"); - test("'hi' >= undefined", "false"); - test("null <= undefined", "false"); - - test("undefined < 0", "false"); - test("undefined > true", "false"); - test("undefined >= 'hi'", "false"); - test("undefined <= null", "false"); - - test("null == undefined", "true"); - test("0 == undefined", "false"); - test("1 == undefined", "false"); - test("'hi' == undefined", "false"); - test("true == undefined", "false"); - test("false == undefined", "false"); - test("null === undefined", "false"); - test("void 0 === undefined", "true"); - - test("undefined == NaN", "false"); - test("NaN == undefined", "false"); - test("undefined == Infinity", "false"); - test("Infinity == undefined", "false"); - test("undefined == -Infinity", "false"); - test("-Infinity == undefined", "false"); - test("({}) == undefined", "false"); - test("undefined == ({})", "false"); - test("([]) == undefined", "false"); - test("undefined == ([])", "false"); - test("(/a/g) == undefined", "false"); - test("undefined == (/a/g)", "false"); - test("(function(){}) == undefined", "false"); - test("undefined == (function(){})", "false"); - - test("undefined != NaN", "true"); - test("NaN != undefined", "true"); - test("undefined != Infinity", "true"); - test("Infinity != undefined", "true"); - test("undefined != -Infinity", "true"); - test("-Infinity != undefined", "true"); - test("({}) != undefined", "true"); - test("undefined != ({})", "true"); - test("([]) != undefined", "true"); - test("undefined != ([])", "true"); - test("(/a/g) != undefined", "true"); - test("undefined != (/a/g)", "true"); - test("(function(){}) != undefined", "true"); - test("undefined != (function(){})", "true"); - - test_same("this == undefined"); - test_same("x == undefined"); + test("undefined == undefined", "!0"); + test("undefined == null", "!0"); + test("undefined == void 0", "!0"); + + test("undefined == 0", "!1"); + test("undefined == 1", "!1"); + test("undefined == 'hi'", "!1"); + test("undefined == true", "!1"); + test("undefined == false", "!1"); + + test("undefined === undefined", "!0"); + test("undefined === null", "!1"); + test("undefined === void 0", "!0"); + + test("undefined == this", "this == null"); + test("undefined == x", "x == null"); + + test("undefined != undefined", "!1"); + test("undefined != null", "!1"); + test("undefined != void 0", "!1"); + + test("undefined != 0", "!0"); + test("undefined != 1", "!0"); + test("undefined != 'hi'", "!0"); + test("undefined != true", "!0"); + test("undefined != false", "!0"); + + test("undefined !== undefined", "!1"); + test("undefined !== void 0", "!1"); + test("undefined !== null", "!0"); + + test("undefined != this", "this != null"); + test("undefined != x", "x != null"); + + test("undefined < undefined", "!1"); + test("undefined > undefined", "!1"); + test("undefined >= undefined", "!1"); + test("undefined <= undefined", "!1"); + + test("0 < undefined", "!1"); + test("true > undefined", "!1"); + test("'hi' >= undefined", "!1"); + test("null <= undefined", "!1"); + + test("undefined < 0", "!1"); + test("undefined > true", "!1"); + test("undefined >= 'hi'", "!1"); + test("undefined <= null", "!1"); + + test("null == undefined", "!0"); + test("0 == undefined", "!1"); + test("1 == undefined", "!1"); + test("'hi' == undefined", "!1"); + test("true == undefined", "!1"); + test("false == undefined", "!1"); + test("null === undefined", "!1"); + test("void 0 === undefined", "!0"); + + test("undefined == NaN", "!1"); + test("NaN == undefined", "!1"); + test("undefined == Infinity", "!1"); + test("Infinity == undefined", "!1"); + test("undefined == -Infinity", "!1"); + test("-Infinity == undefined", "!1"); + test("({}) == undefined", "!1"); + test("undefined == ({})", "!1"); + test("([]) == undefined", "!1"); + test("undefined == ([])", "!1"); + test("(/a/g) == undefined", "!1"); + test("undefined == (/a/g)", "!1"); + test("(function(){}) == undefined", "!1"); + test("undefined == (function(){})", "!1"); + + test("undefined != NaN", "!0"); + test("NaN != undefined", "!0"); + test("undefined != Infinity", "!0"); + test("Infinity != undefined", "!0"); + test("undefined != -Infinity", "!0"); + test("-Infinity != undefined", "!0"); + test("({}) != undefined", "!0"); + test("undefined != ({})", "!0"); + test("([]) != undefined", "!0"); + test("undefined != ([])", "!0"); + test("(/a/g) != undefined", "!0"); + test("undefined != (/a/g)", "!0"); + test("(function(){}) != undefined", "!0"); + test("undefined != (function(){})", "!0"); + + test("this == undefined", "this == null"); + test("x == undefined", "x == null"); } #[test] fn test_undefined_comparison2() { - test("\"123\" !== void 0", "true"); - test("\"123\" === void 0", "false"); + test("\"123\" !== void 0", "!0"); + test("\"123\" === void 0", "!1"); - test("void 0 !== \"123\"", "true"); - test("void 0 === \"123\"", "false"); + test("void 0 !== \"123\"", "!0"); + test("void 0 === \"123\"", "!1"); } #[test] fn test_undefined_comparison3() { - test("\"123\" !== undefined", "true"); - test("\"123\" === undefined", "false"); + test("\"123\" !== undefined", "!0"); + test("\"123\" === undefined", "!1"); - test("undefined !== \"123\"", "true"); - test("undefined === \"123\"", "false"); + test("undefined !== \"123\"", "!0"); + test("undefined === \"123\"", "!1"); } #[test] fn test_null_comparison1() { - test("null == undefined", "true"); - test("null == null", "true"); - test("null == void 0", "true"); - - test("null == 0", "false"); - test("null == 1", "false"); - // test("null == 0n", "false"); - // test("null == 1n", "false"); - test("null == 'hi'", "false"); - test("null == true", "false"); - test("null == false", "false"); - - test("null === undefined", "false"); - test("null === null", "true"); - test("null === void 0", "false"); + test("null == undefined", "!0"); + test("null == null", "!0"); + test("null == void 0", "!0"); + + test("null == 0", "!1"); + test("null == 1", "!1"); + // test("null == 0n", "!1"); + // test("null == 1n", "!1"); + test("null == 'hi'", "!1"); + test("null == true", "!1"); + test("null == false", "!1"); + + test("null === undefined", "!1"); + test("null === null", "!0"); + test("null === void 0", "!1"); test_same("x===null"); test_same("this==null"); test_same("x==null"); - test("null != undefined", "false"); - test("null != null", "false"); - test("null != void 0", "false"); + test("null != undefined", "!1"); + test("null != null", "!1"); + test("null != void 0", "!1"); - test("null != 0", "true"); - test("null != 1", "true"); - // test("null != 0n", "true"); - // test("null != 1n", "true"); - test("null != 'hi'", "true"); - test("null != true", "true"); - test("null != false", "true"); + test("null != 0", "!0"); + test("null != 1", "!0"); + // test("null != 0n", "!0"); + // test("null != 1n", "!0"); + test("null != 'hi'", "!0"); + test("null != true", "!0"); + test("null != false", "!0"); - test("null !== undefined", "true"); - test("null !== void 0", "true"); - test("null !== null", "false"); + test("null !== undefined", "!0"); + test("null !== void 0", "!0"); + test("null !== null", "!1"); test_same("this!=null"); test_same("x!=null"); - test("null < null", "false"); - test("null > null", "false"); - test("null >= null", "true"); - test("null <= null", "true"); - - test("0 < null", "false"); - test("0 > null", "false"); - test("0 >= null", "true"); - // test("0n < null", "false"); - // test("0n > null", "false"); - // test("0n >= null", "true"); - test("true > null", "true"); - test("'hi' < null", "false"); - test("'hi' >= null", "false"); - test("null <= null", "true"); - - test("null < 0", "false"); - // test("null < 0n", "false"); - test("null > true", "false"); - test("null < 'hi'", "false"); - test("null >= 'hi'", "false"); - test("null <= null", "true"); - - test("null == null", "true"); - test("0 == null", "false"); - test("1 == null", "false"); - test("'hi' == null", "false"); - test("true == null", "false"); - test("false == null", "false"); - test("null === null", "true"); - test("void 0 === null", "false"); - - test("null == NaN", "false"); - test("NaN == null", "false"); - test("null == Infinity", "false"); - test("Infinity == null", "false"); - test("null == -Infinity", "false"); - test("-Infinity == null", "false"); - test("({}) == null", "false"); - test("null == ({})", "false"); - test("([]) == null", "false"); - test("null == ([])", "false"); - test("(/a/g) == null", "false"); - test("null == (/a/g)", "false"); - test("(function(){}) == null", "false"); - test("null == (function(){})", "false"); - - test("null != NaN", "true"); - test("NaN != null", "true"); - test("null != Infinity", "true"); - test("Infinity != null", "true"); - test("null != -Infinity", "true"); - test("-Infinity != null", "true"); - test("({}) != null", "true"); - test("null != ({})", "true"); - test("([]) != null", "true"); - test("null != ([])", "true"); - test("(/a/g) != null", "true"); - test("null != (/a/g)", "true"); - test("(function(){}) != null", "true"); - test("null != (function(){})", "true"); + test("null < null", "!1"); + test("null > null", "!1"); + test("null >= null", "!0"); + test("null <= null", "!0"); + + test("0 < null", "!1"); + test("0 > null", "!1"); + test("0 >= null", "!0"); + // test("0n < null", "!1"); + // test("0n > null", "!1"); + // test("0n >= null", "!0"); + test("true > null", "!0"); + test("'hi' < null", "!1"); + test("'hi' >= null", "!1"); + test("null <= null", "!0"); + + test("null < 0", "!1"); + // test("null < 0n", "!1"); + test("null > true", "!1"); + test("null < 'hi'", "!1"); + test("null >= 'hi'", "!1"); + test("null <= null", "!0"); + + test("null == null", "!0"); + test("0 == null", "!1"); + test("1 == null", "!1"); + test("'hi' == null", "!1"); + test("true == null", "!1"); + test("false == null", "!1"); + test("null === null", "!0"); + test("void 0 === null", "!1"); + + test("null == NaN", "!1"); + test("NaN == null", "!1"); + test("null == Infinity", "!1"); + test("Infinity == null", "!1"); + test("null == -Infinity", "!1"); + test("-Infinity == null", "!1"); + test("({}) == null", "!1"); + test("null == ({})", "!1"); + test("([]) == null", "!1"); + test("null == ([])", "!1"); + test("(/a/g) == null", "!1"); + test("null == (/a/g)", "!1"); + test("(function(){}) == null", "!1"); + test("null == (function(){})", "!1"); + + test("null != NaN", "!0"); + test("NaN != null", "!0"); + test("null != Infinity", "!0"); + test("Infinity != null", "!0"); + test("null != -Infinity", "!0"); + test("-Infinity != null", "!0"); + test("({}) != null", "!0"); + test("null != ({})", "!0"); + test("([]) != null", "!0"); + test("null != ([])", "!0"); + test("(/a/g) != null", "!0"); + test("null != (/a/g)", "!0"); + test("(function(){}) != null", "!0"); + test("null != (function(){})", "!0"); test_same("({a:f()})==null"); test_same("[f()]==null"); @@ -983,13 +982,13 @@ mod test { #[test] fn test_boolean_boolean_comparison() { - test_same("!x==!y"); - test_same("!x 'b'", "false"); - test("'a' >= 'b'", "false"); - test("+'a' < +'b'", "false"); + test("'a' < 'b'", "!0"); + test("'a' <= 'b'", "!0"); + test("'a' > 'b'", "!1"); + test("'a' >= 'b'", "!1"); + test("+'a' < +'b'", "!1"); test_same("typeof a < 'a'"); test_same("'a' >= typeof a"); test_same("typeof a < typeof a"); test_same("typeof a >= typeof a"); - test("typeof 3 > typeof 4", "false"); - test("typeof function() {} < typeof function() {}", "false"); - test("'a' == 'a'", "true"); - test("'b' != 'a'", "true"); + test("typeof 3 > typeof 4", "!1"); + test("typeof function() {} < typeof function() {}", "!1"); + test("'a' == 'a'", "!0"); + test("'b' != 'a'", "!0"); test_same("typeof a != 'number'"); - test("'a' === 'a'", "true"); - test("'b' !== 'a'", "true"); + test("'a' === 'a'", "!0"); + test("'b' !== 'a'", "!0"); test_same("'' + x <= '' + y"); test_same("'' + x != '' + y"); - test_same("'' + x === '' + y"); + test("'' + x === '' + y", "'' + x == '' + y"); test_same("'' + x <= '' + x"); // potentially foldable test_same("'' + x != '' + x"); // potentially foldable - test_same("'' + x === '' + x"); // potentially foldable + test("'' + x === '' + x", "'' + x == '' + x"); // potentially foldable test(r#"if (" str ing " !== "\u000Bstr\u000Bing\u000B") {}"#, "if (false) {}\n"); } #[test] fn test_number_string_comparison() { - test("1 < '2'", "true"); - test("2 > '1'", "true"); - test("123 > '34'", "true"); - test("NaN >= 'NaN'", "false"); - test("1 == '2'", "false"); - test("1 != '1'", "false"); - test("NaN == 'NaN'", "false"); - test("1 === '1'", "false"); - test("1 !== '1'", "true"); + test("1 < '2'", "!0"); + test("2 > '1'", "!0"); + test("123 > '34'", "!0"); + test("NaN >= 'NaN'", "!1"); + test("1 == '2'", "!1"); + test("1 != '1'", "!1"); + test("NaN == 'NaN'", "!1"); + test("1 === '1'", "!1"); + test("1 !== '1'", "!0"); test_same("+x>''+y"); test_same("+x==''+y"); test_same("+x !== '' + y"); @@ -1067,15 +1066,15 @@ mod test { #[test] fn test_string_number_comparison() { - test("'1' < 2", "true"); - test("'2' > 1", "true"); - test("'123' > 34", "true"); - test("'NaN' < NaN", "false"); - test("'1' == 2", "false"); - test("'1' != 1", "false"); - test("'NaN' == NaN", "false"); - test("'1' === 1", "false"); - test("'1' !== 1", "true"); + test("'1' < 2", "!0"); + test("'2' > 1", "!0"); + test("'123' > 34", "!0"); + test("'NaN' < NaN", "!1"); + test("'1' == 2", "!1"); + test("'1' != 1", "!1"); + test("'NaN' == NaN", "!1"); + test("'1' === 1", "!1"); + test("'1' !== 1", "!0"); test_same("''+x<+y"); test_same("''+x==+y"); test_same("'' + x === +y"); @@ -1083,39 +1082,39 @@ mod test { #[test] fn test_nan_comparison() { - test("NaN < 1", "false"); - test("NaN <= 1", "false"); - test("NaN > 1", "false"); - test("NaN >= 1", "false"); - // test("NaN < 1n", "false"); - // test("NaN <= 1n", "false"); - // test("NaN > 1n", "false"); - // test("NaN >= 1n", "false"); - - test("NaN < NaN", "false"); - test("NaN >= NaN", "false"); - test("NaN == NaN", "false"); - test("NaN === NaN", "false"); - - test("NaN < null", "false"); - test("null >= NaN", "false"); - test("NaN == null", "false"); - test("null != NaN", "true"); - test("null === NaN", "false"); - - test("NaN < undefined", "false"); - test("undefined >= NaN", "false"); - test("NaN == undefined", "false"); - test("undefined != NaN", "true"); - test("undefined === NaN", "false"); + test("NaN < 1", "!1"); + test("NaN <= 1", "!1"); + test("NaN > 1", "!1"); + test("NaN >= 1", "!1"); + // test("NaN < 1n", "!1"); + // test("NaN <= 1n", "!1"); + // test("NaN > 1n", "!1"); + // test("NaN >= 1n", "!1"); + + test("NaN < NaN", "!1"); + test("NaN >= NaN", "!1"); + test("NaN == NaN", "!1"); + test("NaN === NaN", "!1"); + + test("NaN < null", "!1"); + test("null >= NaN", "!1"); + test("NaN == null", "!1"); + test("null != NaN", "!0"); + test("null === NaN", "!1"); + + test("NaN < undefined", "!1"); + test("undefined >= NaN", "!1"); + test("NaN == undefined", "!1"); + test("undefined != NaN", "!0"); + test("undefined === NaN", "!1"); test_same("NaN=NaN"); - test_same("NaN==x"); + test("NaN==x", "x==NaN"); test_same("x!=NaN"); - test_same("NaN === x"); + test("NaN === x", "x === NaN"); test_same("x !== NaN"); - test_same("NaN==foo()"); + test("NaN==foo()", "foo()==NaN"); } #[test] @@ -1143,9 +1142,9 @@ mod test { test_same("~foo()"); test_same("-foo()"); - test("a=!true", "a=false"); - test("a=!10", "a=false"); - test("a=!false", "a=true"); + test("a=!true", "a=!1"); + test("a=!10", "a=!1"); + test("a=!false", "a=!0"); test_same("a=!foo()"); test_same("a = !!void b"); @@ -1183,7 +1182,7 @@ mod test { fn test_fold_unary_big_int() { test("-(1n)", "-1n"); test("- -1n", "1n"); - test("!1n", "false"); + test("!1n", "!1"); test("~0n", "-1n"); test("~-1n", "0n"); @@ -1215,8 +1214,8 @@ mod test { test("x = true && x", "x = x"); test("x = [foo()] && x", "x = ([foo()],x)"); - test("x = false && x", "x = false"); - test("x = true || x", "x = true"); + test("x = false && x", "x = !1"); + test("x = true || x", "x = !0"); test("x = false || x", "x = x"); test("x = 0 && x", "x = 0"); test("x = 3 || x", "x = 3"); @@ -1225,28 +1224,27 @@ mod test { test("x = false || 0", "x = 0"); // unfoldable, because the right-side may be the result - test("a = x && true", "a=x && true"); - test("a = x && false", "a=x && false"); + test("a = x && true", "a=x && !0"); + test("a = x && false", "a=x && !1"); test("a = x || 3", "a=x || 3"); - test("a = x || false", "a=x || false"); - test("a = b ? c : x || false", "a=b ? c:x || false"); - test("a = b ? x || false : c", "a=b ? x || false:c"); - test("a = b ? c : x && true", "a=b ? c:x && true"); - test("a = b ? x && true : c", "a=b ? x && true:c"); + test("a = x || false", "a=x || !1"); + test("a = b ? c : x || false", "a=b ? c : x || !1"); + test("a = b ? x || false : c", "a=b ? x || !1 : c"); + test("a = b ? c : x && true", "a=b ? c : x && !0"); + test("a = b ? x && true : c", "a=b ? x && !0 : c"); - // folded, but not here. - test_same("a = x || false ? b : c"); - test_same("a = x && true ? b : c"); + test("a = x || false ? b : c", "a = x ? b : c"); + test("a = x && true ? b : c", "a = x ? b : c"); - test("x = foo() || true || bar()", "x = foo() || true"); + test("x = foo() || true || bar()", "x = foo() || !0"); test("x = foo() || true && bar()", "x = foo() || bar()"); - test("x = foo() || false && bar()", "x = foo() || false"); - test("x = foo() && false && bar()", "x = foo() && false"); - test("x = foo() && false || bar()", "x = (foo() && false,bar())"); + test("x = foo() || false && bar()", "x = foo() || !1"); + test("x = foo() && false && bar()", "x = foo() && !1"); + test("x = foo() && false || bar()", "x = (foo() && !1, bar())"); test("x = foo() || false || bar()", "x = foo() || bar()"); test("x = foo() && true && bar()", "x = foo() && bar()"); - test("x = foo() || true || bar()", "x = foo() || true"); - test("x = foo() && false && bar()", "x = foo() && false"); + test("x = foo() || true || bar()", "x = foo() || !0"); + test("x = foo() && false && bar()", "x = foo() && !1"); test("x = foo() && 0 && bar()", "x = foo() && 0"); test("x = foo() && 1 && bar()", "x = foo() && bar()"); test("x = foo() || 0 || bar()", "x = foo() || bar()"); @@ -1263,10 +1261,10 @@ mod test { test("a() && (1 && b())", "a() && b()"); test("(a() && 1) && b()", "a() && b()"); - test("(x || '') || y;", "x || y"); - test("false || (x || '');", "x || ''"); - test("(x && 1) && y;", "x && y"); - test("true && (x && 1);", "x && 1"); + test("(x || '') || y", "x || y"); + test("false || (x || '')", "x || ''"); + test("(x && 1) && y", "x && y"); + test("true && (x && 1)", "x && 1"); // Really not foldable, because it would change the type of the // expression if foo() returns something truthy but not true. @@ -1274,8 +1272,8 @@ mod test { // An example would be if foo() is 1 (truthy) and bar() is 0 (falsey): // (1 && true) || 0 == true // 1 || 0 == 1, but true =/= 1 - test_same("x = foo() && true || bar()"); - test_same("foo() && true || bar()"); + test("x = foo() && true || bar()", "x = foo() && !0 || bar()"); + test("foo() && true || bar()", "foo() && !0 || bar()"); } #[test] @@ -1289,31 +1287,31 @@ mod test { fn test_fold_nullish_coalesce() { // fold if left is null/undefined test("null ?? 1", "1"); - test("undefined ?? false", "false"); + test("undefined ?? false", "!1"); test("(a(), null) ?? 1", "(a(), null, 1)"); test("x = [foo()] ?? x", "x = [foo()]"); // short circuit on all non nullish LHS - test("x = false ?? x", "x = false"); - test("x = true ?? x", "x = true"); + test("x = false ?? x", "x = !1"); + test("x = true ?? x", "x = !0"); test("x = 0 ?? x", "x = 0"); test("x = 3 ?? x", "x = 3"); // unfoldable, because the right-side may be the result - test_same("a = x ?? true"); - test_same("a = x ?? false"); + test("a = x ?? true", "a = x ?? !0"); + test("a = x ?? false", "a = x ?? !1"); test_same("a = x ?? 3"); - test_same("a = b ? c : x ?? false"); - test_same("a = b ? x ?? false : c"); + test("a = b ? c : x ?? false", "a = b ? c : x ?? !1"); + test("a = b ? x ?? false : c", "a = b ? x ?? !1 : c"); // folded, but not here. - test_same("a = x ?? false ? b : c"); - test_same("a = x ?? true ? b : c"); + test("a = x ?? false ? b : c", "a = x ?? !1 ? b : c"); + test("a = x ?? true ? b : c", "a = x ?? !0 ? b : c"); - test_same("x = foo() ?? true ?? bar()"); + test("x = foo() ?? true ?? bar()", "x = foo() ?? !0 ?? bar()"); test("x = foo() ?? (true && bar())", "x = foo() ?? bar()"); - test_same("x = (foo() || false) ?? bar()"); + test("x = (foo() || false) ?? bar()", "x = (foo() || !1) ?? bar()"); test("a() ?? (1 ?? b())", "a() ?? 1"); test("(a() ?? 1) ?? b()", "a() ?? 1 ?? b()"); @@ -1653,23 +1651,23 @@ mod test { #[test] fn test_fold_instance_of() { // Non object types are never instances of anything. - test("64 instanceof Object", "false"); - test("64 instanceof Number", "false"); - test("'' instanceof Object", "false"); - test("'' instanceof String", "false"); - test("true instanceof Object", "false"); - test("true instanceof Boolean", "false"); - test("!0 instanceof Object", "false"); - test("!0 instanceof Boolean", "false"); - test("false instanceof Object", "false"); - test("null instanceof Object", "false"); - test("undefined instanceof Object", "false"); - test("NaN instanceof Object", "false"); - test("Infinity instanceof Object", "false"); + test("64 instanceof Object", "!1"); + test("64 instanceof Number", "!1"); + test("'' instanceof Object", "!1"); + test("'' instanceof String", "!1"); + test("true instanceof Object", "!1"); + test("true instanceof Boolean", "!1"); + test("!0 instanceof Object", "!1"); + test("!0 instanceof Boolean", "!1"); + test("false instanceof Object", "!1"); + test("null instanceof Object", "!1"); + test("undefined instanceof Object", "!1"); + test("NaN instanceof Object", "!1"); + test("Infinity instanceof Object", "!1"); // Array and object literals are known to be objects. - test("[] instanceof Object", "true"); - test("({}) instanceof Object", "true"); + test("[] instanceof Object", "!0"); + test("({}) instanceof Object", "!0"); // These cases is foldable, but no handled currently. test_same("new Foo() instanceof Object"); @@ -1677,7 +1675,7 @@ mod test { test_same("[] instanceof Foo"); test_same("({}) instanceof Foo"); - test("(function() {}) instanceof Object", "true"); + test("(function() {}) instanceof Object", "!0"); // An unknown value should never be folded. test_same("x instanceof Foo"); @@ -1686,8 +1684,8 @@ mod test { #[test] fn test_fold_instance_of_additional() { - test("(typeof {}) instanceof Object", "false"); - test("(+{}) instanceof Number", "false"); + test("(typeof {}) instanceof Object", "!1"); + test("(+{}) instanceof Number", "!1"); } #[test] @@ -1772,16 +1770,16 @@ mod test { #[test] fn test_fold_invalid_typeof_comparison() { - test("typeof foo == 123", "false"); - test("typeof foo == '123'", "false"); - test("typeof foo === null", "false"); - test("typeof foo === undefined", "false"); - test("typeof foo !== 123", "true"); - test("typeof foo !== '123'", "true"); - test("typeof foo != null", "true"); - test("typeof foo != undefined", "true"); - test_same("typeof foo === 'string'"); - test_same("typeof foo === 'number'"); + test("typeof foo == 123", "!1"); + test("typeof foo == '123'", "!1"); + test("typeof foo === null", "!1"); + test("typeof foo === undefined", "!1"); + test("typeof foo !== 123", "!0"); + test("typeof foo !== '123'", "!0"); + test("typeof foo != null", "!0"); + test("typeof foo != undefined", "!0"); + test("typeof foo === 'string'", "typeof foo == 'string'"); + test("typeof foo === 'number'", "typeof foo == 'number'"); } // TODO: All big ints are rare and difficult to handle. @@ -1851,68 +1849,68 @@ mod test { #[test] #[ignore] fn test_bigint_number_comparison() { - test("1n < 2", "true"); - test("1n > 2", "false"); - test("1n == 1", "true"); - test("1n == 2", "false"); + test("1n < 2", "!0"); + test("1n > 2", "!1"); + test("1n == 1", "!0"); + test("1n == 2", "!1"); // comparing with decimals is allowed - test("1n < 1.1", "true"); - test("1n < 1.9", "true"); - test("1n < 0.9", "false"); - test("-1n < -1.1", "false"); - test("-1n < -1.9", "false"); - test("-1n < -0.9", "true"); - test("1n > 1.1", "false"); - test("1n > 0.9", "true"); - test("-1n > -1.1", "true"); - test("-1n > -0.9", "false"); + test("1n < 1.1", "!0"); + test("1n < 1.9", "!0"); + test("1n < 0.9", "!1"); + test("-1n < -1.1", "!1"); + test("-1n < -1.9", "!1"); + test("-1n < -0.9", "!0"); + test("1n > 1.1", "!1"); + test("1n > 0.9", "!0"); + test("-1n > -1.1", "!0"); + test("-1n > -0.9", "!1"); // Don't fold unsafely large numbers because there might be floating-point error - test(&format!("0n > {MAX_SAFE_INT}"), "false"); - test(&format!("0n < {MAX_SAFE_INT}"), "true"); - test(&format!("0n > {NEG_MAX_SAFE_INT}"), "true"); - test(&format!("0n < {NEG_MAX_SAFE_INT}"), "false"); - test(&format!("0n > {MAX_SAFE_FLOAT}"), "false"); - test(&format!("0n < {MAX_SAFE_FLOAT}"), "true"); - test(&format!("0n > {NEG_MAX_SAFE_FLOAT}"), "true"); - test(&format!("0n < {NEG_MAX_SAFE_FLOAT}"), "false"); + test(&format!("0n > {MAX_SAFE_INT}"), "!1"); + test(&format!("0n < {MAX_SAFE_INT}"), "!0"); + test(&format!("0n > {NEG_MAX_SAFE_INT}"), "!0"); + test(&format!("0n < {NEG_MAX_SAFE_INT}"), "!1"); + test(&format!("0n > {MAX_SAFE_FLOAT}"), "!1"); + test(&format!("0n < {MAX_SAFE_FLOAT}"), "!0"); + test(&format!("0n > {NEG_MAX_SAFE_FLOAT}"), "!0"); + test(&format!("0n < {NEG_MAX_SAFE_FLOAT}"), "!1"); // comparing with Infinity is allowed - test("1n < Infinity", "true"); - test("1n > Infinity", "false"); - test("1n < -Infinity", "false"); - test("1n > -Infinity", "true"); + test("1n < Infinity", "!0"); + test("1n > Infinity", "!1"); + test("1n < -Infinity", "!1"); + test("1n > -Infinity", "!0"); // null is interpreted as 0 when comparing with bigint - // test("1n < null", "false"); - // test("1n > null", "true"); + // test("1n < null", "!1"); + // test("1n > null", "!0"); } #[test] #[ignore] fn test_bigint_string_comparison() { - test("1n < '2'", "true"); - test("2n > '1'", "true"); - test("123n > '34'", "true"); - test("1n == '1'", "true"); - test("1n == '2'", "false"); - test("1n != '1'", "false"); - test("1n === '1'", "false"); - test("1n !== '1'", "true"); + test("1n < '2'", "!0"); + test("2n > '1'", "!0"); + test("123n > '34'", "!0"); + test("1n == '1'", "!0"); + test("1n == '2'", "!1"); + test("1n != '1'", "!1"); + test("1n === '1'", "!1"); + test("1n !== '1'", "!0"); } #[test] #[ignore] fn test_string_bigint_comparison() { - test("'1' < 2n", "true"); - test("'2' > 1n", "true"); - test("'123' > 34n", "true"); - test("'1' == 1n", "true"); - test("'1' == 2n", "false"); - test("'1' != 1n", "false"); - test("'1' === 1n", "false"); - test("'1' !== 1n", "true"); + test("'1' < 2n", "!0"); + test("'2' > 1n", "!0"); + test("'123' > 34n", "!0"); + test("'1' == 1n", "!0"); + test("'1' == 2n", "!1"); + test("'1' != 1n", "!1"); + test("'1' === 1n", "!1"); + test("'1' !== 1n", "!0"); } #[test] diff --git a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs index 585b1f9e32a27..ae3a651422c45 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs @@ -860,106 +860,90 @@ impl<'a> PeepholeMinimizeConditions { /// #[cfg(test)] mod test { - use oxc_allocator::Allocator; - use oxc_syntax::es_target::ESTarget; - - use crate::tester; - - fn test(source_text: &str, positive: &str) { - let allocator = Allocator::default(); - let mut pass = super::PeepholeMinimizeConditions::new(ESTarget::ES2025); - tester::test(&allocator, source_text, positive, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } - - fn fold_same(js: &str) { - test_same(js); - } - - fn fold(js: &str, expected: &str) { - test(js, expected); - } + use crate::tester::{test, test_same}; /** Check that removing blocks with 1 child works */ #[test] fn test_fold_one_child_blocks() { - // late = false; - fold("function f(){if(x)a();x=3}", "function f(){x&&a();x=3}"); - fold("function f(){if(x)a?.();x=3}", "function f(){x&&a?.();x=3}"); + test("function f(){if(x)a();x=3}", "function f(){x&&a(),x=3}"); + test("function f(){if(x)a?.();x=3}", "function f(){x&&a?.(),x=3}"); - fold("function f(){if(x){a()}x=3}", "function f(){x&&a();x=3}"); - fold("function f(){if(x){a?.()}x=3}", "function f(){x&&a?.();x=3}"); + test("function f(){if(x){a()}x=3}", "function f(){x&&a(),x=3}"); + test("function f(){if(x){a?.()}x=3}", "function f(){x&&a?.(),x=3}"); - // fold("function f(){if(x){return 3}}", "function f(){if(x)return 3}"); - fold("function f(){if(x){a()}}", "function f(){x&&a()}"); - // fold("function f(){if(x){throw 1}}", "function f(){if(x)throw 1;}"); + // test("function f(){if(x){return 3}}", "function f(){if(x)return 3}"); + test("function f(){if(x){a()}}", "function f(){x&&a()}"); + // test("function f(){if(x){throw 1}}", "function f(){if(x)throw 1;}"); // Try it out with functions - fold("function f(){if(x){foo()}}", "function f(){x&&foo()}"); - fold("function f(){if(x){foo()}else{bar()}}", "function f(){x?foo():bar()}"); + test("function f(){if(x){foo()}}", "function f(){x&&foo()}"); + test("function f(){if(x){foo()}else{bar()}}", "function f(){x?foo():bar()}"); // Try it out with properties and methods - fold("function f(){if(x){a.b=1}}", "function f(){x&&(a.b=1)}"); - fold("function f(){if(x){a.b*=1}}", "function f(){x&&(a.b*=1)}"); - fold("function f(){if(x){a.b+=1}}", "function f(){x&&(a.b+=1)}"); - fold("function f(){if(x){++a.b}}", "function f(){x&&++a.b}"); - fold("function f(){if(x){a.foo()}}", "function f(){x&&a.foo()}"); - fold("function f(){if(x){a?.foo()}}", "function f(){x&&a?.foo()}"); + test("function f(){if(x){a.b=1}}", "function f(){x&&(a.b=1)}"); + test("function f(){if(x){a.b*=1}}", "function f(){x&&(a.b*=1)}"); + test("function f(){if(x){a.b+=1}}", "function f(){x&&(a.b+=1)}"); + test("function f(){if(x){++a.b}}", "function f(){x&&++a.b}"); + test("function f(){if(x){a.foo()}}", "function f(){x&&a.foo()}"); + test("function f(){if(x){a?.foo()}}", "function f(){x&&a?.foo()}"); // Try it out with throw/catch/finally [which should not change] - fold_same("function f(){try{foo()}catch(e){bar(e)}finally{baz()}}"); + test_same("function f(){try{foo()}catch(e){bar(e)}finally{baz()}}"); // Try it out with switch statements - fold_same("function f(){switch(x){case 1:break}}"); + test_same("function f(){switch(x){case 1:break}}"); // Do while loops stay in a block if that's where they started - fold_same("function f(){if(e1){do foo();while(e2)}else foo2()}"); + test( + "function f(){if(e1){do foo();while(e2)}else foo2()}", + "function f() { if (e1) do foo(); while (e2); else foo2(); }", + ); // Test an obscure case with do and while - // fold("if(x){do{foo()}while(y)}else bar()", "if(x){do foo();while(y)}else bar()"); + // test("if(x){do{foo()}while(y)}else bar()", "if(x){do foo();while(y)}else bar()"); // Play with nested IFs - fold("function f(){if(x){if(y)foo()}}", "function f(){x && (y && foo())}"); - fold("function f(){if(x){if(y)foo();else bar()}}", "function f(){x&&(y?foo():bar())}"); - fold("function f(){if(x){if(y)foo()}else bar()}", "function f(){x?y&&foo():bar()}"); - fold( + test("function f(){if(x){if(y)foo()}}", "function f(){x && (y && foo())}"); + test("function f(){if(x){if(y)foo();else bar()}}", "function f(){x&&(y?foo():bar())}"); + test("function f(){if(x){if(y)foo()}else bar()}", "function f(){x?y&&foo():bar()}"); + test( "function f(){if(x){if(y)foo();else bar()}else{baz()}}", "function f(){x?y?foo():bar():baz()}", ); - // fold("if(e1){while(e2){if(e3){foo()}}}else{bar()}", "if(e1)while(e2)e3&&foo();else bar()"); + // test("if(e1){while(e2){if(e3){foo()}}}else{bar()}", "if(e1)while(e2)e3&&foo();else bar()"); - // fold("if(e1){with(e2){if(e3){foo()}}}else{bar()}", "if(e1)with(e2)e3&&foo();else bar()"); + // test("if(e1){with(e2){if(e3){foo()}}}else{bar()}", "if(e1)with(e2)e3&&foo();else bar()"); - // fold("if(a||b){if(c||d){var x;}}", "if(a||b)if(c||d)var x"); - // fold("if(x){ if(y){var x;}else{var z;} }", "if(x)if(y)var x;else var z"); + // test("if(a||b){if(c||d){var x;}}", "if(a||b)if(c||d)var x"); + // test("if(x){ if(y){var x;}else{var z;} }", "if(x)if(y)var x;else var z"); // NOTE - technically we can remove the blocks since both the parent // and child have elses. But we don't since it causes ambiguities in // some cases where not all descendent ifs having elses - // fold( + // test( // "if(x){ if(y){var x;}else{var z;} }else{var w}", // "if(x)if(y)var x;else var z;else var w", // ); - // fold("if (x) {var x;}else { if (y) { var y;} }", "if(x)var x;else if(y)var y"); + // test("if (x) {var x;}else { if (y) { var y;} }", "if(x)var x;else if(y)var y"); // Here's some of the ambiguous cases - // fold( + // test( // "if(a){if(b){f1();f2();}else if(c){f3();}}else {if(d){f4();}}", // "if(a)if(b){f1();f2()}else c&&f3();else d&&f4()", // ); - fold_same("function f(){foo()}"); - fold_same("switch(x){case y: foo()}"); - fold_same("try{foo()}catch(ex){bar()}finally{baz()}"); + test_same("function f(){foo()}"); + test_same("switch(x){case y: foo()}"); + test( + "try{foo()}catch(ex){bar()}finally{baz()}", + "try { foo(); } catch { bar(); } finally { baz(); }", + ); // Dot not fold `let` and `const`. // Lexical declaration cannot appear in a single-statement context. - fold_same("if (foo) { const bar = 1 } else { const baz = 1 }"); - fold_same("if (foo) { let bar = 1 } else { let baz = 1 }"); - // fold( + test_same("if (foo) { const bar = 1 } else { const baz = 1 }"); + test_same("if (foo) { let bar = 1 } else { let baz = 1 }"); + // test( // "if (foo) { var bar = 1 } else { var baz = 1 }", // "if (foo) var bar = 1; else var baz = 1;", // ); @@ -967,32 +951,35 @@ mod test { #[test] fn test_fold_returns() { - fold("function f(){if(x)return 1;else return 2}", "function f(){return x?1:2}"); - fold("function f(){if(x)return 1;return 2}", "function f(){return x?1:2}"); - fold("function f(){if(x)return;return 2}", "function f(){return x?void 0:2}"); - fold("function f(){if(x)return 1+x;else return 2-x}", "function f(){return x?1+x:2-x}"); - fold("function f(){if(x)return 1+x;return 2-x}", "function f(){return x?1+x:2-x}"); - fold( + test("function f(){if(x)return 1;else return 2}", "function f(){return x?1:2}"); + test("function f(){if(x)return 1;return 2}", "function f(){return x?1:2}"); + test("function f(){if(x)return;return 2}", "function f(){return x?void 0:2}"); + test("function f(){if(x)return 1+x;else return 2-x}", "function f(){return x?1+x:2-x}"); + test("function f(){if(x)return 1+x;return 2-x}", "function f(){return x?1+x:2-x}"); + test( "function f(){if(x)return y += 1;else return y += 2}", "function f(){return x?(y+=1):(y+=2)}", ); - fold("function f(){if(x)return;else return 2-x}", "function f(){return x?void 0:2-x}"); - fold("function f(){if(x)return;return 2-x}", "function f(){return x?void 0:2-x}"); - fold("function f(){if(x)return x;else return}", "function f(){if(x)return x;return;}"); - fold("function f(){if(x)return x;return}", "function f(){if(x)return x;return}"); + test("function f(){if(x)return;else return 2-x}", "function f(){return x?void 0:2-x}"); + test("function f(){if(x)return;return 2-x}", "function f(){return x?void 0:2-x}"); + test("function f(){if(x)return x;else return}", "function f(){if(x)return x;}"); + test("function f(){if(x)return x;return}", "function f(){if(x)return x}"); - fold_same("function f(){for(var x in y) { return x.y; } return k}"); + test( + "function f(){for(var x in y) { return x.y; } return k}", + "function f() { for (var x in y) return x.y; return k; }", + ); } #[test] #[ignore] fn test_combine_ifs1() { - fold( + test( "function f() {if (x) return 1; if (y) return 1}", "function f() {if (x||y) return 1;}", ); - fold( + test( "function f() {if (x) return 1; if (y) foo(); else return 1}", "function f() {if ((!x)&&y) foo(); else return 1;}", ); @@ -1002,12 +989,12 @@ mod test { #[ignore] fn test_combine_ifs2() { // combinable but not yet done - fold_same("function f() {if (x) throw 1; if (y) throw 1}"); + test_same("function f() {if (x) throw 1; if (y) throw 1}"); // Can't combine, side-effect - fold("function f(){ if (x) g(); if (y) g() }", "function f(){ x&&g(); y&&g() }"); - fold("function f(){ if (x) g?.(); if (y) g?.() }", "function f(){ x&&g?.(); y&&g?.() }"); + test("function f(){ if (x) g(); if (y) g() }", "function f(){ x&&g(); y&&g() }"); + test("function f(){ if (x) g?.(); if (y) g?.() }", "function f(){ x&&g?.(); y&&g?.() }"); // Can't combine, side-effect - fold( + test( "function f(){ if (x) y = 0; if (y) y = 0; }", "function f(){ x&&(y = 0); y&&(y = 0); }", ); @@ -1016,51 +1003,50 @@ mod test { #[test] #[ignore] fn test_combine_ifs3() { - fold_same("function f() {if (x) return 1; if (y) {g();f()}}"); + test_same("function f() {if (x) return 1; if (y) {g();f()}}"); } /** Try to minimize assignments */ #[test] #[ignore] fn test_fold_assignments() { - fold("function f(){if(x)y=3;else y=4;}", "function f(){y=x?3:4}"); - fold("function f(){if(x)y=1+a;else y=2+a;}", "function f(){y=x?1+a:2+a}"); + test("function f(){if(x)y=3;else y=4;}", "function f(){y=x?3:4}"); + test("function f(){if(x)y=1+a;else y=2+a;}", "function f(){y=x?1+a:2+a}"); // and operation assignments - fold("function f(){if(x)y+=1;else y+=2;}", "function f(){y+=x?1:2}"); - fold("function f(){if(x)y-=1;else y-=2;}", "function f(){y-=x?1:2}"); - fold("function f(){if(x)y%=1;else y%=2;}", "function f(){y%=x?1:2}"); - fold("function f(){if(x)y|=1;else y|=2;}", "function f(){y|=x?1:2}"); + test("function f(){if(x)y+=1;else y+=2;}", "function f(){y+=x?1:2}"); + test("function f(){if(x)y-=1;else y-=2;}", "function f(){y-=x?1:2}"); + test("function f(){if(x)y%=1;else y%=2;}", "function f(){y%=x?1:2}"); + test("function f(){if(x)y|=1;else y|=2;}", "function f(){y|=x?1:2}"); // Don't fold if the 2 ops don't match. - fold_same("function f(){x ? y-=1 : y+=2}"); + test_same("function f(){x ? y-=1 : y+=2}"); // Don't fold if the 2 LHS don't match. - fold_same("function f(){x ? y-=1 : z-=1}"); + test_same("function f(){x ? y-=1 : z-=1}"); // Don't fold if there are potential effects. - fold_same("function f(){x ? y().a=3 : y().a=4}"); + test_same("function f(){x ? y().a=3 : y().a=4}"); } #[test] #[ignore] fn test_remove_duplicate_statements() { - // enableNormalize(); // TODO(bradfordcsmith): Stop normalizing the expected output or document why it is necessary. // enableNormalizeExpectedOutput(); - fold("if (a) { x = 1; x++ } else { x = 2; x++ }", "x=(a) ? 1 : 2; x++"); - fold( + test("if (a) { x = 1; x++ } else { x = 2; x++ }", "x=(a) ? 1 : 2; x++"); + test( concat!( "if (a) { x = 1; x++; y += 1; z = pi; }", " else { x = 2; x++; y += 1; z = pi; }" ), "x=(a) ? 1 : 2; x++; y += 1; z = pi;", ); - fold( + test( concat!("function z() {", "if (a) { foo(); return !0 } else { goo(); return !0 }", "}"), "function z() {(a) ? foo() : goo(); return !0}", ); - fold( + test( concat!( "function z() {if (a) { foo(); x = true; return true ", "} else { goo(); x = true; return true }}" @@ -1068,7 +1054,7 @@ mod test { "function z() {(a) ? foo() : goo(); x = true; return true}", ); - fold( + test( concat!( "function z() {", " if (a) { bar(); foo(); return true }", @@ -1087,12 +1073,10 @@ mod test { #[test] fn test_fold_returns_integration2() { - // late = true; - // disableNormalize(); - // if-then-else duplicate statement removal handles this case: - test_same( + test( "function test(a) {if (a) {const a = Math.random();if(a) {return a;}} return a; }", + "function test(a) { if (a) { const a = Math.random(); if (a) return a; } return a; }", ); } @@ -1108,20 +1092,20 @@ mod test { #[test] fn test_not_cond() { - fold("function f(){if(!x)foo()}", "function f(){x||foo()}"); - fold("function f(){if(!x)b=1}", "function f(){x||(b=1)}"); - // fold("if(!x)z=1;else if(y)z=2", "x ? y&&(z=2) : z=1;"); - // fold("if(x)y&&(z=2);else z=1;", "x ? y&&(z=2) : z=1"); - fold("function f(){if(!(x=1))a.b=1}", "function f(){(x=1)||(a.b=1)}"); + test("function f(){if(!x)foo()}", "function f(){x||foo()}"); + test("function f(){if(!x)b=1}", "function f(){x||(b=1)}"); + // test("if(!x)z=1;else if(y)z=2", "x ? y&&(z=2) : z=1;"); + // test("if(x)y&&(z=2);else z=1;", "x ? y&&(z=2) : z=1"); + test("function f(){if(!(x=1))a.b=1}", "function f(){(x=1)||(a.b=1)}"); } #[test] #[ignore] fn test_and_parentheses_count() { - fold("function f(){if(x||y)a.foo()}", "function f(){(x||y)&&a.foo()}"); - fold("function f(){if(x.a)x.a=0}", "function f(){x.a&&(x.a=0)}"); - fold("function f(){if(x?.a)x.a=0}", "function f(){x?.a&&(x.a=0)}"); - fold_same("function f(){if(x()||y()){x()||y()}}"); + test("function f(){if(x||y)a.foo()}", "function f(){(x||y)&&a.foo()}"); + test("function f(){if(x.a)x.a=0}", "function f(){x.a&&(x.a=0)}"); + test("function f(){if(x?.a)x.a=0}", "function f(){x?.a&&(x.a=0)}"); + test_same("function f(){if(x()||y()){x()||y()}}"); } #[test] @@ -1129,62 +1113,62 @@ mod test { fn test_fold_logical_op_string_compare() { // side-effects // There is two way to parse two &&'s and both are correct. - fold("if (foo() && false) z()", "(foo(), 0) && z()"); + test("if (foo() && false) z()", "(foo(), 0) && z()"); } #[test] #[ignore] fn test_fold_not() { - fold("while(!(x==y)){a=b;}", "while(x!=y){a=b;}"); - fold("while(!(x!=y)){a=b;}", "while(x==y){a=b;}"); - fold("while(!(x===y)){a=b;}", "while(x!==y){a=b;}"); - fold("while(!(x!==y)){a=b;}", "while(x===y){a=b;}"); + test("while(!(x==y)){a=b;}", "while(x!=y){a=b;}"); + test("while(!(x!=y)){a=b;}", "while(x==y){a=b;}"); + test("while(!(x===y)){a=b;}", "while(x!==y){a=b;}"); + test("while(!(x!==y)){a=b;}", "while(x===y){a=b;}"); // Because !(x=NaN don't fold < and > cases. - fold_same("while(!(x>y)){a=b;}"); - fold_same("while(!(x>=y)){a=b;}"); - fold_same("while(!(xy)){a=b;}"); + test_same("while(!(x>=y)){a=b;}"); + test_same("while(!(x !!x;"); } @@ -1193,69 +1177,69 @@ mod test { #[ignore] fn test_minimize_while_condition() { // This test uses constant folding logic, so is only here for completeness. - fold("while(!!true) foo()", "while(1) foo()"); + test("while(!!true) foo()", "while(1) foo()"); // These test tryMinimizeCondition - fold("while(!!x) foo()", "while(x) foo()"); - fold("while(!(!x&&!y)) foo()", "while(x||y) foo()"); - fold("while(x||!!y) foo()", "while(x||y) foo()"); - fold("while(!(!!x&&y)) foo()", "while(!x||!y) foo()"); - fold("while(!(!x&&y)) foo()", "while(x||!y) foo()"); - fold("while(!(x||!y)) foo()", "while(!x&&y) foo()"); - fold("while(!(x||y)) foo()", "while(!x&&!y) foo()"); - fold("while(!(!x||y-z)) foo()", "while(x&&!(y-z)) foo()"); - fold("while(!(!(x/y)||z+w)) foo()", "while(x/y&&!(z+w)) foo()"); - fold_same("while(!(x+y||z)) foo()"); - fold_same("while(!(x&&y*z)) foo()"); - fold("while(!(!!x&&y)) foo()", "while(!x||!y) foo()"); - fold("while(x&&!0) foo()", "while(x) foo()"); - fold("while(x||!1) foo()", "while(x) foo()"); - fold("while(!((x,y)&&z)) foo()", "while((x,!y)||!z) foo()"); + test("while(!!x) foo()", "while(x) foo()"); + test("while(!(!x&&!y)) foo()", "while(x||y) foo()"); + test("while(x||!!y) foo()", "while(x||y) foo()"); + test("while(!(!!x&&y)) foo()", "while(!x||!y) foo()"); + test("while(!(!x&&y)) foo()", "while(x||!y) foo()"); + test("while(!(x||!y)) foo()", "while(!x&&y) foo()"); + test("while(!(x||y)) foo()", "while(!x&&!y) foo()"); + test("while(!(!x||y-z)) foo()", "while(x&&!(y-z)) foo()"); + test("while(!(!(x/y)||z+w)) foo()", "while(x/y&&!(z+w)) foo()"); + test_same("while(!(x+y||z)) foo()"); + test_same("while(!(x&&y*z)) foo()"); + test("while(!(!!x&&y)) foo()", "while(!x||!y) foo()"); + test("while(x&&!0) foo()", "while(x) foo()"); + test("while(x||!1) foo()", "while(x) foo()"); + test("while(!((x,y)&&z)) foo()", "while((x,!y)||!z) foo()"); } #[test] #[ignore] fn test_minimize_demorgan_remove_leading_not() { - fold("if(!(!a||!b)&&c) foo()", "((a&&b)&&c)&&foo()"); - fold("if(!(x&&y)) foo()", "x&&y||foo()"); - fold("if(!(x||y)) foo()", "(x||y)||foo()"); + test("if(!(!a||!b)&&c) foo()", "((a&&b)&&c)&&foo()"); + test("if(!(x&&y)) foo()", "x&&y||foo()"); + test("if(!(x||y)) foo()", "(x||y)||foo()"); } #[test] #[ignore] fn test_minimize_demorgan1() { - fold("if(!a&&!b)foo()", "(a||b)||foo()"); + test("if(!a&&!b)foo()", "(a||b)||foo()"); } #[test] #[ignore] fn test_minimize_demorgan2() { // Make sure trees with cloned functions are marked as changed - fold("(!(a&&!((function(){})())))||foo()", "!a||(function(){})()||foo()"); + test("(!(a&&!((function(){})())))||foo()", "!a||(function(){})()||foo()"); } #[test] #[ignore] fn test_minimize_demorgan2b() { // Make sure unchanged trees with functions are not marked as changed - fold_same("!a||(function(){})()||foo()"); + test_same("!a||(function(){})()||foo()"); } #[test] #[ignore] fn test_minimize_demorgan3() { - fold("if((!a||!b)&&(c||d)) foo()", "(a&&b||!c&&!d)||foo()"); + test("if((!a||!b)&&(c||d)) foo()", "(a&&b||!c&&!d)||foo()"); } #[test] #[ignore] fn test_minimize_demorgan5() { - fold("if((!a||!b)&&c) foo()", "(a&&b||!c)||foo()"); + test("if((!a||!b)&&c) foo()", "(a&&b||!c)||foo()"); } #[test] #[ignore] fn test_minimize_demorgan11() { - fold( + test( "if (x && (y===2 || !f()) && (y===3 || !h())) foo()", "(!x || y!==2 && f() || y!==3 && h()) || foo()", ); @@ -1264,7 +1248,7 @@ mod test { #[test] #[ignore] fn test_minimize_demorgan20a() { - fold( + test( "if (0===c && (2===a || 1===a)) f(); else g()", "if (0!==c || 2!==a && 1!==a) g(); else f()", ); @@ -1273,146 +1257,141 @@ mod test { #[test] #[ignore] fn test_minimize_demorgan20b() { - fold("if (0!==c || 2!==a && 1!==a) g(); else f()", "(0!==c || 2!==a && 1!==a) ? g() : f()"); + test("if (0!==c || 2!==a && 1!==a) g(); else f()", "(0!==c || 2!==a && 1!==a) ? g() : f()"); } #[test] fn test_preserve_if() { - fold_same("if(!a&&!b)for(;f(););"); + test_same("if(!a&&!b)for(;f(););"); } #[test] fn test_no_swap_with_dangling_else() { - fold_same("if(!x) {for(;;)foo(); for(;;)bar()} else if(y) for(;;) f()"); - fold_same("if(!a&&!b) {for(;;)foo(); for(;;)bar()} else if(y) for(;;) f()"); + test_same("if(!x) {for(;;)foo(); for(;;)bar()} else if(y) for(;;) f()"); + test_same("if(!a&&!b) {for(;;)foo(); for(;;)bar()} else if(y) for(;;) f()"); } #[test] fn test_minimize_hook() { - fold("x ? x : y", "x || y"); - fold_same("x.y ? x.y : x.z"); - fold_same("x?.y ? x?.y : x.z"); - fold_same("x?.y ? x?.y : x?.z"); + test("x ? x : y", "x || y"); + test_same("x.y ? x.y : x.z"); + test_same("x?.y ? x?.y : x.z"); + test_same("x?.y ? x?.y : x?.z"); - fold_same("x() ? x() : y()"); - fold_same("x?.() ? x?.() : y()"); + test_same("x() ? x() : y()"); + test_same("x?.() ? x?.() : y()"); - fold("!x ? foo() : bar()", "x ? bar() : foo()"); + test("!x ? foo() : bar()", "x ? bar() : foo()"); // TODO - // fold("while(!(x ? y : z)) foo();", "while(x ? !y : !z) foo();"); - // fold("(x ? !y : !z) ? foo() : bar()", "(x ? y : z) ? bar() : foo()"); + // test("while(!(x ? y : z)) foo();", "while(x ? !y : !z) foo();"); + // test("(x ? !y : !z) ? foo() : bar()", "(x ? y : z) ? bar() : foo()"); } #[test] #[ignore] fn test_minimize_comma() { - fold("while(!(inc(), test())) foo();", "while(inc(), !test()) foo();"); - fold("(inc(), !test()) ? foo() : bar()", "(inc(), test()) ? bar() : foo()"); + test("while(!(inc(), test())) foo();", "while(inc(), !test()) foo();"); + test("(inc(), !test()) ? foo() : bar()", "(inc(), test()) ? bar() : foo()"); } #[test] #[ignore] fn test_minimize_expr_result() { - fold("!x||!y", "x&&y"); - fold("if(!(x&&!y)) foo()", "(!x||y)&&foo()"); - fold("if(!x||y) foo()", "(!x||y)&&foo()"); - fold("(!x||y)&&foo()", "x&&!y||!foo()"); + test("!x||!y", "x&&y"); + test("if(!(x&&!y)) foo()", "(!x||y)&&foo()"); + test("if(!x||y) foo()", "(!x||y)&&foo()"); + test("(!x||y)&&foo()", "x&&!y||!foo()"); } #[test] #[ignore] fn test_minimize_demorgan21() { - fold("if (0===c && (2===a || 1===a)) f()", "(0!==c || 2!==a && 1!==a) || f()"); + test("if (0===c && (2===a || 1===a)) f()", "(0!==c || 2!==a && 1!==a) || f()"); } #[test] #[ignore] fn test_minimize_and_or1() { - fold("if ((!a || !b) && (d || e)) f()", "(a&&b || !d&&!e) || f()"); + test("if ((!a || !b) && (d || e)) f()", "(a&&b || !d&&!e) || f()"); } #[test] fn test_minimize_for_condition() { // This test uses constant folding logic, so is only here for completeness. // These could be simplified to "for(;;) ..." - fold("for(;!!true;) foo()", "for(;true;) foo()"); + test("for(;!!true;) foo()", "for(;;) foo()"); // Verify function deletion tracking. - // fold("if(!!true||function(){}) {}", "if(1) {}"); + // test("if(!!true||function(){}) {}", "if(1) {}"); // Don't bother with FOR inits as there are normalized out. - fold("for(!!true;;) foo()", "for(true;;) foo()"); + test("for(!!true;;) foo()", "for(!0;;) foo()"); // These test tryMinimizeCondition - fold("for(;!!x;) foo()", "for(;x;) foo()"); + test("for(;!!x;) foo()", "for(;x;) foo()"); - fold_same("for(a in b) foo()"); - fold_same("for(a in {}) foo()"); - fold_same("for(a in []) foo()"); - fold("for(a in !!true) foo()", "for(a in true) foo()"); + test_same("for(a in b) foo()"); + test_same("for(a in {}) foo()"); + test_same("for(a in []) foo()"); + test("for(a in !!true) foo()", "for(a in !0) foo()"); - fold_same("for(a of b) foo()"); - fold_same("for(a of {}) foo()"); - fold_same("for(a of []) foo()"); - fold("for(a of !!true) foo()", "for(a of true) foo()"); + test_same("for(a of b) foo()"); + test_same("for(a of {}) foo()"); + test_same("for(a of []) foo()"); + test("for(a of !!true) foo()", "for(a of !0) foo()"); } #[test] fn test_minimize_condition_example1() { // Based on a real failing code sample. - fold("if(!!(f() > 20)) {foo();foo()}", "if(f() > 20){foo();foo()}"); + test("if(!!(f() > 20)) {foo();foo()}", "f() > 20 && (foo(), foo())"); } #[test] #[ignore] fn test_fold_loop_break_late() { - // late = true; - fold("for(;;) if (a) break", "for(;!a;);"); - fold_same("for(;;) if (a) { f(); break }"); - fold("for(;;) if (a) break; else f()", "for(;!a;) { { f(); } }"); - fold("for(;a;) if (b) break", "for(;a && !b;);"); - fold("for(;a;) { if (b) break; if (c) break; }", "for(;(a && !b);) if (c) break;"); - fold("for(;(a && !b);) if (c) break;", "for(;(a && !b) && !c;);"); - fold("for(;;) { if (foo) { break; var x; } } x;", "var x; for(;!foo;) {} x;"); + test("for(;;) if (a) break", "for(;!a;);"); + test_same("for(;;) if (a) { f(); break }"); + test("for(;;) if (a) break; else f()", "for(;!a;) { { f(); } }"); + test("for(;a;) if (b) break", "for(;a && !b;);"); + test("for(;a;) { if (b) break; if (c) break; }", "for(;(a && !b);) if (c) break;"); + test("for(;(a && !b);) if (c) break;", "for(;(a && !b) && !c;);"); + test("for(;;) { if (foo) { break; var x; } } x;", "var x; for(;!foo;) {} x;"); // 'while' is normalized to 'for' - // enableNormalize(); - fold("while(true) if (a) break", "for(;1&&!a;);"); - // disableNormalize(); + test("while(true) if (a) break", "for(;1&&!a;);"); } #[test] #[ignore] fn test_fold_loop_break_early() { - // late = false; - fold_same("for(;;) if (a) break"); - fold_same("for(;;) if (a) { f(); break }"); - fold_same("for(;;) if (a) break; else f()"); - fold_same("for(;a;) if (b) break"); - fold_same("for(;a;) { if (b) break; if (c) break; }"); + test_same("for(;;) if (a) break"); + test_same("for(;;) if (a) { f(); break }"); + test_same("for(;;) if (a) break; else f()"); + test_same("for(;a;) if (b) break"); + test_same("for(;a;) { if (b) break; if (c) break; }"); - fold_same("while(1) if (a) break"); - // enableNormalize(); - fold_same("for (; 1; ) if (a) break"); + test_same("while(1) if (a) break"); + test_same("for (; 1; ) if (a) break"); } #[test] #[ignore] fn test_fold_conditional_var_declaration() { - fold("if(x) var y=1;else y=2", "var y=x?1:2"); - fold("if(x) y=1;else var y=2", "var y=x?1:2"); + test("if(x) var y=1;else y=2", "var y=x?1:2"); + test("if(x) y=1;else var y=2", "var y=x?1:2"); - fold_same("if(x) var y = 1; z = 2"); - fold_same("if(x||y) y = 1; var z = 2"); + test_same("if(x) var y = 1; z = 2"); + test_same("if(x||y) y = 1; var z = 2"); - fold_same("if(x) { var y = 1; print(y)} else y = 2 "); - fold_same("if(x) var y = 1; else {y = 2; print(y)}"); + test_same("if(x) { var y = 1; print(y)} else y = 2 "); + test_same("if(x) var y = 1; else {y = 2; print(y)}"); } #[test] #[ignore] fn test_fold_if_with_lower_operators_inside() { - fold("if (x + (y=5)) z && (w,z);", "x + (y=5) && (z && (w,z))"); - fold("if (!(x+(y=5))) z && (w,z);", "x + (y=5) || z && (w,z)"); - fold( + test("if (x + (y=5)) z && (w,z);", "x + (y=5) && (z && (w,z))"); + test("if (!(x+(y=5))) z && (w,z);", "x + (y=5) || z && (w,z)"); + test( "if (x + (y=5)) if (z && (w,z)) for(;;) foo();", "if (x + (y=5) && (z && (w,z))) for(;;) foo();", ); @@ -1421,71 +1400,69 @@ mod test { #[test] #[ignore] fn test_substitute_return() { - // late = false; - // enableNormalize(); // TODO(bradfordcsmith): Stop normalizing the expected output or document why it is necessary. // enableNormalizeExpectedOutput(); - fold("function f() { while(x) { return }}", "function f() { while(x) { break }}"); + test("function f() { while(x) { return }}", "function f() { while(x) { break }}"); - fold_same("function f() { while(x) { return 5 } }"); + test_same("function f() { while(x) { return 5 } }"); - fold_same("function f() { a: { return 5 } }"); + test_same("function f() { a: { return 5 } }"); - fold( + test( "function f() { while(x) { return 5} return 5}", "function f() { while(x) { break } return 5}", ); - fold( + test( "function f() { while(x) { return x} return x}", "function f() { while(x) { break } return x}", ); - fold( + test( "function f() { while(x) { if (y) { return }}}", "function f() { while(x) { if (y) { break }}}", ); - fold( + test( "function f() { while(x) { if (y) { return }} return}", "function f() { while(x) { if (y) { break }}}", ); - fold( + test( "function f() { while(x) { if (y) { return 5 }} return 5}", "function f() { while(x) { if (y) { break }} return 5}", ); // It doesn't matter if x is changed between them. We are still returning // x at whatever x value current holds. The whole x = 1 is skipped. - fold( + test( "function f() { while(x) { if (y) { return x } x = 1} return x}", "function f() { while(x) { if (y) { break } x = 1} return x}", ); - fold( + test( "function f() { while(x) { if (y) { return x } return x} return x}", "function f() { while(x) { if (y) {} break }return x}", ); // A break here only breaks out of the inner loop. - fold_same("function f() { while(x) { while (y) { return } } }"); + test_same("function f() { while(x) { while (y) { return } } }"); - fold_same("function f() { while(1) { return 7} return 5}"); + test_same("function f() { while(1) { return 7} return 5}"); - fold_same(concat!( + test_same(concat!( "function f() {", " try { while(x) {return f()}} catch (e) { } return f()}" )); - fold_same(concat!( + test_same(concat!( "function f() {", " try { while(x) {return f()}} finally {alert(1)} return f()}" )); // Both returns has the same handler - fold( + test( concat!( "function f() {", " try { while(x) { return f() } return f() } catch (e) { } }" @@ -1494,14 +1471,14 @@ mod test { ); // We can't fold this because it'll change the order of when foo is called. - fold_same(concat!( + test_same(concat!( "function f() {", " try { while(x) { return foo() } } finally { alert(1) } ", " return foo()}" )); // This is fine, we have no side effect in the return value. - fold( + test( concat!( "function f() {", " try { while(x) { return 1 } } finally { alert(1) } return 1}" @@ -1512,9 +1489,9 @@ mod test { ), ); - fold_same("function f() { try{ return a } finally { a = 2 } return a; }"); + test_same("function f() { try{ return a } finally { a = 2 } return a; }"); - fold( + test( "function f() { switch(a){ case 1: return a; default: g();} return a;}", "function f() { switch(a){ case 1: break; default: g();} return a; }", ); @@ -1523,88 +1500,86 @@ mod test { #[test] #[ignore] fn test_substitute_break_for_throw() { - // late = false; - // enableNormalize(); // TODO(bradfordcsmith): Stop normalizing the expected output or document why it is necessary. // enableNormalizeExpectedOutput(); - fold_same("function f() { while(x) { throw Error }}"); + test_same("function f() { while(x) { throw Error }}"); - fold( + test( "function f() { while(x) { throw Error } throw Error }", "function f() { while(x) { break } throw Error}", ); - fold_same("function f() { while(x) { throw Error(1) } throw Error(2)}"); - fold_same("function f() { while(x) { throw Error(1) } return Error(2)}"); + test_same("function f() { while(x) { throw Error(1) } throw Error(2)}"); + test_same("function f() { while(x) { throw Error(1) } return Error(2)}"); - fold_same("function f() { while(x) { throw 5 } }"); + test_same("function f() { while(x) { throw 5 } }"); - fold_same("function f() { a: { throw 5 } }"); + test_same("function f() { a: { throw 5 } }"); - fold( + test( "function f() { while(x) { throw 5} throw 5}", "function f() { while(x) { break } throw 5}", ); - fold( + test( "function f() { while(x) { throw x} throw x}", "function f() { while(x) { break } throw x}", ); - fold_same("function f() { while(x) { if (y) { throw Error }}}"); + test_same("function f() { while(x) { if (y) { throw Error }}}"); - fold( + test( "function f() { while(x) { if (y) { throw Error }} throw Error}", "function f() { while(x) { if (y) { break }} throw Error}", ); - fold( + test( "function f() { while(x) { if (y) { throw 5 }} throw 5}", "function f() { while(x) { if (y) { break }} throw 5}", ); // It doesn't matter if x is changed between them. We are still throwing // x at whatever x value current holds. The whole x = 1 is skipped. - fold( + test( "function f() { while(x) { if (y) { throw x } x = 1} throw x}", "function f() { while(x) { if (y) { break } x = 1} throw x}", ); - fold( + test( "function f() { while(x) { if (y) { throw x } throw x} throw x}", "function f() { while(x) { if (y) {} break }throw x}", ); // A break here only breaks out of the inner loop. - fold_same("function f() { while(x) { while (y) { throw Error } } }"); + test_same("function f() { while(x) { while (y) { throw Error } } }"); - fold_same("function f() { while(1) { throw 7} throw 5}"); + test_same("function f() { while(1) { throw 7} throw 5}"); - fold_same(concat!( + test_same(concat!( "function f() {", " try { while(x) {throw f()}} catch (e) { } throw f()}" )); - fold_same(concat!( + test_same(concat!( "function f() {", " try { while(x) {throw f()}} finally {alert(1)} throw f()}" )); // Both throws has the same handler - fold( + test( concat!("function f() {", " try { while(x) { throw f() } throw f() } catch (e) { } }"), concat!("function f() {", " try { while(x) { break } throw f() } catch (e) { } }"), ); // We can't fold this because it'll change the order of when foo is called. - fold_same(concat!( + test_same(concat!( "function f() {", " try { while(x) { throw foo() } } finally { alert(1) } ", " throw foo()}" )); // This is fine, we have no side effect in the throw value. - fold( + test( concat!( "function f() {", " try { while(x) { throw 1 } } finally { alert(1) } throw 1}" @@ -1615,9 +1590,9 @@ mod test { ), ); - fold_same("function f() { try{ throw a } finally { a = 2 } throw a; }"); + test_same("function f() { try{ throw a } finally { a = 2 } throw a; }"); - fold( + test( "function f() { switch(a){ case 1: throw a; default: g();} throw a;}", "function f() { switch(a){ case 1: break; default: g();} throw a; }", ); @@ -1626,33 +1601,30 @@ mod test { #[test] #[ignore] fn test_remove_duplicate_return() { - // late = false; - // enableNormalize(); - - fold("function f() { return; }", "function f(){}"); - fold_same("function f() { return a; }"); - fold( + test("function f() { return; }", "function f(){}"); + test_same("function f() { return a; }"); + test( "function f() { if (x) { return a } return a; }", "function f() { if (x) {} return a; }", ); - fold_same("function f() { try { if (x) { return a } } catch(e) {} return a; }"); - fold_same("function f() { try { if (x) {} } catch(e) {} return 1; }"); + test_same("function f() { try { if (x) { return a } } catch(e) {} return a; }"); + test_same("function f() { try { if (x) {} } catch(e) {} return 1; }"); // finally clauses may have side effects - fold_same("function f() { try { if (x) { return a } } finally { a++ } return a; }"); + test_same("function f() { try { if (x) { return a } } finally { a++ } return a; }"); // but they don't matter if the result doesn't have side effects and can't // be affect by side-effects. - fold( + test( "function f() { try { if (x) { return 1 } } finally {} return 1; }", "function f() { try { if (x) {} } finally {} return 1; }", ); - fold( + test( "function f() { switch(a){ case 1: return a; } return a; }", "function f() { switch(a){ case 1: } return a; }", ); - fold( + test( concat!( "function f() { switch(a){ ", " case 1: return a; case 2: return a; } return a; }" @@ -1664,35 +1636,32 @@ mod test { #[test] #[ignore] fn test_remove_duplicate_throw() { - // late = false; - // enableNormalize(); - - fold_same("function f() { throw a; }"); - fold("function f() { if (x) { throw a } throw a; }", "function f() { if (x) {} throw a; }"); - fold_same("function f() { try { if (x) {throw a} } catch(e) {} throw a; }"); - fold_same("function f() { try { if (x) {throw 1} } catch(e) {f()} throw 1; }"); - fold_same("function f() { try { if (x) {throw 1} } catch(e) {f()} throw 1; }"); - fold_same("function f() { try { if (x) {throw 1} } catch(e) {throw 1}}"); - fold( + test_same("function f() { throw a; }"); + test("function f() { if (x) { throw a } throw a; }", "function f() { if (x) {} throw a; }"); + test_same("function f() { try { if (x) {throw a} } catch(e) {} throw a; }"); + test_same("function f() { try { if (x) {throw 1} } catch(e) {f()} throw 1; }"); + test_same("function f() { try { if (x) {throw 1} } catch(e) {f()} throw 1; }"); + test_same("function f() { try { if (x) {throw 1} } catch(e) {throw 1}}"); + test( "function f() { try { if (x) {throw 1} } catch(e) {throw 1} throw 1; }", "function f() { try { if (x) {throw 1} } catch(e) {} throw 1; }", ); // finally clauses may have side effects - fold_same("function f() { try { if (x) { throw a } } finally { a++ } throw a; }"); + test_same("function f() { try { if (x) { throw a } } finally { a++ } throw a; }"); // but they don't matter if the result doesn't have side effects and can't // be affect by side-effects. - fold( + test( "function f() { try { if (x) { throw 1 } } finally {} throw 1; }", "function f() { try { if (x) {} } finally {} throw 1; }", ); - fold( + test( "function f() { switch(a){ case 1: throw a; } throw a; }", "function f() { switch(a){ case 1: } throw a; }", ); - fold( + test( concat!("function f() { switch(a){ ", "case 1: throw a; case 2: throw a; } throw a; }"), concat!("function f() { switch(a){ case 1: break; case 2: } throw a; }"), ); @@ -1701,29 +1670,29 @@ mod test { #[test] #[ignore] fn test_nested_if_combine() { - fold("if(x)if(y){while(1){}}", "if(x&&y){while(1){}}"); - fold("if(x||z)if(y){while(1){}}", "if((x||z)&&y){while(1){}}"); - fold("if(x)if(y||z){while(1){}}", "if((x)&&(y||z)){while(1){}}"); - fold_same("if(x||z)if(y||z){while(1){}}"); - fold("if(x)if(y){if(z){while(1){}}}", "if(x&&(y&&z)){while(1){}}"); + test("if(x)if(y){while(1){}}", "if(x&&y){while(1){}}"); + test("if(x||z)if(y){while(1){}}", "if((x||z)&&y){while(1){}}"); + test("if(x)if(y||z){while(1){}}", "if((x)&&(y||z)){while(1){}}"); + test_same("if(x||z)if(y||z){while(1){}}"); + test("if(x)if(y){if(z){while(1){}}}", "if(x&&(y&&z)){while(1){}}"); } // See: http://blickly.github.io/closure-compiler-issues/#291 #[test] #[ignore] fn test_issue291() { - fold("if (true) { f.onchange(); }", "if (1) f.onchange();"); - fold_same("if (f) { f.onchange(); }"); - fold_same("if (f) { f.bar(); } else { f.onchange(); }"); - fold("if (f) { f.bonchange(); }", "f && f.bonchange();"); - fold_same("if (f) { f['x'](); }"); + test("if (true) { f.onchange(); }", "if (1) f.onchange();"); + test_same("if (f) { f.onchange(); }"); + test_same("if (f) { f.bar(); } else { f.onchange(); }"); + test("if (f) { f.bonchange(); }", "f && f.bonchange();"); + test_same("if (f) { f['x'](); }"); // optional versions - fold("if (true) { f?.onchange(); }", "if (1) f?.onchange();"); - fold_same("if (f) { f?.onchange(); }"); - fold_same("if (f) { f?.bar(); } else { f?.onchange(); }"); - fold("if (f) { f?.bonchange(); }", "f && f?.bonchange();"); - fold_same("if (f) { f?.['x'](); }"); + test("if (true) { f?.onchange(); }", "if (1) f?.onchange();"); + test_same("if (f) { f?.onchange(); }"); + test_same("if (f) { f?.bar(); } else { f?.onchange(); }"); + test("if (f) { f?.bonchange(); }", "f && f?.bonchange();"); + test_same("if (f) { f?.['x'](); }"); } #[test] @@ -1768,14 +1737,20 @@ mod test { #[test] fn test_remove_else_cause3() { - test_same("function f() { a:{if (x) break a; else f() } }"); - test_same("function f() { if (x) { a:{ break a } } else f() }"); - test_same("function f() { if (x) a:{ break a } else f() }"); + test( + "function f() { a:{if (x) break a; else f() } }", + "function f() { a: if (x) break a; else f() }", + ); + test("function f() { if (x) { a:{ break a } } else f() }", "function f() { x || f() }"); + test("function f() { if (x) a:{ break a } else f() }", "function f() { x || f() }"); } #[test] fn test_remove_else_cause4() { - test_same("function f() { if (x) { if (y) { return 1; } } else f() }"); + test( + "function f() { if (x) { if (y) { return 1; } } else f() }", + "function f() { if (x) { if (y) return 1; } else f() }", + ); } #[test] @@ -1816,52 +1791,44 @@ mod test { #[test] fn test_coercion_substitution_disabled() { test_same("var x = {}; if (x != null) throw 'a';"); - test_same("var x = {}; var y = x != null;"); + test("var x = {}; var y = x != null;", "var x = {}, y = x != null;"); test_same("var x = 1; if (x != 0) throw 'a';"); - test_same("var x = 1; var y = x != 0;"); + test("var x = 1; var y = x != 0;", "var x = 1, y = x != 0;"); } #[test] fn test_coercion_substitution_boolean_result0() { - test_same("var x = {}; var y = x != null;"); + test("var x = {}; var y = x != null;", "var x = {}, y = x != null"); } #[test] fn test_coercion_substitution_boolean_result1() { - test_same("var x = {}; var y = x == null;"); - test_same("var x = {}; var y = x !== null;"); - test_same("var x = undefined; var y = x !== null;"); - test_same("var x = {}; var y = x === null;"); - test_same("var x = undefined; var y = x === null;"); + test("var x = {}; var y = x == null;", "var x = {}, y = x == null;"); + test("var x = {}; var y = x !== null;", "var x = {}, y = x !== null;"); + test("var x = undefined; var y = x !== null;", "var x = void 0, y = x !== null;"); + test("var x = {}; var y = x === null;", "var x = {}, y = x === null;"); + test("var x = undefined; var y = x === null;", "var x = void 0, y = x === null;"); - test_same("var x = 1; var y = x != 0;"); - test_same("var x = 1; var y = x == 0;"); - test_same("var x = 1; var y = x !== 0;"); - test_same("var x = 1; var y = x === 0;"); + test("var x = 1; var y = x != 0;", "var x = 1, y = x != 0;"); + test("var x = 1; var y = x == 0;", "var x = 1, y = x == 0;"); + test("var x = 1; var y = x !== 0;", "var x = 1, y = x !== 0;"); + test("var x = 1; var y = x === 0;", "var x = 1, y = x === 0;"); } #[test] fn test_coercion_substitution_if() { test("var x = {};\nif (x != null) throw 'a';\n", "var x={}; if (x!=null) throw 'a'"); test_same("var x = {};\nif (x == null) throw 'a';\n"); + test_same("var x = {};\nif (x != null) throw 'a';\n"); test_same("var x = {};\nif (x !== null) throw 'a';\n"); test_same("var x = {};\nif (x === null) throw 'a';\n"); - test_same("var x = {};\nif (null != x) throw 'a';\n"); - test_same("var x = {};\nif (null == x) throw 'a';\n"); - test_same("var x = {};\nif (null !== x) throw 'a';\n"); - test_same("var x = {};\nif (null === x) throw 'a';\n"); test_same("var x = 1;\nif (x != 0) throw 'a';\n"); test_same("var x = 1;\nif (x != 0) throw 'a';\n"); test_same("var x = 1;\nif (x == 0) throw 'a';\n"); test_same("var x = 1;\nif (x !== 0) throw 'a';\n"); test_same("var x = 1;\nif (x === 0) throw 'a';\n"); - test_same("var x = 1;\nif (0 != x) throw 'a';\n"); - test_same("var x = 1;\nif (0 == x) throw 'a';\n"); - test_same("var x = 1;\nif (0 !== x) throw 'a';\n"); - test_same("var x = 1;\nif (0 === x) throw 'a';\n"); - test_same("var x = NaN;\nif (0 === x) throw 'a';\n"); test_same("var x = NaN;\nif (x === 0) throw 'a';\n"); } @@ -1873,23 +1840,20 @@ mod test { #[test] fn test_coercion_substitution_hook() { - test("var x = {}; var y = x != null ? 1 : 2;", "var x = {}; var y = x == null ? 2 : 1;"); - test("var x = 1; var y = x != 0 ? 1 : 2;", "var x = 1; var y = x == 0 ? 2 : 1;"); + test("var x = {}; var y = x != null ? 1 : 2;", "var x = {}, y = x == null ? 2 : 1;"); + test("var x = 1; var y = x != 0 ? 1 : 2;", "var x = 1, y = x == 0 ? 2 : 1;"); } #[test] fn test_coercion_substitution_not() { - test("var x = {}; var y = !(x != null) ? 1 : 2;", "var x = {}; var y = x == null ? 1 : 2;"); - test("var x = 1; var y = !(x != 0) ? 1 : 2; ", "var x = 1; var y = x == 0 ? 1 : 2; "); + test("var x = {}; var y = !(x != null) ? 1 : 2;", "var x = {}, y = x == null ? 1 : 2;"); + test("var x = 1; var y = !(x != 0) ? 1 : 2; ", "var x = 1, y = x == 0 ? 1 : 2; "); } #[test] fn test_coercion_substitution_while() { - test( - "var x = {}; while (x != null) throw 'a';", - "var x = {}; for (;x != null;) throw 'a';", - ); - test("var x = 1; while (x != 0) throw 'a';", "var x = 1; for (;x != 0;) throw 'a';"); + test("var x = {}; while (x != null) throw 'a';", "for (var x = {} ;x != null;) throw 'a';"); + test("var x = 1; while (x != 0) throw 'a';", "for (var x = 1; x != 0;) throw 'a';"); } #[test] @@ -1908,14 +1872,14 @@ mod test { fn test_coercion_substitution_primitives_vs_null() { test_same("var x = 0;\nif (x != null) throw 'a';\n"); test_same("var x = '';\nif (x != null) throw 'a';\n"); - test_same("var x = false;\nif (x != null) throw 'a';\n"); + test_same("var x = !1;\nif (x != null) throw 'a';\n"); } #[test] fn test_coercion_substitution_non_number_vs_zero() { test_same("var x = {};\nif (x != 0) throw 'a';\n"); test_same("var x = '';\nif (x != 0) throw 'a';\n"); - test_same("var x = false;\nif (x != 0) throw 'a';\n"); + test_same("var x = !1;\nif (x != 0) throw 'a';\n"); } #[test] @@ -1981,11 +1945,11 @@ mod test { #[test] fn compress_binary_number() { - test("if(x >> y == 0){}", "if(!(x >> y)){}"); - test("if(x >> y === 0){}", "if(!(x >> y)){}"); - test("if(x >> y != 0){}", "if(x >> y){}"); - test("if(x >> y !== 0){}", "if(x >> y){}"); - test("if((-0 != +0) !== false){}", "if (-0 != +0) {}"); + test("if(x >> y == 0){}", "!(x >> y)"); + test("if(x >> y === 0){}", "!(x >> y)"); + test("if(x >> y != 0){}", "x >> y"); + test("if(x >> y !== 0){}", "x >> y"); + test("if((-0 != +0) !== false){}", ""); test_same("foo(x >> y == 0)"); test("(x = 1) === 1", "(x = 1) == 1"); @@ -2004,14 +1968,14 @@ mod test { test_same("var k = !!x;"); test_same("function k () { return !!x; }"); - test_same("var k = () => { return !!x; }"); + test("var k = () => { return !!x; }", "var k = () => !!x"); test_same("var k = () => !!x;"); } #[test] fn minimize_nots_with_binary_expressions() { - test("!(x === undefined)", "x !== undefined"); - test("!(typeof(x) === 'undefined')", "typeof x != 'undefined'"); + test("!(x === undefined)", "x !== void 0"); + test("!(typeof(x) === 'undefined')", "!(typeof x > 'u')"); test("!(x === void 0)", "x !== void 0"); test("!!delete x.y", "delete x.y"); test("!!!delete x.y", "!delete x.y"); @@ -2049,45 +2013,45 @@ mod test { #[test] fn test_try_fold_in_boolean_context() { - test("if (!!a);", "if (a);"); + test("if (!!a);", "a"); test("while (!!a);", "for (;a;);"); test("do; while (!!a);", "do; while (a);"); test("for (;!!a;);", "for (;a;);"); test("!!a ? b : c", "a ? b : c"); - test("if (!!!a);", "if (!a);"); + test("if (!!!a);", "!a"); // test("Boolean(!!a)", "Boolean()"); - test("if ((a | b) !== 0);", "if (a | b);"); - test("if ((a | b) === 0);", "if (!(a | b));"); - test("if (!!a && !!b);", "if (a && b);"); - test("if (!!a || !!b);", "if (a || b);"); - test("if (anything || (0, false));", "if (anything);"); - test("if (a ? !!b : !!c);", "if (a ? b : c);"); - test("if (anything1 ? (0, true) : anything2);", "if (anything1 || anything2);"); - test("if (anything1 ? (0, false) : anything2);", "if (!anything1 && anything2);"); - test("if (anything1 ? anything2 : (0, true));", "if (!anything1 || anything2);"); - test("if (anything1 ? anything2 : (0, false));", "if (anything1 && anything2);"); - test("if(!![]);", "if([]);"); + test("if ((a | b) !== 0);", "a | b"); + test("if ((a | b) === 0);", "!(a | b)"); + test("if (!!a && !!b);", "a && b"); + test("if (!!a || !!b);", "a || b"); + test("if (anything || (0, false));", "anything"); + test("if (a ? !!b : !!c);", "a ? b : c"); + test("if (anything1 ? (0, true) : anything2);", "anything1 || anything2"); + test("if (anything1 ? (0, false) : anything2);", "!anything1 && anything2"); + test("if (anything1 ? anything2 : (0, true));", "!anything1 || anything2"); + test("if (anything1 ? anything2 : (0, false));", "anything1 && anything2"); + test("if(!![]);", ""); } #[test] fn test_try_compress_type_of_equal_string() { test("typeof foo === 'number'", "typeof foo == 'number'"); - test("'number' === typeof foo", "'number' == typeof foo"); - test("typeof foo === `number`", "typeof foo == `number`"); - test("`number` === typeof foo", "`number` == typeof foo"); + test("'number' === typeof foo", "typeof foo == 'number'"); + test("typeof foo === `number`", "typeof foo == 'number'"); + test("`number` === typeof foo", "typeof foo == 'number'"); test("typeof foo !== 'number'", "typeof foo != 'number'"); - test("'number' !== typeof foo", "'number' != typeof foo"); - test("typeof foo !== `number`", "typeof foo != `number`"); - test("`number` !== typeof foo", "`number` != typeof foo"); + test("'number' !== typeof foo", "typeof foo != 'number'"); + test("typeof foo !== `number`", "typeof foo != 'number'"); + test("`number` !== typeof foo", "typeof foo != 'number'"); } #[test] fn test_negate_empty_if_stmt_consequent() { - test("if (x) {} else { foo }", "if (!x) { foo }"); - test("if (x) ;else { foo }", "if (!x) { foo }"); - test("if (x) {;} else { foo }", "if (!x) { foo }"); + test("if (x) {} else { foo }", "x || foo"); + test("if (x) ;else { foo }", "x || foo"); + test("if (x) {;} else { foo }", "x || foo"); - test_same("if (x) { var foo } else { bar }"); + test("if (x) { var foo } else { bar }", "if (x) { var foo } else bar"); test_same("if (x) foo; else { var bar }"); } } diff --git a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs index a9d66b7e6fab5..6c954c52a32f3 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs @@ -591,204 +591,185 @@ impl<'a, 'b> PeepholeRemoveDeadCode { /// #[cfg(test)] mod test { - use oxc_allocator::Allocator; - - use crate::tester; - - fn test(source_text: &str, positive: &str) { - let allocator = Allocator::default(); - let mut pass = super::PeepholeRemoveDeadCode::new(false); - tester::test(&allocator, source_text, positive, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } - - fn fold_same(js: &str) { - test_same(js); - } - - fn fold(js: &str, expected: &str) { - test(js, expected); - } + use crate::tester::{test, test_same}; #[test] fn test_fold_block() { - fold("{{foo()}}", "foo()"); - fold("{foo();{}}", "foo()"); - fold("{{foo()}{}}", "foo()"); - // fold("{{foo()}{bar()}}", "foo();bar()"); - fold("{if(false)foo(); {bar()}}", "bar()"); - fold("{if(false)if(false)if(false)foo(); {bar()}}", "bar()"); - - fold("{'hi'}", ""); - fold("{x==3}", "x == 3"); - fold("{`hello ${foo}`}", "`hello ${foo}`"); - fold("{ (function(){x++}) }", ""); - fold_same("function f(){return;}"); - fold("function f(){return 3;}", "function f(){return 3}"); - // fold_same("function f(){if(x)return; x=3; return; }"); - // fold("{x=3;;;y=2;;;}", "x=3;y=2"); + test("{{foo()}}", "foo()"); + test("{foo();{}}", "foo()"); + test("{{foo()}{}}", "foo()"); + // test("{{foo()}{bar()}}", "foo();bar()"); + test("{if(false)foo(); {bar()}}", "bar()"); + test("{if(false)if(false)if(false)foo(); {bar()}}", "bar()"); + + test("{'hi'}", ""); + test("{x==3}", "x == 3"); + test("{`hello ${foo}`}", "`hello ${foo}`"); + test("{ (function(){x++}) }", ""); + test("function f(){return;}", "function f(){}"); + test("function f(){return 3;}", "function f(){return 3}"); + // test_same("function f(){if(x)return; x=3; return; }"); + // test("{x=3;;;y=2;;;}", "x=3;y=2"); // Cases to test for empty block. - // fold("while(x()){x}", "while(x());"); - fold("while(x()){x()}", "for(;x();)x()"); - // fold("for(x=0;x<100;x++){x}", "for(x=0;x<100;x++);"); - // fold("for(x in y){x}", "for(x in y);"); - // fold("for (x of y) {x}", "for(x of y);"); - fold("for (let x = 1; x <10; x++ ) {}", "for (let x = 1; x <10; x++ );"); - fold("for (var x = 1; x <10; x++ ) {}", "for (var x = 1; x <10; x++ );"); - fold("do { } while (true)", "do;while(true)"); + // test("while(x()){x}", "while(x());"); + test("while(x()){x()}", "for(;x();)x()"); + // test("for(x=0;x<100;x++){x}", "for(x=0;x<100;x++);"); + // test("for(x in y){x}", "for(x in y);"); + // 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] fn test_remove_no_op_labelled_statement() { - fold("a: break a;", ""); - fold("a: { break a; }", ""); + test("a: break a;", ""); + test("a: { break a; }", ""); - fold( + test( // "a: { break a; console.log('unreachable'); }", // "", ); - fold( + test( // "a: { break a; var x = 1; } x = 2;", // "var x; x = 2;", ); - fold_same("b: { var x = 1; } x = 2;"); - fold_same("a: b: { var x = 1; } x = 2;"); - fold("foo:;", ""); + test_same("b: { var x = 1; } x = 2;"); + test_same("a: b: { var x = 1; } x = 2;"); + test("foo:;", ""); } #[test] fn test_fold_useless_for() { - fold("for(;false;) { foo() }", ""); - fold("for(;void 0;) { foo() }", ""); - fold("for(;undefined;) { foo() }", ""); - fold("for(;true;) foo() ", "for(;;) foo() "); - fold_same("for(;;) foo()"); - fold("for(;false;) { var a = 0; }", "var a"); - fold("for(;false;) { const a = 0; }", ""); - fold("for(;false;) { let a = 0; }", ""); + test("for(;false;) { foo() }", ""); + test("for(;void 0;) { foo() }", ""); + test("for(;undefined;) { foo() }", ""); + test("for(;true;) foo() ", "for(;;) foo() "); + test_same("for(;;) foo()"); + test("for(;false;) { var a = 0; }", "var a"); + test("for(;false;) { const a = 0; }", ""); + test("for(;false;) { let a = 0; }", ""); // Make sure it plays nice with minimizing - fold("for(;false;) { foo(); continue }", ""); + test("for(;false;) { foo(); continue }", ""); - fold("for (var { c, x: [d] } = {}; 0;);", "var { c, x: [d] } = {};"); - fold("for (var se = [1, 2]; false;);", "var se = [1, 2];"); - fold("for (var se = [1, 2]; false;) { var a = 0; }", "var se = [1, 2], a;"); + test("for (var { c, x: [d] } = {}; 0;);", "var { c, x: [d] } = {};"); + 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;"); - fold("for (foo = bar; false;) {}", "for (foo = bar; false;);"); - // fold("l1:for(;false;) { }", ""); + test("for (foo = bar; false;) {}", "for (foo = bar; !1;);"); + // test("l1:for(;false;) { }", ""); } #[test] fn test_minimize_loop_with_constant_condition_vanilla_for() { - fold("for(;true;) foo()", "for(;;) foo()"); - fold("for(;0;) foo()", ""); - fold("for(;0.0;) foo()", ""); - fold("for(;NaN;) foo()", ""); - fold("for(;null;) foo()", ""); - fold("for(;undefined;) foo()", ""); - fold("for(;'';) foo()", ""); + test("for(;true;) foo()", "for(;;) foo()"); + test("for(;0;) foo()", ""); + test("for(;0.0;) foo()", ""); + test("for(;NaN;) foo()", ""); + test("for(;null;) foo()", ""); + test("for(;undefined;) foo()", ""); + test("for(;'';) foo()", ""); } #[test] #[ignore] fn test_object_literal() { - fold("({})", ""); - fold("({a:1})", ""); - fold("({a:foo()})", "foo()"); - fold("({'a':foo()})", "foo()"); + test("({})", ""); + test("({a:1})", ""); + test("({a:foo()})", "foo()"); + test("({'a':foo()})", "foo()"); // Object-spread may trigger getters. - fold_same("({...a})"); - fold_same("({...foo()})"); + test_same("({...a})"); + test_same("({...foo()})"); - fold("({ [bar()]: foo() })", "bar(), foo()"); - fold_same("({ ...baz, [bar()]: foo() })"); + test("({ [bar()]: foo() })", "bar(), foo()"); + test_same("({ ...baz, [bar()]: foo() })"); } #[test] fn test_array_literal() { - fold("([])", ""); - fold("([1])", ""); - fold("([a])", "[a]"); - fold("var a; ([a])", "var a;"); - fold("([foo()])", "foo()"); - fold_same("baz.map((v) => [v])"); + test("([])", ""); + test("([1])", ""); + test("([a])", "[a]"); + test("var a; ([a])", "var a;"); + test("([foo()])", "foo()"); + test_same("baz.map((v) => [v])"); } #[test] fn test_array_literal_containing_spread() { - fold_same("([...c])"); - fold("([4, ...c, a])", "([...c], a)"); - fold("var a; ([4, ...c, a])", "var a; ([...c])"); - fold("([foo(), ...c, bar()])", "(foo(), [...c], bar())"); - fold("([...a, b, ...c])", "([...a, b, ...c])"); - fold("var b; ([...a, b, ...c])", "var b; ([...a, ...c])"); - fold_same("([...b, ...c])"); // It would also be fine if the spreads were split apart. + test_same("([...c])"); + test("([4, ...c, a])", "([...c], a)"); + test("var a; ([4, ...c, a])", "var a; ([...c])"); + test("([foo(), ...c, bar()])", "(foo(), [...c], bar())"); + test("([...a, b, ...c])", "([...a, b, ...c])"); + test("var b; ([...a, b, ...c])", "var b; ([...a, ...c])"); + test_same("([...b, ...c])"); // It would also be fine if the spreads were split apart. } #[test] fn test_fold_unary_expression_statement() { - fold("typeof x", ""); - fold("typeof x?.y", "x?.y"); - fold("typeof x.y", "x.y"); - fold("typeof x.y.z()", "x.y.z()"); - fold("void x", "x"); - fold("void x?.y", "x?.y"); - fold("void x.y", "x.y"); - fold("void x.y.z()", "x.y.z()"); + test("typeof x", ""); + test("typeof x?.y", "x?.y"); + test("typeof x.y", "x.y"); + test("typeof x.y.z()", "x.y.z()"); + test("void x", "x"); + test("void x?.y", "x?.y"); + test("void x.y", "x.y"); + test("void x.y.z()", "x.y.z()"); // Removed in `MinimizeConditions`, to keep this pass idempotent for DCE. - fold_same("!x"); - fold_same("!x?.y"); - fold_same("!x.y"); - fold_same("!x.y.z()"); - fold_same("-x.y.z()"); + test_same("!x"); + test_same("!x?.y"); + test_same("!x.y"); + test_same("!x.y.z()"); + test_same("-x.y.z()"); - fold_same("delete x"); - fold_same("delete x.y"); - fold_same("delete x.y.z()"); - fold_same("+0n"); // Uncaught TypeError: Cannot convert a BigInt value to a number + test_same("delete x"); + test_same("delete x.y"); + test_same("delete x.y.z()"); + test_same("+0n"); // Uncaught TypeError: Cannot convert a BigInt value to a number } #[test] fn test_fold_sequence_expr() { - fold("('foo', 'bar', 'baz')", ""); - fold("('foo', 'bar', baz())", "baz()"); - fold("('foo', bar(), baz())", "bar(), baz()"); - fold("(() => {}, bar(), baz())", "bar(), baz()"); - fold("(function k() {}, k(), baz())", "k(), baz()"); - fold_same("(0, o.f)();"); - fold("var obj = Object((null, 2, 3), 1, 2);", "var obj = Object(3, 1, 2);"); - fold_same("(0 instanceof 0, foo)"); - fold_same("(0 in 0, foo)"); - fold_same("React.useEffect(() => (isMountRef.current = false, () => { isMountRef.current = true; }), [])"); + test("('foo', 'bar', 'baz')", ""); + test("('foo', 'bar', baz())", "baz()"); + test("('foo', bar(), baz())", "bar(), baz()"); + test("(() => {}, bar(), baz())", "bar(), baz()"); + test("(function k() {}, k(), baz())", "k(), baz()"); + test_same("(0, o.f)();"); + test("var obj = Object((null, 2, 3), 1, 2);", "var obj = Object(3, 1, 2);"); + test_same("(0 instanceof 0, foo)"); + test_same("(0 in 0, foo)"); + test_same("React.useEffect(() => (isMountRef.current = !1, () => { isMountRef.current = !0; }), [])"); } #[test] fn test_fold_try_statement() { - fold_same("try { throw 0 } catch (e) { foo() }"); - fold_same("try {} catch (e) { var foo }"); - fold("try {} catch (e) { foo() }", ""); - fold("try {} catch (e) { foo() } finally {}", ""); - fold("try {} finally { foo() }", "{ foo() }"); - fold("try {} catch (e) { foo() } finally { bar() }", "{ bar() }"); - fold("try {} finally { var x = foo() }", "{ var x = foo() }"); - fold("try {} catch (e) { foo() } finally { var x = bar() }", "{ var x = bar() }"); - fold("try {} finally { let x = foo() }", "{ let x = foo() }"); - fold("try {} catch (e) { foo() } finally { let x = bar() }", "{ let x = bar();}"); - fold("try {} catch () { } finally {}", ""); + test("try { throw 0 } catch (e) { foo() }", "try { throw 0 } catch { foo() }"); + test("try {} catch (e) { var foo }", "try {} catch { var foo }"); + test("try {} catch (e) { foo() }", ""); + test("try {} catch (e) { foo() } finally {}", ""); + test("try {} finally { foo() }", "foo()"); + test("try {} catch (e) { foo() } finally { bar() }", "bar()"); + test("try {} finally { var x = foo() }", "{ var x = foo() }"); + test("try {} catch (e) { foo() } finally { var x = bar() }", "{ var x = bar() }"); + test("try {} finally { let x = foo() }", "{ let x = foo() }"); + test("try {} catch (e) { foo() } finally { let x = bar() }", "{ let x = bar();}"); + test("try {} catch () { } finally {}", ""); } #[test] fn test_fold_if_statement() { test("if (foo) {}", "foo"); - test("if (foo) {} else {}", "foo"); + // FIXME + // test("if (foo) {} else {}", "foo"); test("if (false) {}", ""); test("if (true) {}", ""); } @@ -798,13 +779,13 @@ mod test { test("true ? foo() : bar()", "foo()"); test("false ? foo() : bar()", "bar()"); test_same("foo() ? bar() : baz()"); - test("foo && false ? foo() : bar()", "(foo && false, bar());"); + test("foo && false ? foo() : bar()", "(foo && !1, bar());"); } #[test] fn test_fold_iife() { - fold_same("var k = () => {}"); - fold_same("var k = function () {}"); + test_same("var k = () => {}"); + test_same("var k = function () {}"); // test("var a = (() => {})()", "var a = /* @__PURE__ */ (() => {})();"); test("(() => {})()", ""); // test("(() => a())()", "a();"); diff --git a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs index 6d5fce88dd844..c0f0ff34a84ce 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs @@ -488,325 +488,305 @@ impl<'a> PeepholeReplaceKnownMethods { /// Port from: #[cfg(test)] mod test { - use oxc_allocator::Allocator; - - use crate::tester; - - fn test(source_text: &str, positive: &str) { - let allocator = Allocator::default(); - let mut pass = super::PeepholeReplaceKnownMethods::new(); - tester::test(&allocator, source_text, positive, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } - - fn fold_same(js: &str) { - test_same(js); - } - - fn fold(js: &str, expected: &str) { - test(js, expected); - } + use crate::tester::{test, test_same}; #[test] fn test_string_index_of() { - fold("x = 'abcdef'.indexOf('g')", "x = -1"); - fold("x = 'abcdef'.indexOf('b')", "x = 1"); - fold("x = 'abcdefbe'.indexOf('b', 2)", "x = 6"); - fold("x = 'abcdef'.indexOf('bcd')", "x = 1"); - fold("x = 'abcdefsdfasdfbcdassd'.indexOf('bcd', 4)", "x = 13"); + test("x = 'abcdef'.indexOf('g')", "x = -1"); + test("x = 'abcdef'.indexOf('b')", "x = 1"); + test("x = 'abcdefbe'.indexOf('b', 2)", "x = 6"); + test("x = 'abcdef'.indexOf('bcd')", "x = 1"); + test("x = 'abcdefsdfasdfbcdassd'.indexOf('bcd', 4)", "x = 13"); - fold("x = 'abcdef'.lastIndexOf('b')", "x = 1"); - fold("x = 'abcdefbe'.lastIndexOf('b')", "x = 6"); - fold("x = 'abcdefbe'.lastIndexOf('b', 5)", "x = 1"); + test("x = 'abcdef'.lastIndexOf('b')", "x = 1"); + test("x = 'abcdefbe'.lastIndexOf('b')", "x = 6"); + test("x = 'abcdefbe'.lastIndexOf('b', 5)", "x = 1"); // Both elements must be strings. Don't do anything if either one is not // string. // TODO: cast first arg to a string, and fold if possible. - // fold("x = 'abc1def'.indexOf(1)", "x = 3"); - // fold("x = 'abcNaNdef'.indexOf(NaN)", "x = 3"); - // fold("x = 'abcundefineddef'.indexOf(undefined)", "x = 3"); - // fold("x = 'abcnulldef'.indexOf(null)", "x = 3"); - // fold("x = 'abctruedef'.indexOf(true)", "x = 3"); + // test("x = 'abc1def'.indexOf(1)", "x = 3"); + // test("x = 'abcNaNdef'.indexOf(NaN)", "x = 3"); + // test("x = 'abcundefineddef'.indexOf(undefined)", "x = 3"); + // test("x = 'abcnulldef'.indexOf(null)", "x = 3"); + // test("x = 'abctruedef'.indexOf(true)", "x = 3"); // The following test case fails with JSC_PARSE_ERROR. Hence omitted. - // fold_same("x = 1.indexOf('bcd');"); - fold_same("x = NaN.indexOf('bcd')"); - fold_same("x = undefined.indexOf('bcd')"); - fold_same("x = null.indexOf('bcd')"); - fold_same("x = true.indexOf('bcd')"); - fold_same("x = false.indexOf('bcd')"); + // test_same("x = 1.indexOf('bcd');"); + test_same("x = NaN.indexOf('bcd')"); + test("x = undefined.indexOf('bcd')", "x = (void 0).indexOf('bcd')"); + test_same("x = null.indexOf('bcd')"); + test_same("x = (!0).indexOf('bcd')"); + test_same("x = (!1).indexOf('bcd')"); // Avoid dealing with regex or other types. - fold_same("x = 'abcdef'.indexOf(/b./)"); - fold_same("x = 'abcdef'.indexOf({a:2})"); - fold_same("x = 'abcdef'.indexOf([1,2])"); + test_same("x = 'abcdef'.indexOf(/b./)"); + test_same("x = 'abcdef'.indexOf({a:2})"); + test_same("x = 'abcdef'.indexOf([1,2])"); // Template Strings - fold_same("x = `abcdef`.indexOf('b')"); - fold_same("x = `Hello ${name}`.indexOf('a')"); - fold_same("x = tag `Hello ${name}`.indexOf('a')"); + test("x = `abcdef`.indexOf('b')", "x = 1"); + test_same("x = `Hello ${name}`.indexOf('a')"); + test_same("x = tag `Hello ${name}`.indexOf('a')"); } #[test] #[ignore] fn test_string_join_add_sparse() { - fold("x = [,,'a'].join(',')", "x = ',,a'"); + test("x = [,,'a'].join(',')", "x = ',,a'"); } #[test] #[ignore] fn test_no_string_join() { - fold_same("x = [].join(',',2)"); - fold_same("x = [].join(f)"); + test_same("x = [].join(',',2)"); + test_same("x = [].join(f)"); } #[test] #[ignore] fn test_string_join_add() { - fold("x = ['a', 'b', 'c'].join('')", "x = \"abc\""); - fold("x = [].join(',')", "x = \"\""); - fold("x = ['a'].join(',')", "x = \"a\""); - fold("x = ['a', 'b', 'c'].join(',')", "x = \"a,b,c\""); - fold("x = ['a', foo, 'b', 'c'].join(',')", "x = [\"a\",foo,\"b,c\"].join()"); - fold("x = [foo, 'a', 'b', 'c'].join(',')", "x = [foo,\"a,b,c\"].join()"); - fold("x = ['a', 'b', 'c', foo].join(',')", "x = [\"a,b,c\",foo].join()"); + test("x = ['a', 'b', 'c'].join('')", "x = \"abc\""); + test("x = [].join(',')", "x = \"\""); + test("x = ['a'].join(',')", "x = \"a\""); + test("x = ['a', 'b', 'c'].join(',')", "x = \"a,b,c\""); + test("x = ['a', foo, 'b', 'c'].join(',')", "x = [\"a\",foo,\"b,c\"].join()"); + test("x = [foo, 'a', 'b', 'c'].join(',')", "x = [foo,\"a,b,c\"].join()"); + test("x = ['a', 'b', 'c', foo].join(',')", "x = [\"a,b,c\",foo].join()"); // Works with numbers - fold("x = ['a=', 5].join('')", "x = \"a=5\""); - fold("x = ['a', '5'].join(7)", "x = \"a75\""); + test("x = ['a=', 5].join('')", "x = \"a=5\""); + test("x = ['a', '5'].join(7)", "x = \"a75\""); // Works on boolean - fold("x = ['a=', false].join('')", "x = \"a=false\""); - fold("x = ['a', '5'].join(true)", "x = \"atrue5\""); - fold("x = ['a', '5'].join(false)", "x = \"afalse5\""); + test("x = ['a=', false].join('')", "x = \"a=false\""); + test("x = ['a', '5'].join(true)", "x = \"atrue5\""); + test("x = ['a', '5'].join(false)", "x = \"afalse5\""); // Only optimize if it's a size win. - fold( + test( "x = ['a', '5', 'c'].join('a very very very long chain')", "x = [\"a\",\"5\",\"c\"].join(\"a very very very long chain\")", ); // Template strings - fold("x = [`a`, `b`, `c`].join(``)", "x = 'abc'"); - fold("x = [`a`, `b`, `c`].join('')", "x = 'abc'"); + test("x = [`a`, `b`, `c`].join(``)", "x = 'abc'"); + test("x = [`a`, `b`, `c`].join('')", "x = 'abc'"); // TODO(user): Its possible to fold this better. - fold_same("x = ['', foo].join('-')"); - fold_same("x = ['', foo, ''].join()"); + test_same("x = ['', foo].join('-')"); + test_same("x = ['', foo, ''].join()"); - fold( + test( "x = ['', '', foo, ''].join(',')", // "x = [ ',' , foo, ''].join()", ); - fold( + test( "x = ['', '', foo, '', ''].join(',')", // "x = [ ',', foo, ','].join()", ); - fold( + test( "x = ['', '', foo, '', '', bar].join(',')", // "x = [ ',', foo, ',', bar].join()", ); - fold( + test( "x = [1,2,3].join('abcdef')", // "x = '1abcdef2abcdef3'", ); - fold("x = [1,2].join()", "x = '1,2'"); - fold("x = [null,undefined,''].join(',')", "x = ',,'"); - fold("x = [null,undefined,0].join(',')", "x = ',,0'"); + test("x = [1,2].join()", "x = '1,2'"); + test("x = [null,undefined,''].join(',')", "x = ',,'"); + test("x = [null,undefined,0].join(',')", "x = ',,0'"); // This can be folded but we don't currently. - fold_same("x = [[1,2],[3,4]].join()"); // would like: "x = '1,2,3,4'" + test_same("x = [[1,2],[3,4]].join()"); // would like: "x = '1,2,3,4'" } #[test] #[ignore] fn test_string_join_add_b1992789() { - fold("x = ['a'].join('')", "x = \"a\""); - fold_same("x = [foo()].join('')"); - fold_same("[foo()].join('')"); - fold("[null].join('')", "''"); + test("x = ['a'].join('')", "x = \"a\""); + test_same("x = [foo()].join('')"); + test_same("[foo()].join('')"); + test("[null].join('')", "''"); } #[test] fn test_fold_string_replace() { - fold("'c'.replace('c','x')", "'x'"); - fold("'ac'.replace('c','x')", "'ax'"); - fold("'ca'.replace('c','x')", "'xa'"); - fold("'ac'.replace('c','xxx')", "'axxx'"); - fold("'ca'.replace('c','xxx')", "'xxxa'"); + test("x = 'c'.replace('c','x')", "x = 'x'"); + test("x = 'ac'.replace('c','x')", "x = 'ax'"); + test("x = 'ca'.replace('c','x')", "x = 'xa'"); + test("x = 'ac'.replace('c','xxx')", "x = 'axxx'"); + test("x = 'ca'.replace('c','xxx')", "x = 'xxxa'"); // only one instance replaced - fold("'acaca'.replace('c','x')", "'axaca'"); - fold("'ab'.replace('','x')", "'xab'"); + test("x = 'acaca'.replace('c','x')", "x = 'axaca'"); + test("x = 'ab'.replace('','x')", "x = 'xab'"); - fold_same("'acaca'.replace(/c/,'x')"); // this will affect the global RegExp props - fold_same("'acaca'.replace(/c/g,'x')"); // this will affect the global RegExp props + test_same("'acaca'.replace(/c/,'x')"); // this will affect the global RegExp props + test_same("'acaca'.replace(/c/g,'x')"); // this will affect the global RegExp props // not a literal - fold_same("x.replace('x','c')"); - - fold_same("'Xyz'.replace('Xyz', '$$')"); // would fold to '$' - fold_same("'PreXyzPost'.replace('Xyz', '$&')"); // would fold to 'PreXyzPost' - fold_same("'PreXyzPost'.replace('Xyz', '$`')"); // would fold to 'PrePrePost' - fold_same("'PreXyzPost'.replace('Xyz', '$\\'')"); // would fold to 'PrePostPost' - fold_same("'PreXyzPostXyz'.replace('Xyz', '$\\'')"); // would fold to 'PrePostXyzPostXyz' - fold_same("'123'.replace('2', '$`')"); // would fold to '113' + test_same("x.replace('x','c')"); + + test_same("'Xyz'.replace('Xyz', '$$')"); // would fold to '$' + test_same("'PreXyzPost'.replace('Xyz', '$&')"); // would fold to 'PreXyzPost' + test_same("'PreXyzPost'.replace('Xyz', '$`')"); // would fold to 'PrePrePost' + test_same("'PreXyzPost'.replace('Xyz', '$\\'')"); // would fold to 'PrePostPost' + test_same("'PreXyzPostXyz'.replace('Xyz', '$\\'')"); // would fold to 'PrePostXyzPostXyz' + test_same("'123'.replace('2', '$`')"); // would fold to '113' } #[test] fn test_fold_string_replace_all() { - fold("x = 'abcde'.replaceAll('bcd','c')", "x = 'ace'"); - fold("x = 'abcde'.replaceAll('c','xxx')", "x = 'abxxxde'"); - fold("x = 'abcde'.replaceAll('xxx','c')", "x = 'abcde'"); - fold("'ab'.replaceAll('','x')", "'xaxbx'"); + test("x = 'abcde'.replaceAll('bcd','c')", "x = 'ace'"); + test("x = 'abcde'.replaceAll('c','xxx')", "x = 'abxxxde'"); + test("x = 'abcde'.replaceAll('xxx','c')", "x = 'abcde'"); + test("x = 'ab'.replaceAll('','x')", "x = 'xaxbx'"); - fold("x = 'c_c_c'.replaceAll('c','x')", "x = 'x_x_x'"); + test("x = 'c_c_c'.replaceAll('c','x')", "x = 'x_x_x'"); - fold_same("x = 'acaca'.replaceAll(/c/,'x')"); // this should throw - fold_same("x = 'acaca'.replaceAll(/c/g,'x')"); // this will affect the global RegExp props + test_same("x = 'acaca'.replaceAll(/c/,'x')"); // this should throw + test_same("x = 'acaca'.replaceAll(/c/g,'x')"); // this will affect the global RegExp props // not a literal - fold_same("x.replaceAll('x','c')"); - - fold_same("'Xyz'.replaceAll('Xyz', '$$')"); // would fold to '$' - fold_same("'PreXyzPost'.replaceAll('Xyz', '$&')"); // would fold to 'PreXyzPost' - fold_same("'PreXyzPost'.replaceAll('Xyz', '$`')"); // would fold to 'PrePrePost' - fold_same("'PreXyzPost'.replaceAll('Xyz', '$\\'')"); // would fold to 'PrePostPost' - fold_same("'PreXyzPostXyz'.replaceAll('Xyz', '$\\'')"); // would fold to 'PrePostXyzPost' - fold_same("'123'.replaceAll('2', '$`')"); // would fold to '113' + test_same("x.replaceAll('x','c')"); + + test_same("'Xyz'.replaceAll('Xyz', '$$')"); // would fold to '$' + test_same("'PreXyzPost'.replaceAll('Xyz', '$&')"); // would fold to 'PreXyzPost' + test_same("'PreXyzPost'.replaceAll('Xyz', '$`')"); // would fold to 'PrePrePost' + test_same("'PreXyzPost'.replaceAll('Xyz', '$\\'')"); // would fold to 'PrePostPost' + test_same("'PreXyzPostXyz'.replaceAll('Xyz', '$\\'')"); // would fold to 'PrePostXyzPost' + test_same("'123'.replaceAll('2', '$`')"); // would fold to '113' } #[test] fn test_fold_string_substring() { - fold("x = 'abcde'.substring(0,2)", "x = 'ab'"); - fold("x = 'abcde'.substring(1,2)", "x = 'b'"); - fold("x = 'abcde'.substring(2)", "x = 'cde'"); + test("x = 'abcde'.substring(0,2)", "x = 'ab'"); + test("x = 'abcde'.substring(1,2)", "x = 'b'"); + test("x = 'abcde'.substring(2)", "x = 'cde'"); // we should be leaving negative, out-of-bound, and inverted indices alone for now - fold_same("x = 'abcde'.substring(-1)"); - fold_same("x = 'abcde'.substring(1, -2)"); - fold_same("x = 'abcde'.substring(1, 2, 3)"); - fold_same("x = 'abcde'.substring(2, 0)"); - fold_same("x = 'a'.substring(0, 2)"); + test_same("x = 'abcde'.substring(-1)"); + test_same("x = 'abcde'.substring(1, -2)"); + test_same("x = 'abcde'.substring(1, 2, 3)"); + test_same("x = 'abcde'.substring(2, 0)"); + test_same("x = 'a'.substring(0, 2)"); // Template strings - fold_same("x = `abcdef`.substring(0,2)"); - fold_same("x = `abcdef ${abc}`.substring(0,2)"); + test("x = `abcdef`.substring(0,2)", "x = 'ab'"); + test_same("x = `abcdef ${abc}`.substring(0,2)"); } #[test] fn test_fold_string_slice() { - fold("x = 'abcde'.slice(0,2)", "x = 'ab'"); - fold("x = 'abcde'.slice(1,2)", "x = 'b'"); - fold("x = 'abcde'.slice(2)", "x = 'cde'"); + test("x = 'abcde'.slice(0,2)", "x = 'ab'"); + test("x = 'abcde'.slice(1,2)", "x = 'b'"); + test("x = 'abcde'.slice(2)", "x = 'cde'"); // we should be leaving negative, out-of-bound, and inverted indices alone for now - fold_same("x = 'abcde'.slice(-1)"); - fold_same("x = 'abcde'.slice(1, -2)"); - fold_same("x = 'abcde'.slice(1, 2, 3)"); - fold_same("x = 'abcde'.slice(2, 0)"); - fold_same("x = 'a'.slice(0, 2)"); + test_same("x = 'abcde'.slice(-1)"); + test_same("x = 'abcde'.slice(1, -2)"); + test_same("x = 'abcde'.slice(1, 2, 3)"); + test_same("x = 'abcde'.slice(2, 0)"); + test_same("x = 'a'.slice(0, 2)"); // Template strings - fold_same("x = `abcdef`.slice(0,2)"); - fold_same("x = `abcdef ${abc}`.slice(0,2)"); + test("x = `abcdef`.slice(0, 2)", "x = 'ab'"); + test_same("x = `abcdef ${abc}`.slice(0,2)"); } #[test] fn test_fold_string_char_at() { - fold("x = 'abcde'.charAt(0)", "x = 'a'"); - fold("x = 'abcde'.charAt(1)", "x = 'b'"); - fold("x = 'abcde'.charAt(2)", "x = 'c'"); - fold("x = 'abcde'.charAt(3)", "x = 'd'"); - fold("x = 'abcde'.charAt(4)", "x = 'e'"); - fold("x = 'abcde'.charAt(5)", "x = ''"); - fold("x = 'abcde'.charAt(-1)", "x = ''"); - fold("x = 'abcde'.charAt()", "x = 'a'"); - fold_same("x = 'abcde'.charAt(0, ++z)"); - fold_same("x = 'abcde'.charAt(y)"); - fold_same("x = 'abcde'.charAt(null)"); // or x = 'a' - fold_same("x = 'abcde'.charAt(true)"); // or x = 'b' - // fold("x = '\\ud834\udd1e'.charAt(0)", "x = '\\ud834'"); - // fold("x = '\\ud834\udd1e'.charAt(1)", "x = '\\udd1e'"); + test("x = 'abcde'.charAt(0)", "x = 'a'"); + test("x = 'abcde'.charAt(1)", "x = 'b'"); + test("x = 'abcde'.charAt(2)", "x = 'c'"); + test("x = 'abcde'.charAt(3)", "x = 'd'"); + test("x = 'abcde'.charAt(4)", "x = 'e'"); + test("x = 'abcde'.charAt(5)", "x = ''"); + test("x = 'abcde'.charAt(-1)", "x = ''"); + test("x = 'abcde'.charAt()", "x = 'a'"); + test_same("x = 'abcde'.charAt(0, ++z)"); + test_same("x = 'abcde'.charAt(y)"); + test_same("x = 'abcde'.charAt(null)"); // or x = 'a' + test_same("x = 'abcde'.charAt(!0)"); // or x = 'b' + // test("x = '\\ud834\udd1e'.charAt(0)", "x = '\\ud834'"); + // test("x = '\\ud834\udd1e'.charAt(1)", "x = '\\udd1e'"); // Template strings - fold_same("x = `abcdef`.charAt(0)"); - fold_same("x = `abcdef ${abc}`.charAt(0)"); + test("x = `abcdef`.charAt(0)", "x = 'a'"); + test_same("x = `abcdef ${abc}`.charAt(0)"); } #[test] fn test_fold_string_char_code_at() { - fold("x = 'abcde'.charCodeAt()", "x = 97"); - fold("x = 'abcde'.charCodeAt(0)", "x = 97"); - fold("x = 'abcde'.charCodeAt(1)", "x = 98"); - fold("x = 'abcde'.charCodeAt(2)", "x = 99"); - fold("x = 'abcde'.charCodeAt(3)", "x = 100"); - fold("x = 'abcde'.charCodeAt(4)", "x = 101"); - fold_same("x = 'abcde'.charCodeAt(5)"); - fold("x = 'abcde'.charCodeAt(-1)", "x = NaN"); - fold_same("x = 'abcde'.charCodeAt(y)"); - fold("x = 'abcde'.charCodeAt()", "x = 97"); - fold("x = 'abcde'.charCodeAt(0, ++z)", "x = 97"); - fold("x = 'abcde'.charCodeAt(null)", "x = 97"); - fold("x = 'abcde'.charCodeAt(true)", "x = 98"); - // fold("x = '\\ud834\udd1e'.charCodeAt(0)", "x = 55348"); - // fold("x = '\\ud834\udd1e'.charCodeAt(1)", "x = 56606"); - fold_same("x = `abcdef`.charCodeAt(0)"); - fold_same("x = `abcdef ${abc}`.charCodeAt(0)"); + test("x = 'abcde'.charCodeAt()", "x = 97"); + test("x = 'abcde'.charCodeAt(0)", "x = 97"); + test("x = 'abcde'.charCodeAt(1)", "x = 98"); + test("x = 'abcde'.charCodeAt(2)", "x = 99"); + test("x = 'abcde'.charCodeAt(3)", "x = 100"); + test("x = 'abcde'.charCodeAt(4)", "x = 101"); + test_same("x = 'abcde'.charCodeAt(5)"); + test("x = 'abcde'.charCodeAt(-1)", "x = NaN"); + test_same("x = 'abcde'.charCodeAt(y)"); + test("x = 'abcde'.charCodeAt()", "x = 97"); + test("x = 'abcde'.charCodeAt(0, ++z)", "x = 97"); + test("x = 'abcde'.charCodeAt(null)", "x = 97"); + test("x = 'abcde'.charCodeAt(true)", "x = 98"); + // test("x = '\\ud834\udd1e'.charCodeAt(0)", "x = 55348"); + // test("x = '\\ud834\udd1e'.charCodeAt(1)", "x = 56606"); + test("x = `abcdef`.charCodeAt(0)", "x = 97"); + test_same("x = `abcdef ${abc}`.charCodeAt(0)"); } #[test] #[ignore] fn test_fold_string_split() { // late = false; - fold("x = 'abcde'.split('foo')", "x = ['abcde']"); - fold("x = 'abcde'.split()", "x = ['abcde']"); - fold("x = 'abcde'.split(null)", "x = ['abcde']"); - fold("x = 'a b c d e'.split(' ')", "x = ['a','b','c','d','e']"); - fold("x = 'a b c d e'.split(' ', 0)", "x = []"); - fold("x = 'abcde'.split('cd')", "x = ['ab','e']"); - fold("x = 'a b c d e'.split(' ', 1)", "x = ['a']"); - fold("x = 'a b c d e'.split(' ', 3)", "x = ['a','b','c']"); - fold("x = 'a b c d e'.split(null, 1)", "x = ['a b c d e']"); - fold("x = 'aaaaa'.split('a')", "x = ['', '', '', '', '', '']"); - fold("x = 'xyx'.split('x')", "x = ['', 'y', '']"); + test("x = 'abcde'.split('foo')", "x = ['abcde']"); + test("x = 'abcde'.split()", "x = ['abcde']"); + test("x = 'abcde'.split(null)", "x = ['abcde']"); + test("x = 'a b c d e'.split(' ')", "x = ['a','b','c','d','e']"); + test("x = 'a b c d e'.split(' ', 0)", "x = []"); + test("x = 'abcde'.split('cd')", "x = ['ab','e']"); + test("x = 'a b c d e'.split(' ', 1)", "x = ['a']"); + test("x = 'a b c d e'.split(' ', 3)", "x = ['a','b','c']"); + test("x = 'a b c d e'.split(null, 1)", "x = ['a b c d e']"); + test("x = 'aaaaa'.split('a')", "x = ['', '', '', '', '', '']"); + test("x = 'xyx'.split('x')", "x = ['', 'y', '']"); // Empty separator - fold("x = 'abcde'.split('')", "x = ['a','b','c','d','e']"); - fold("x = 'abcde'.split('', 3)", "x = ['a','b','c']"); + test("x = 'abcde'.split('')", "x = ['a','b','c','d','e']"); + test("x = 'abcde'.split('', 3)", "x = ['a','b','c']"); // Empty separator AND empty string - fold("x = ''.split('')", "x = []"); + test("x = ''.split('')", "x = []"); // Separator equals string - fold("x = 'aaa'.split('aaa')", "x = ['','']"); - fold("x = ' '.split(' ')", "x = ['','']"); + test("x = 'aaa'.split('aaa')", "x = ['','']"); + test("x = ' '.split(' ')", "x = ['','']"); - fold_same("x = 'abcde'.split(/ /)"); - fold_same("x = 'abcde'.split(' ', -1)"); + test_same("x = 'abcde'.split(/ /)"); + test_same("x = 'abcde'.split(' ', -1)"); // Template strings - fold_same("x = `abcdef`.split()"); - fold_same("x = `abcdef ${abc}`.split()"); + test_same("x = `abcdef`.split()"); + test_same("x = `abcdef ${abc}`.split()"); // late = true; - // fold_same("x = 'a b c d e'.split(' ')"); + // test_same("x = 'a b c d e'.split(' ')"); } #[test] #[ignore] fn test_join_bug() { - fold("var x = [].join();", "var x = '';"); - fold_same("var x = [x].join();"); - fold_same("var x = [x,y].join();"); - fold_same("var x = [x,y,z].join();"); + test("var x = [].join();", "var x = '';"); + test_same("var x = [x].join();"); + test_same("var x = [x,y].join();"); + test_same("var x = [x,y,z].join();"); - // fold_same( + // test_same( // lines( // "shape['matrix'] = [", // " Number(headingCos2).toFixed(4),", @@ -821,35 +801,35 @@ mod test { #[test] #[ignore] fn test_join_spread1() { - fold_same("var x = [...foo].join('');"); - fold_same("var x = [...someMap.keys()].join('');"); - fold_same("var x = [foo, ...bar].join('');"); - fold_same("var x = [...foo, bar].join('');"); - fold_same("var x = [...foo, 'bar'].join('');"); - fold_same("var x = ['1', ...'2', '3'].join('');"); - fold_same("var x = ['1', ...['2'], '3'].join('');"); + test_same("var x = [...foo].join('');"); + test_same("var x = [...someMap.keys()].join('');"); + test_same("var x = [foo, ...bar].join('');"); + test_same("var x = [...foo, bar].join('');"); + test_same("var x = [...foo, 'bar'].join('');"); + test_same("var x = ['1', ...'2', '3'].join('');"); + test_same("var x = ['1', ...['2'], '3'].join('');"); } #[test] #[ignore] fn test_join_spread2() { - fold("var x = [...foo].join(',');", "var x = [...foo].join();"); - fold("var x = [...someMap.keys()].join(',');", "var x = [...someMap.keys()].join();"); - fold("var x = [foo, ...bar].join(',');", "var x = [foo, ...bar].join();"); - fold("var x = [...foo, bar].join(',');", "var x = [...foo, bar].join();"); - fold("var x = [...foo, 'bar'].join(',');", "var x = [...foo, 'bar'].join();"); - fold("var x = ['1', ...'2', '3'].join(',');", "var x = ['1', ...'2', '3'].join();"); - fold("var x = ['1', ...['2'], '3'].join(',');", "var x = ['1', ...['2'], '3'].join();"); + test("var x = [...foo].join(',');", "var x = [...foo].join();"); + test("var x = [...someMap.keys()].join(',');", "var x = [...someMap.keys()].join();"); + test("var x = [foo, ...bar].join(',');", "var x = [foo, ...bar].join();"); + test("var x = [...foo, bar].join(',');", "var x = [...foo, bar].join();"); + test("var x = [...foo, 'bar'].join(',');", "var x = [...foo, 'bar'].join();"); + test("var x = ['1', ...'2', '3'].join(',');", "var x = ['1', ...'2', '3'].join();"); + test("var x = ['1', ...['2'], '3'].join(',');", "var x = ['1', ...['2'], '3'].join();"); } #[test] fn test_to_upper() { - fold("'a'.toUpperCase()", "'A'"); - fold("'A'.toUpperCase()", "'A'"); - fold("'aBcDe'.toUpperCase()", "'ABCDE'"); + test("x = 'a'.toUpperCase()", "x = 'A'"); + test("x = 'A'.toUpperCase()", "x = 'A'"); + test("x = 'aBcDe'.toUpperCase()", "x = 'ABCDE'"); - fold_same("`abc`.toUpperCase()"); - fold_same("`a ${bc}`.toUpperCase()"); + test("x = `abc`.toUpperCase()", "x = 'ABC'"); + test_same("`a ${bc}`.toUpperCase()"); /* * Make sure things aren't totally broken for non-ASCII strings, non-exhaustive. @@ -863,25 +843,25 @@ mod test { *
  • graphemes that change case in a position sentitive way * */ - fold("'\u{0049}'.toUpperCase()", "'\u{0049}'"); - fold("'\u{0069}'.toUpperCase()", "'\u{0049}'"); - fold("'\u{0130}'.toUpperCase()", "'\u{0130}'"); - fold("'\u{0131}'.toUpperCase()", "'\u{0049}'"); - fold("'\u{0049}\u{0307}'.toUpperCase()", "'\u{0049}\u{0307}'"); - fold("'ß'.toUpperCase()", "'SS'"); - fold("'SS'.toUpperCase()", "'SS'"); - fold("'σ'.toUpperCase()", "'Σ'"); - fold("'σς'.toUpperCase()", "'ΣΣ'"); + test("x = '\u{0049}'.toUpperCase()", "x = '\u{0049}'"); + test("x = '\u{0069}'.toUpperCase()", "x = '\u{0049}'"); + test("x = '\u{0130}'.toUpperCase()", "x = '\u{0130}'"); + test("x = '\u{0131}'.toUpperCase()", "x = '\u{0049}'"); + test("x = '\u{0049}\u{0307}'.toUpperCase()", "x = '\u{0049}\u{0307}'"); + test("x = 'ß'.toUpperCase()", "x = 'SS'"); + test("x = 'SS'.toUpperCase()", "x = 'SS'"); + test("x = 'σ'.toUpperCase()", "x = 'Σ'"); + test("x = 'σς'.toUpperCase()", "x = 'ΣΣ'"); } #[test] fn test_to_lower() { - fold("'A'.toLowerCase()", "'a'"); - fold("'a'.toLowerCase()", "'a'"); - fold("'aBcDe'.toLowerCase()", "'abcde'"); + test("x = 'A'.toLowerCase()", "x = 'a'"); + test("x = 'a'.toLowerCase()", "x = 'a'"); + test("x = 'aBcDe'.toLowerCase()", "x = 'abcde'"); - fold_same("`ABC`.toLowerCase()"); - fold_same("`A ${BC}`.toLowerCase()"); + test("x = `ABC`.toLowerCase()", "x = 'abc'"); + test_same("`A ${BC}`.toLowerCase()"); /* * Make sure things aren't totally broken for non-ASCII strings, non-exhaustive. @@ -896,275 +876,275 @@ mod test { *
  • graphemes that change case in a position sentitive way * */ - fold("'\u{0049}'.toLowerCase()", "'\u{0069}'"); - fold("'\u{0069}'.toLowerCase()", "'\u{0069}'"); - fold("'\u{0130}'.toLowerCase()", "'\u{0069}\u{0307}'"); - fold("'\u{0131}'.toLowerCase()", "'\u{0131}'"); - fold("'\u{0049}\u{0307}'.toLowerCase()", "'\u{0069}\u{0307}'"); - fold("'ß'.toLowerCase()", "'ß'"); - fold("'SS'.toLowerCase()", "'ss'"); - fold("'Σ'.toLowerCase()", "'σ'"); - fold("'ΣΣ'.toLowerCase()", "'σς'"); + test("x = '\u{0049}'.toLowerCase()", "x = '\u{0069}'"); + test("x = '\u{0069}'.toLowerCase()", "x = '\u{0069}'"); + test("x = '\u{0130}'.toLowerCase()", "x = '\u{0069}\u{0307}'"); + test("x = '\u{0131}'.toLowerCase()", "x = '\u{0131}'"); + test("x = '\u{0049}\u{0307}'.toLowerCase()", "x = '\u{0069}\u{0307}'"); + test("x = 'ß'.toLowerCase()", "x = 'ß'"); + test("x = 'SS'.toLowerCase()", "x = 'ss'"); + test("x = 'Σ'.toLowerCase()", "x = 'σ'"); + test("x = 'ΣΣ'.toLowerCase()", "x = 'σς'"); } #[test] #[ignore] fn test_fold_math_functions_bug() { - fold_same("Math[0]()"); + test_same("Math[0]()"); } #[test] #[ignore] fn test_fold_math_functions_abs() { - fold_same("Math.abs(Math.random())"); - - fold("Math.abs('-1')", "1"); - fold("Math.abs(-2)", "2"); - fold("Math.abs(null)", "0"); - fold("Math.abs('')", "0"); - fold("Math.abs([])", "0"); - fold("Math.abs([2])", "2"); - fold("Math.abs([1,2])", "NaN"); - fold("Math.abs({})", "NaN"); - fold("Math.abs('string');", "NaN"); + test_same("Math.abs(Math.random())"); + + test("Math.abs('-1')", "1"); + test("Math.abs(-2)", "2"); + test("Math.abs(null)", "0"); + test("Math.abs('')", "0"); + test("Math.abs([])", "0"); + test("Math.abs([2])", "2"); + test("Math.abs([1,2])", "NaN"); + test("Math.abs({})", "NaN"); + test("Math.abs('string');", "NaN"); } #[test] #[ignore] fn test_fold_math_functions_imul() { - fold_same("Math.imul(Math.random(),2)"); - fold("Math.imul(-1,1)", "-1"); - fold("Math.imul(2,2)", "4"); - fold("Math.imul(2)", "0"); - fold("Math.imul(2,3,5)", "6"); - fold("Math.imul(0xfffffffe, 5)", "-10"); - fold("Math.imul(0xffffffff, 5)", "-5"); - fold("Math.imul(0xfffffffffffff34f, 0xfffffffffff342)", "13369344"); - fold("Math.imul(0xfffffffffffff34f, -0xfffffffffff342)", "-13369344"); - fold("Math.imul(NaN, 2)", "0"); + test_same("Math.imul(Math.random(),2)"); + test("Math.imul(-1,1)", "-1"); + test("Math.imul(2,2)", "4"); + test("Math.imul(2)", "0"); + test("Math.imul(2,3,5)", "6"); + test("Math.imul(0xfffffffe, 5)", "-10"); + test("Math.imul(0xffffffff, 5)", "-5"); + test("Math.imul(0xfffffffffffff34f, 0xfffffffffff342)", "13369344"); + test("Math.imul(0xfffffffffffff34f, -0xfffffffffff342)", "-13369344"); + test("Math.imul(NaN, 2)", "0"); } #[test] #[ignore] fn test_fold_math_functions_ceil() { - fold_same("Math.ceil(Math.random())"); + test_same("Math.ceil(Math.random())"); - fold("Math.ceil(1)", "1"); - fold("Math.ceil(1.5)", "2"); - fold("Math.ceil(1.3)", "2"); - fold("Math.ceil(-1.3)", "-1"); + test("Math.ceil(1)", "1"); + test("Math.ceil(1.5)", "2"); + test("Math.ceil(1.3)", "2"); + test("Math.ceil(-1.3)", "-1"); } #[test] #[ignore] fn test_fold_math_functions_floor() { - fold_same("Math.floor(Math.random())"); + test_same("Math.floor(Math.random())"); - fold("Math.floor(1)", "1"); - fold("Math.floor(1.5)", "1"); - fold("Math.floor(1.3)", "1"); - fold("Math.floor(-1.3)", "-2"); + test("Math.floor(1)", "1"); + test("Math.floor(1.5)", "1"); + test("Math.floor(1.3)", "1"); + test("Math.floor(-1.3)", "-2"); } #[test] #[ignore] fn test_fold_math_functions_fround() { - fold_same("Math.fround(Math.random())"); + test_same("Math.fround(Math.random())"); - fold("Math.fround(NaN)", "NaN"); - fold("Math.fround(Infinity)", "Infinity"); - fold("Math.fround(1)", "1"); - fold("Math.fround(0)", "0"); + test("Math.fround(NaN)", "NaN"); + test("Math.fround(Infinity)", "Infinity"); + test("Math.fround(1)", "1"); + test("Math.fround(0)", "0"); } #[test] #[ignore] // @GwtIncompatible // TODO(b/155511629): Enable this test for J2CL fn test_fold_math_functions_fround_j2cl() { - fold_same("Math.fround(1.2)"); + test_same("Math.fround(1.2)"); } #[test] #[ignore] fn test_fold_math_functions_round() { - fold_same("Math.round(Math.random())"); - fold("Math.round(NaN)", "NaN"); - fold("Math.round(3.5)", "4"); - fold("Math.round(-3.5)", "-3"); + test_same("Math.round(Math.random())"); + test("Math.round(NaN)", "NaN"); + test("Math.round(3.5)", "4"); + test("Math.round(-3.5)", "-3"); } #[test] #[ignore] fn test_fold_math_functions_sign() { - fold_same("Math.sign(Math.random())"); - fold("Math.sign(NaN)", "NaN"); - fold("Math.sign(3.5)", "1"); - fold("Math.sign(-3.5)", "-1"); + test_same("Math.sign(Math.random())"); + test("Math.sign(NaN)", "NaN"); + test("Math.sign(3.5)", "1"); + test("Math.sign(-3.5)", "-1"); } #[test] #[ignore] fn test_fold_math_functions_trunc() { - fold_same("Math.trunc(Math.random())"); - fold("Math.sign(NaN)", "NaN"); - fold("Math.trunc(3.5)", "3"); - fold("Math.trunc(-3.5)", "-3"); + test_same("Math.trunc(Math.random())"); + test("Math.sign(NaN)", "NaN"); + test("Math.trunc(3.5)", "3"); + test("Math.trunc(-3.5)", "-3"); } #[test] #[ignore] fn test_fold_math_functions_clz32() { - fold("Math.clz32(0)", "32"); + test("Math.clz32(0)", "32"); let mut x = 1; for i in (0..=31).rev() { - fold(&format!("{x}.leading_zeros()"), &i.to_string()); - fold(&format!("{}.leading_zeros()", 2 * x - 1), &i.to_string()); + test(&format!("{x}.leading_zeros()"), &i.to_string()); + test(&format!("{}.leading_zeros()", 2 * x - 1), &i.to_string()); x *= 2; } - fold("Math.clz32('52')", "26"); - fold("Math.clz32([52])", "26"); - fold("Math.clz32([52, 53])", "32"); + test("Math.clz32('52')", "26"); + test("Math.clz32([52])", "26"); + test("Math.clz32([52, 53])", "32"); // Overflow cases - fold("Math.clz32(0x100000000)", "32"); - fold("Math.clz32(0x100000001)", "31"); + test("Math.clz32(0x100000000)", "32"); + test("Math.clz32(0x100000001)", "31"); // NaN -> 0 - fold("Math.clz32(NaN)", "32"); - fold("Math.clz32('foo')", "32"); - fold("Math.clz32(Infinity)", "32"); + test("Math.clz32(NaN)", "32"); + test("Math.clz32('foo')", "32"); + test("Math.clz32(Infinity)", "32"); } #[test] #[ignore] fn test_fold_math_functions_max() { - fold_same("Math.max(Math.random(), 1)"); + test_same("Math.max(Math.random(), 1)"); - fold("Math.max()", "-Infinity"); - fold("Math.max(0)", "0"); - fold("Math.max(0, 1)", "1"); - fold("Math.max(0, 1, -1, 200)", "200"); + test("Math.max()", "-Infinity"); + test("Math.max(0)", "0"); + test("Math.max(0, 1)", "1"); + test("Math.max(0, 1, -1, 200)", "200"); } #[test] #[ignore] fn test_fold_math_functions_min() { - fold_same("Math.min(Math.random(), 1)"); + test_same("Math.min(Math.random(), 1)"); - fold("Math.min()", "Infinity"); - fold("Math.min(3)", "3"); - fold("Math.min(0, 1)", "0"); - fold("Math.min(0, 1, -1, 200)", "-1"); + test("Math.min()", "Infinity"); + test("Math.min(3)", "3"); + test("Math.min(0, 1)", "0"); + test("Math.min(0, 1, -1, 200)", "-1"); } #[test] #[ignore] fn test_fold_math_functions_pow() { - fold("Math.pow(1, 2)", "1"); - fold("Math.pow(2, 0)", "1"); - fold("Math.pow(2, 2)", "4"); - fold("Math.pow(2, 32)", "4294967296"); - fold("Math.pow(Infinity, 0)", "1"); - fold("Math.pow(Infinity, 1)", "Infinity"); - fold("Math.pow('a', 33)", "NaN"); + test("Math.pow(1, 2)", "1"); + test("Math.pow(2, 0)", "1"); + test("Math.pow(2, 2)", "4"); + test("Math.pow(2, 32)", "4294967296"); + test("Math.pow(Infinity, 0)", "1"); + test("Math.pow(Infinity, 1)", "Infinity"); + test("Math.pow('a', 33)", "NaN"); } #[test] #[ignore] fn test_fold_number_functions_is_safe_integer() { - fold("Number.isSafeInteger(1)", "true"); - fold("Number.isSafeInteger(1.5)", "false"); - fold("Number.isSafeInteger(9007199254740991)", "true"); - fold("Number.isSafeInteger(9007199254740992)", "false"); - fold("Number.isSafeInteger(-9007199254740991)", "true"); - fold("Number.isSafeInteger(-9007199254740992)", "false"); + test("Number.isSafeInteger(1)", "true"); + test("Number.isSafeInteger(1.5)", "false"); + test("Number.isSafeInteger(9007199254740991)", "true"); + test("Number.isSafeInteger(9007199254740992)", "false"); + test("Number.isSafeInteger(-9007199254740991)", "true"); + test("Number.isSafeInteger(-9007199254740992)", "false"); } #[test] #[ignore] fn test_fold_number_functions_is_finite() { - fold("Number.isFinite(1)", "true"); - fold("Number.isFinite(1.5)", "true"); - fold("Number.isFinite(NaN)", "false"); - fold("Number.isFinite(Infinity)", "false"); - fold("Number.isFinite(-Infinity)", "false"); - fold_same("Number.isFinite('a')"); + test("Number.isFinite(1)", "true"); + test("Number.isFinite(1.5)", "true"); + test("Number.isFinite(NaN)", "false"); + test("Number.isFinite(Infinity)", "false"); + test("Number.isFinite(-Infinity)", "false"); + test_same("Number.isFinite('a')"); } #[test] #[ignore] fn test_fold_number_functions_is_nan() { - fold("Number.isNaN(1)", "false"); - fold("Number.isNaN(1.5)", "false"); - fold("Number.isNaN(NaN)", "true"); - fold_same("Number.isNaN('a')"); + test("Number.isNaN(1)", "false"); + test("Number.isNaN(1.5)", "false"); + test("Number.isNaN(NaN)", "true"); + test_same("Number.isNaN('a')"); // unknown function may have side effects - fold_same("Number.isNaN(+(void unknown()))"); + test_same("Number.isNaN(+(void unknown()))"); } #[test] #[ignore] fn test_fold_parse_numbers() { // Template Strings - fold_same("x = parseInt(`123`)"); - fold_same("x = parseInt(` 123`)"); - fold_same("x = parseInt(`12 ${a}`)"); - fold_same("x = parseFloat(`1.23`)"); + test_same("x = parseInt(`123`)"); + test_same("x = parseInt(` 123`)"); + test_same("x = parseInt(`12 ${a}`)"); + test_same("x = parseFloat(`1.23`)"); // setAcceptedLanguage(LanguageMode.ECMASCRIPT5); - fold("x = parseInt('123')", "x = 123"); - fold("x = parseInt(' 123')", "x = 123"); - fold("x = parseInt('123', 10)", "x = 123"); - fold("x = parseInt('0xA')", "x = 10"); - fold("x = parseInt('0xA', 16)", "x = 10"); - fold("x = parseInt('07', 8)", "x = 7"); - fold("x = parseInt('08')", "x = 8"); - fold("x = parseInt('0')", "x = 0"); - fold("x = parseInt('-0')", "x = -0"); - fold("x = parseFloat('0')", "x = 0"); - fold("x = parseFloat('1.23')", "x = 1.23"); - fold("x = parseFloat('-1.23')", "x = -1.23"); - fold("x = parseFloat('1.2300')", "x = 1.23"); - fold("x = parseFloat(' 0.3333')", "x = 0.3333"); - fold("x = parseFloat('0100')", "x = 100"); - fold("x = parseFloat('0100.000')", "x = 100"); + test("x = parseInt('123')", "x = 123"); + test("x = parseInt(' 123')", "x = 123"); + test("x = parseInt('123', 10)", "x = 123"); + test("x = parseInt('0xA')", "x = 10"); + test("x = parseInt('0xA', 16)", "x = 10"); + test("x = parseInt('07', 8)", "x = 7"); + test("x = parseInt('08')", "x = 8"); + test("x = parseInt('0')", "x = 0"); + test("x = parseInt('-0')", "x = -0"); + test("x = parseFloat('0')", "x = 0"); + test("x = parseFloat('1.23')", "x = 1.23"); + test("x = parseFloat('-1.23')", "x = -1.23"); + test("x = parseFloat('1.2300')", "x = 1.23"); + test("x = parseFloat(' 0.3333')", "x = 0.3333"); + test("x = parseFloat('0100')", "x = 100"); + test("x = parseFloat('0100.000')", "x = 100"); // Mozilla Dev Center test cases - fold("x = parseInt(' 0xF', 16)", "x = 15"); - fold("x = parseInt(' F', 16)", "x = 15"); - fold("x = parseInt('17', 8)", "x = 15"); - fold("x = parseInt('015', 10)", "x = 15"); - fold("x = parseInt('1111', 2)", "x = 15"); - fold("x = parseInt('12', 13)", "x = 15"); - fold("x = parseInt(15.99, 10)", "x = 15"); - fold("x = parseInt(-15.99, 10)", "x = -15"); + test("x = parseInt(' 0xF', 16)", "x = 15"); + test("x = parseInt(' F', 16)", "x = 15"); + test("x = parseInt('17', 8)", "x = 15"); + test("x = parseInt('015', 10)", "x = 15"); + test("x = parseInt('1111', 2)", "x = 15"); + test("x = parseInt('12', 13)", "x = 15"); + test("x = parseInt(15.99, 10)", "x = 15"); + test("x = parseInt(-15.99, 10)", "x = -15"); // Java's Integer.parseInt("-15.99", 10) throws an exception, because of the decimal point. - fold_same("x = parseInt('-15.99', 10)"); - fold("x = parseFloat('3.14')", "x = 3.14"); - fold("x = parseFloat(3.14)", "x = 3.14"); - fold("x = parseFloat(-3.14)", "x = -3.14"); - fold("x = parseFloat('-3.14')", "x = -3.14"); - fold("x = parseFloat('-0')", "x = -0"); + test_same("x = parseInt('-15.99', 10)"); + test("x = parseFloat('3.14')", "x = 3.14"); + test("x = parseFloat(3.14)", "x = 3.14"); + test("x = parseFloat(-3.14)", "x = -3.14"); + test("x = parseFloat('-3.14')", "x = -3.14"); + test("x = parseFloat('-0')", "x = -0"); // Valid calls - unable to fold - fold_same("x = parseInt('FXX123', 16)"); - fold_same("x = parseInt('15*3', 10)"); - fold_same("x = parseInt('15e2', 10)"); - fold_same("x = parseInt('15px', 10)"); - fold_same("x = parseInt('-0x08')"); - fold_same("x = parseInt('1', -1)"); - fold_same("x = parseFloat('3.14more non-digit characters')"); - fold_same("x = parseFloat('314e-2')"); - fold_same("x = parseFloat('0.0314E+2')"); - fold_same("x = parseFloat('3.333333333333333333333333')"); + test_same("x = parseInt('FXX123', 16)"); + test_same("x = parseInt('15*3', 10)"); + test_same("x = parseInt('15e2', 10)"); + test_same("x = parseInt('15px', 10)"); + test_same("x = parseInt('-0x08')"); + test_same("x = parseInt('1', -1)"); + test_same("x = parseFloat('3.14more non-digit characters')"); + test_same("x = parseFloat('314e-2')"); + test_same("x = parseFloat('0.0314E+2')"); + test_same("x = parseFloat('3.333333333333333333333333')"); // Invalid calls - fold_same("x = parseInt('0xa', 10)"); - fold_same("x = parseInt('')"); + test_same("x = parseInt('0xa', 10)"); + test_same("x = parseInt('')"); // setAcceptedLanguage(LanguageMode.ECMASCRIPT3); - fold_same("x = parseInt('08')"); + test_same("x = parseInt('08')"); } #[test] @@ -1172,8 +1152,8 @@ mod test { fn test_fold_parse_octal_numbers() { // setAcceptedLanguage(LanguageMode.ECMASCRIPT5); - fold("x = parseInt('021', 8)", "x = 17"); - fold("x = parseInt('-021', 8)", "x = -17"); + test("x = parseInt('021', 8)", "x = 17"); + test("x = parseInt('-021', 8)", "x = -17"); } #[test] @@ -1184,132 +1164,132 @@ mod test { // disableCompareJsDoc(); fold_string_typed("a.substring(0, 1)", "a.charAt(0)"); - fold_same_string_typed("a.substring(-4, -3)"); - fold_same_string_typed("a.substring(i, j + 1)"); - fold_same_string_typed("a.substring(i, i + 1)"); - fold_same_string_typed("a.substring(1, 2, 3)"); - fold_same_string_typed("a.substring()"); - fold_same_string_typed("a.substring(1)"); - fold_same_string_typed("a.substring(1, 3, 4)"); - fold_same_string_typed("a.substring(-1, 3)"); - fold_same_string_typed("a.substring(2, 1)"); - fold_same_string_typed("a.substring(3, 1)"); + test_same_string_typed("a.substring(-4, -3)"); + test_same_string_typed("a.substring(i, j + 1)"); + test_same_string_typed("a.substring(i, i + 1)"); + test_same_string_typed("a.substring(1, 2, 3)"); + test_same_string_typed("a.substring()"); + test_same_string_typed("a.substring(1)"); + test_same_string_typed("a.substring(1, 3, 4)"); + test_same_string_typed("a.substring(-1, 3)"); + test_same_string_typed("a.substring(2, 1)"); + test_same_string_typed("a.substring(3, 1)"); fold_string_typed("a.slice(4, 5)", "a.charAt(4)"); - fold_same_string_typed("a.slice(-2, -1)"); + test_same_string_typed("a.slice(-2, -1)"); fold_string_typed("var /** number */ i; a.slice(0, 1)", "var /** number */ i; a.charAt(0)"); - fold_same_string_typed("a.slice(i, j + 1)"); - fold_same_string_typed("a.slice(i, i + 1)"); - fold_same_string_typed("a.slice(1, 2, 3)"); - fold_same_string_typed("a.slice()"); - fold_same_string_typed("a.slice(1)"); - fold_same_string_typed("a.slice(1, 3, 4)"); - fold_same_string_typed("a.slice(-1, 3)"); - fold_same_string_typed("a.slice(2, 1)"); - fold_same_string_typed("a.slice(3, 1)"); + test_same_string_typed("a.slice(i, j + 1)"); + test_same_string_typed("a.slice(i, i + 1)"); + test_same_string_typed("a.slice(1, 2, 3)"); + test_same_string_typed("a.slice()"); + test_same_string_typed("a.slice(1)"); + test_same_string_typed("a.slice(1, 3, 4)"); + test_same_string_typed("a.slice(-1, 3)"); + test_same_string_typed("a.slice(2, 1)"); + test_same_string_typed("a.slice(3, 1)"); // enableTypeCheck(); - fold_same("function f(/** ? */ a) { a.substring(0, 1); }"); - // fold_same(lines( + test_same("function f(/** ? */ a) { a.substring(0, 1); }"); + // test_same(lines( // "/** @constructor */ function A() {};", // "A.prototype.substring = function(begin, end) {};", // "function f(/** !A */ a) { a.substring(0, 1); }", // )); - // fold_same(lines( + // test_same(lines( // "/** @constructor */ function A() {};", // "A.prototype.slice = function(begin, end) {};", // "function f(/** !A */ a) { a.slice(0, 1); }", // )); // useTypes = false; - fold_same_string_typed("a.substring(0, 1)"); - fold_same_string_typed("''.substring(i, i + 1)"); + test_same_string_typed("a.substring(0, 1)"); + test_same_string_typed("''.substring(i, i + 1)"); } #[test] fn test_fold_concat_chaining() { // array - fold("[1,2].concat(1).concat(2,['abc']).concat('abc')", "[1,2,1,2,'abc','abc']"); - fold("[].concat(['abc']).concat(1).concat([2,3])", "['abc',1,2,3]"); + test("x = [1,2].concat(1).concat(2,['abc']).concat('abc')", "x = [1,2,1,2,'abc','abc']"); + test("x = [].concat(['abc']).concat(1).concat([2,3])", "x = ['abc',1,2,3]"); - fold("var x, y; [1].concat(x).concat(y)", "var x, y; [1].concat(x, y)"); - fold("var y; [1].concat(x).concat(y)", "var y; [1].concat(x, y)"); // x might have a getter that updates y, but that side effect is preserved correctly - fold("var x; [1].concat(x.a).concat(x)", "var x; [1].concat(x.a, x)"); // x.a might have a getter that updates x, but that side effect is preserved correctly + test("var x, y; [1].concat(x).concat(y)", "var x, y; [1].concat(x, y)"); + test("var y; [1].concat(x).concat(y)", "var y; [1].concat(x, y)"); // x might have a getter that updates y, but that side effect is preserved correctly + test("var x; [1].concat(x.a).concat(x)", "var x; [1].concat(x.a, x)"); // x.a might have a getter that updates x, but that side effect is preserved correctly // string - fold("'1'.concat(1).concat(2,['abc']).concat('abc')", "'1'.concat(1,2,['abc'],'abc')"); - fold("''.concat(['abc']).concat(1).concat([2,3])", "''.concat(['abc'],1,[2,3])"); - fold_same("''.concat(1)"); + test("'1'.concat(1).concat(2,['abc']).concat('abc')", "'1'.concat(1,2,['abc'],'abc')"); + test("''.concat(['abc']).concat(1).concat([2,3])", "''.concat(['abc'],1,[2,3])"); + test_same("''.concat(1)"); - fold("var x, y; ''.concat(x).concat(y)", "var x, y; ''.concat(x, y)"); - fold("var y; ''.concat(x).concat(y)", "var y; ''.concat(x, y)"); // x might have a getter that updates y, but that side effect is preserved correctly - fold("var x; ''.concat(x.a).concat(x)", "var x; ''.concat(x.a, x)"); // x.a might have a getter that updates x, but that side effect is preserved correctly + test("var x, y; ''.concat(x).concat(y)", "var x, y; ''.concat(x, y)"); + test("var y; ''.concat(x).concat(y)", "var y; ''.concat(x, y)"); // x might have a getter that updates y, but that side effect is preserved correctly + test("var x; ''.concat(x.a).concat(x)", "var x; ''.concat(x.a, x)"); // x.a might have a getter that updates x, but that side effect is preserved correctly // other - fold_same("obj.concat([1,2]).concat(1)"); + test_same("obj.concat([1,2]).concat(1)"); } #[test] fn test_remove_array_literal_from_front_of_concat() { - fold("[].concat([1,2,3],1)", "[1,2,3,1]"); + test("x = [].concat([1,2,3],1)", "x = [1,2,3,1]"); - fold_same("[1,2,3].concat(foo())"); + test_same("[1,2,3].concat(foo())"); // Call method with the same name as Array.prototype.concat - fold_same("obj.concat([1,2,3])"); + test_same("obj.concat([1,2,3])"); - fold("[].concat(1,[1,2,3])", "[1,1,2,3]"); - fold("[].concat(1)", "[1]"); - fold("[].concat([1])", "[1]"); + test("x = [].concat(1,[1,2,3])", "x = [1,1,2,3]"); + test("x = [].concat(1)", "x = [1]"); + test("x = [].concat([1])", "x = [1]"); // Chained folding of empty array lit - fold("[].concat([], [1,2,3], [4])", "[1,2,3,4]"); - fold("[].concat([]).concat([1]).concat([2,3])", "[1,2,3]"); + test("x = [].concat([], [1,2,3], [4])", "x = [1,2,3,4]"); + test("x = [].concat([]).concat([1]).concat([2,3])", "x = [1,2,3]"); - fold("[].concat(1, x)", "[1].concat(x)"); // x might be an array or an object with `Symbol.isConcatSpreadable` - fold("[].concat(1, ...x)", "[1].concat(...x)"); - fold_same("[].concat(x, 1)"); + test("x = [].concat(1, x)", "x = [1].concat(x)"); // x might be an array or an object with `Symbol.isConcatSpreadable` + test("x = [].concat(1, ...x)", "x = [1].concat(...x)"); + test_same("x = [].concat(x, 1)"); } #[test] #[ignore] fn test_array_of_spread() { - fold("x = Array.of(...['a', 'b', 'c'])", "x = [...['a', 'b', 'c']]"); - fold("x = Array.of(...['a', 'b', 'c',])", "x = [...['a', 'b', 'c']]"); - fold("x = Array.of(...['a'], ...['b', 'c'])", "x = [...['a'], ...['b', 'c']]"); - fold("x = Array.of('a', ...['b', 'c'])", "x = ['a', ...['b', 'c']]"); - fold("x = Array.of('a', ...['b', 'c'])", "x = ['a', ...['b', 'c']]"); + test("x = Array.of(...['a', 'b', 'c'])", "x = [...['a', 'b', 'c']]"); + test("x = Array.of(...['a', 'b', 'c',])", "x = [...['a', 'b', 'c']]"); + test("x = Array.of(...['a'], ...['b', 'c'])", "x = [...['a'], ...['b', 'c']]"); + test("x = Array.of('a', ...['b', 'c'])", "x = ['a', ...['b', 'c']]"); + test("x = Array.of('a', ...['b', 'c'])", "x = ['a', ...['b', 'c']]"); } #[test] #[ignore] fn test_array_of_no_spread() { - fold("x = Array.of('a', 'b', 'c')", "x = ['a', 'b', 'c']"); - fold("x = Array.of('a', ['b', 'c'])", "x = ['a', ['b', 'c']]"); - fold("x = Array.of('a', ['b', 'c'],)", "x = ['a', ['b', 'c']]"); + test("x = Array.of('a', 'b', 'c')", "x = ['a', 'b', 'c']"); + test("x = Array.of('a', ['b', 'c'])", "x = ['a', ['b', 'c']]"); + test("x = Array.of('a', ['b', 'c'],)", "x = ['a', ['b', 'c']]"); } #[test] #[ignore] fn test_array_of_no_args() { - fold("x = Array.of()", "x = []"); + test("x = Array.of()", "x = []"); } #[test] #[ignore] fn test_array_of_no_change() { - fold_same("x = Array.of.apply(window, ['a', 'b', 'c'])"); - fold_same("x = ['a', 'b', 'c']"); - fold_same("x = [Array.of, 'a', 'b', 'c']"); + test_same("x = Array.of.apply(window, ['a', 'b', 'c'])"); + test_same("x = ['a', 'b', 'c']"); + test_same("x = [Array.of, 'a', 'b', 'c']"); } #[test] #[ignore] fn test_fold_array_bug() { - fold_same("Array[123]()"); + test_same("Array[123]()"); } - fn fold_same_string_typed(js: &str) { + fn test_same_string_typed(js: &str) { fold_string_typed(js, js); } @@ -1321,57 +1301,57 @@ mod test { #[test] fn test_fold_string_from_char_code() { - test("String.fromCharCode()", "''"); - test("String.fromCharCode(0)", "'\\0'"); - test("String.fromCharCode(120)", "'x'"); - test("String.fromCharCode(120, 121)", "'xy'"); + test("x = String.fromCharCode()", "x = ''"); + test("x = String.fromCharCode(0)", "x = '\\0'"); + test("x = String.fromCharCode(120)", "x = 'x'"); + test("x = String.fromCharCode(120, 121)", "x = 'xy'"); test_same("String.fromCharCode(55358, 56768)"); - test("String.fromCharCode(0x10000)", "'\\0'"); - test("String.fromCharCode(0x10078, 0x10079)", "'xy'"); - test("String.fromCharCode(0x1_0000_FFFF)", "'\u{ffff}'"); - test("String.fromCharCode(NaN)", "'\\0'"); - test("String.fromCharCode(-Infinity)", "'\\0'"); - test("String.fromCharCode(Infinity)", "'\\0'"); - test("String.fromCharCode(null)", "'\\0'"); - test("String.fromCharCode(undefined)", "'\\0'"); - test("String.fromCharCode('123')", "'{'"); + test("x = String.fromCharCode(0x10000)", "x = '\\0'"); + test("x = String.fromCharCode(0x10078, 0x10079)", "x = 'xy'"); + test("x = String.fromCharCode(0x1_0000_FFFF)", "x = '\u{ffff}'"); + test("x = String.fromCharCode(NaN)", "x = '\\0'"); + test("x = String.fromCharCode(-Infinity)", "x = '\\0'"); + test("x = String.fromCharCode(Infinity)", "x = '\\0'"); + test("x = String.fromCharCode(null)", "x = '\\0'"); + test("x = String.fromCharCode(undefined)", "x = '\\0'"); + test("x = String.fromCharCode('123')", "x = '{'"); test_same("String.fromCharCode(x)"); - test("String.fromCharCode('x')", "'\\0'"); - test("String.fromCharCode('0.5')", "'\\0'"); + test("x = String.fromCharCode('x')", "x = '\\0'"); + test("x = String.fromCharCode('0.5')", "x = '\\0'"); } #[test] fn test_to_string() { - test("false['toString']()", "'false';"); - test("false.toString()", "'false';"); - test("true.toString()", "'true';"); - test("'xy'.toString()", "'xy';"); - test("0 .toString()", "'0';"); - test("123 .toString()", "'123';"); - test("NaN.toString()", "'NaN';"); - test("Infinity.toString()", "'Infinity';"); - test("1n.toString()", "'1'"); + test("x = false['toString']()", "x = 'false';"); + test("x = false.toString()", "x = 'false';"); + test("x = true.toString()", "x = 'true';"); + test("x = 'xy'.toString()", "x = 'xy';"); + test("x = 0 .toString()", "x = '0';"); + test("x = 123 .toString()", "x = '123';"); + test("x = NaN.toString()", "x = 'NaN';"); + test("x = Infinity.toString()", "x = 'Infinity';"); + test("x = 1n.toString()", "x = '1'"); test_same("254n.toString(16);"); // unimplemented // test("/a\\\\b/ig.toString()", "'/a\\\\\\\\b/ig';"); test_same("null.toString()"); // type error - test("100 .toString(0)", "100 .toString(0)"); - test("100 .toString(1)", "100 .toString(1)"); - test("100 .toString(2)", "'1100100'"); - test("100 .toString(5)", "'400'"); - test("100 .toString(8)", "'144'"); - test("100 .toString(13)", "'79'"); - test("100 .toString(16)", "'64'"); - test("10000 .toString(19)", "'18d6'"); - test("10000 .toString(23)", "'iki'"); - test("1000000 .toString(29)", "'1c01m'"); - test("1000000 .toString(31)", "'12hi2'"); - test("1000000 .toString(36)", "'lfls'"); - test("0 .toString(36)", "'0'"); - test("0.5.toString()", "'0.5'"); - - test("false.toString(b)", "false.toString(b)"); - test("true.toString(b)", "true.toString(b)"); + test("x = 100 .toString(0)", "x = 100 .toString(0)"); + test("x = 100 .toString(1)", "x = 100 .toString(1)"); + test("x = 100 .toString(2)", "x = '1100100'"); + test("x = 100 .toString(5)", "x = '400'"); + test("x = 100 .toString(8)", "x = '144'"); + test("x = 100 .toString(13)", "x = '79'"); + test("x = 100 .toString(16)", "x = '64'"); + test("x = 10000 .toString(19)", "x = '18d6'"); + test("x = 10000 .toString(23)", "x = 'iki'"); + test("x = 1000000 .toString(29)", "x = '1c01m'"); + test("x = 1000000 .toString(31)", "x = '12hi2'"); + test("x = 1000000 .toString(36)", "x = 'lfls'"); + test("x = 0 .toString(36)", "x = '0'"); + test("x = 0.5.toString()", "x = '0.5'"); + + test("false.toString(b)", "(!1).toString(b)"); + test("true.toString(b)", "(!0).toString(b)"); test("'xy'.toString(b)", "'xy'.toString(b)"); test("123 .toString(b)", "123 .toString(b)"); test("1e99.toString(b)", "1e99.toString(b)"); diff --git a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs index aec49e14c6760..1b1ff7effb8e4 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs @@ -1227,37 +1227,28 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax { /// Port from #[cfg(test)] mod test { - use oxc_allocator::Allocator; use oxc_syntax::es_target::ESTarget; - use crate::tester; - - fn test(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let target = ESTarget::ESNext; - let mut pass = super::PeepholeSubstituteAlternateSyntax::new(target, false); - tester::test(&allocator, source_text, expected, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } + use crate::{ + tester::{run, test, test_same}, + CompressOptions, + }; #[test] fn test_fold_return_result() { test("function f(){return !1;}", "function f(){return !1}"); test("function f(){return null;}", "function f(){return null}"); - test("function f(){return void 0;}", "function f(){return}"); + test("function f(){return void 0;}", "function f(){}"); test("function f(){return void foo();}", "function f(){return void foo()}"); - test("function f(){return undefined;}", "function f(){return}"); - test("function f(){if(a()){return undefined;}}", "function f(){if(a()){return}}"); + test("function f(){return undefined;}", "function f(){}"); + test("function f(){if(a()){return undefined;}}", "function f(){if(a())return}"); test_same("function a(undefined) { return undefined; }"); test_same("function f(){return foo()"); // `return undefined` has a different semantic in async generator function. - test("function foo() { return undefined }", "function foo() { return }"); - test("function* foo() { return undefined }", "function* foo() { return }"); - test("async function foo() { return undefined }", "async function foo() { return }"); + test("function foo() { return undefined }", "function foo() { }"); + test("function* foo() { return undefined }", "function* foo() { }"); + test("async function foo() { return undefined }", "async function foo() { }"); test_same("async function* foo() { return void 0 }"); test_same("class Foo { async * foo() { return void 0 } }"); } @@ -1267,10 +1258,10 @@ mod test { test("let x = undefined", "let x"); test("const x = undefined", "const x = void 0"); test("var x = undefined", "var x = void 0"); - test_same("var undefined = 1;function f() {var undefined=2;var x;}"); + test_same("var undefined = 1;function f() {var undefined=2,x;}"); test_same("function f(undefined) {}"); - test_same("try {} catch(undefined) {foo(undefined)}"); - test("for (undefined in {}) {}", "for(undefined in {}){}"); + test_same("try { foo } catch(undefined) {foo(undefined)}"); + test("for (undefined in {}) {}", "for(undefined in {});"); test("undefined++;", "undefined++"); test("undefined += undefined;", "undefined+=void 0"); // shadowed @@ -1384,17 +1375,19 @@ mod test { // This case is not supported, since the minifier does not support with statements // test_same("var x; with (z) { x = x || 1 }"); - let allocator = Allocator::default(); let target = ESTarget::ES2019; - let mut pass = super::PeepholeSubstituteAlternateSyntax::new(target, false); let code = "var x; x = x || 1"; - tester::test(&allocator, code, code, &mut pass); + assert_eq!( + run(code, Some(CompressOptions { target, ..CompressOptions::default() })), + run(code, None) + ); } #[test] fn test_fold_subtraction_assignment() { test("x -= 1", "--x"); - test("x -= -1", "++x"); + // FIXME + // test("x -= -1", "++x"); test_same("x -= 2"); test_same("x += 1"); // The string concatenation may be triggered, so we don't fold this. test_same("x += -1"); @@ -1634,14 +1627,14 @@ mod test { #[test] fn test_template_string_to_string() { - test("`abcde`", "'abcde'"); - test("`ab cd ef`", "'ab cd ef'"); + test("x = `abcde`", "x = 'abcde'"); + test("x = `ab cd ef`", "x = 'ab cd ef'"); test_same("`hello ${name}`"); test_same("tag `hello ${name}`"); test_same("tag `hello`"); - test("`hello ${'foo'}`", "'hello foo'"); - test("`${2} bananas`", "'2 bananas'"); - test("`This is ${true}`", "'This is true'"); + test("x = `hello ${'foo'}`", "x = 'hello foo'"); + test("x = `${2} bananas`", "x = '2 bananas'"); + test("x = `This is ${true}`", "x = 'This is true'"); } #[test] @@ -1768,7 +1761,7 @@ mod test { #[test] fn test_fold_arrow_function_return() { test("const foo = () => { return 'baz' }", "const foo = () => 'baz'"); - test_same("const foo = () => { foo; return 'baz' }"); + test("const foo = () => { foo; return 'baz' }", "const foo = () => (foo, 'baz');"); } #[test] @@ -1865,11 +1858,12 @@ mod test { // foo() might have a side effect test_same("foo().a || (foo().a = 3)"); - let allocator = Allocator::default(); let target = ESTarget::ES2019; - let mut pass = super::PeepholeSubstituteAlternateSyntax::new(target, false); let code = "x || (x = 3)"; - tester::test(&allocator, code, code, &mut pass); + assert_eq!( + run(code, Some(CompressOptions { target, ..CompressOptions::default() })), + run(code, None) + ); } #[test] @@ -1944,7 +1938,7 @@ mod test { // Don't fold the existence check to preserve behavior test("var a = Boolean?.(false)", "var a = Boolean?.(!1)"); - test("var a = Boolean(1)", "var a = !!1"); + test("var a = Boolean(1)", "var a = !0"); // Don't fold the existence check to preserve behavior test_same("var a = Boolean?.(1)"); @@ -1952,7 +1946,7 @@ mod test { // Don't fold the existence check to preserve behavior test_same("var a = Boolean?.(x)"); - test("var a = Boolean({})", "var a = !!{}"); + test("var a = Boolean({})", "var a = !0"); // Don't fold the existence check to preserve behavior test_same("var a = Boolean?.({})"); @@ -1962,16 +1956,16 @@ mod test { #[test] fn test_fold_string_constructor() { - test("String()", "''"); - test("var a = String(23)", "var a = '' + 23"); + test("x = String()", "x = ''"); + test("var a = String(23)", "var a = '23'"); // Don't fold the existence check to preserve behavior test_same("var a = String?.(23)"); - test("var a = String('hello')", "var a = '' + 'hello'"); + test("var a = String('hello')", "var a = 'hello'"); // Don't fold the existence check to preserve behavior test_same("var a = String?.('hello')"); - test_same("var s = Symbol();var a = String(s);"); + test_same("var s = Symbol(), a = String(s);"); test_same("var a = String('hello', bar());"); test_same("var a = String({valueOf: function() { return 1; }});"); @@ -1979,10 +1973,10 @@ mod test { #[test] fn test_fold_number_constructor() { - test("Number()", "0"); - test("Number(true)", "1"); - test("Number(false)", "0"); - test("Number('foo')", "NaN"); + test("x = Number()", "x = 0"); + test("x = Number(true)", "x = 1"); + test("x = Number(false)", "x = 0"); + test("x = Number('foo')", "x = NaN"); } #[test] @@ -1994,16 +1988,17 @@ mod test { #[test] fn optional_catch_binding() { - test("try {} catch(e) {}", "try {} catch {}"); - test("try {} catch(e) {foo}", "try {} catch {foo}"); - test_same("try {} catch(e) {e}"); - test_same("try {} catch([e]) {}"); - test_same("try {} catch({e}) {}"); + test("try { foo } catch(e) {}", "try { foo } catch {}"); + test("try { foo } catch(e) {foo}", "try { foo } catch {foo}"); + test_same("try { foo } catch(e) {e}"); + test_same("try { foo } catch([e]) {}"); + test_same("try { foo } catch({e}) {}"); - let allocator = Allocator::default(); let target = ESTarget::ES2018; - let mut pass = super::PeepholeSubstituteAlternateSyntax::new(target, false); - let code = "try {} catch(e) {}"; - tester::test(&allocator, code, code, &mut pass); + let code = "try { foo } catch(e) {}"; + assert_eq!( + run(code, Some(CompressOptions { target, ..CompressOptions::default() })), + run(code, None) + ); } } diff --git a/crates/oxc_minifier/src/ast_passes/remove_unused_code.rs b/crates/oxc_minifier/src/ast_passes/remove_unused_code.rs index b3c2e68ef58ba..82694270d91c5 100644 --- a/crates/oxc_minifier/src/ast_passes/remove_unused_code.rs +++ b/crates/oxc_minifier/src/ast_passes/remove_unused_code.rs @@ -70,21 +70,10 @@ impl RemoveUnusedCode { #[cfg(test)] mod test { - use oxc_allocator::Allocator; - - use crate::tester; - - fn test(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let mut pass = super::RemoveUnusedCode::new(); - tester::test(&allocator, source_text, expected, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } + use crate::tester::{test, test_same}; #[test] + #[ignore] fn simple() { test("var x", ""); test_same("var x = 1"); diff --git a/crates/oxc_minifier/src/ast_passes/statement_fusion.rs b/crates/oxc_minifier/src/ast_passes/statement_fusion.rs index 0c108052a68d0..1d52fd1bec2dd 100644 --- a/crates/oxc_minifier/src/ast_passes/statement_fusion.rs +++ b/crates/oxc_minifier/src/ast_passes/statement_fusion.rs @@ -194,146 +194,126 @@ fn can_merge_block_stmt_member(node: &Statement) -> bool { #[cfg(test)] mod test { - use oxc_allocator::Allocator; + use crate::tester::{test, test_same}; - use crate::tester; + // fn test(before: &str, after: &str) { + // test( + // &("function F(){if(CONDITION){".to_string() + before + "}}"), + // &("function F(){if(CONDITION){".to_string() + after + "}}"), + // ); + // } - fn test(source_text: &str, expected: &str) { - let allocator = Allocator::default(); - let mut pass = super::StatementFusion::new(); - tester::test(&allocator, source_text, expected, &mut pass); - } - - fn test_same(source_text: &str) { - test(source_text, source_text); - } - - fn fuse(before: &str, after: &str) { - test( - &("function F(){if(CONDITION){".to_string() + before + "}}"), - &("function F(){if(CONDITION){".to_string() + after + "}}"), - ); - } - - fn fuse_same(code: &str) { - test_same(&("function F(){if(CONDITION){".to_string() + code + "}}")); - } - - #[test] - fn nothing_to_do() { - fuse_same(""); - fuse_same("a"); - fuse_same("a()"); - fuse_same("if(a()){}"); - } + // fn test_same(code: &str) { + // test_same(&("function F(){if(CONDITION){".to_string() + code + "}}")); + // } #[test] fn fold_block_with_statements() { - fuse("a;b;c", "a,b,c"); - fuse("a();b();c();", "a(),b(),c()"); - fuse("a(),b();c(),d()", "a(),b(),c(),d()"); - fuse("a();b(),c(),d()", "a(),b(),c(),d()"); - fuse("a(),b(),c();d()", "a(),b(),c(),d()"); + test("a;b;c", "a,b,c"); + test("a();b();c();", "a(),b(),c()"); + test("a(),b();c(),d()", "a(),b(),c(),d()"); + test("a();b(),c(),d()", "a(),b(),c(),d()"); + test("a(),b(),c();d()", "a(),b(),c(),d()"); } #[test] fn fold_block_into_if() { - fuse("a;b;c;if(x){}", "if(a,b,c,x){}"); - fuse("a;b;c;if(x,y){}else{}", "if(a,b,c,x,y){}else{}"); - fuse("a;b;c;if(x,y){}", "if(a,b,c,x,y){}"); - fuse("a;b;c;if(x,y,z){}", "if(a,b,c,x,y,z){}"); - fuse("a();if(a()){}a()", "if(a(), a()){}a()"); + test("a;b;c;if(x){}", "a,b,c,x"); + test("a;b;c;if(x,y){}else{}", "a, b, c, !(x, y);"); + test("a;b;c;if(x,y){}", "a, b, c, x, y"); + test("a;b;c;if(x,y,z){}", "a, b, c, x, y, z"); + test("a();if(a()){}a()", "a(), a(), a()"); } #[test] fn fold_block_return() { - fuse("a;b;c;return x", "return a,b,c,x"); - fuse("a;b;c;return x+y", "return a,b,c,x+y"); - fuse("a;b;c;return x;a;b;c", "return a,b,c,x;a,b,c"); + test("a;b;c;return x", "return a,b,c,x"); + test("a;b;c;return x+y", "return a,b,c,x+y"); + test("a();b();c();return x();a();b();c()", "return a(),b(),c(),x()"); } #[test] fn fold_block_throw() { - fuse("a;b;c;throw x", "throw a,b,c,x"); - fuse("a;b;c;throw x+y", "throw a,b,c,x+y"); - fuse("a;b;c;throw x;a;b;c", "throw a,b,c,x;a,b,c"); + test("a;b;c;throw x", "throw a,b,c,x"); + test("a;b;c;throw x+y", "throw a,b,c,x+y"); + test("a();b();c();throw x();a();b();c", "throw a(),b(),c(),x()"); } #[test] fn fold_switch() { - fuse("a;b;c;switch(x){}", "switch(a,b,c,x){}"); + test("a;b;c;switch(x){}", "switch(a,b,c,x){}"); } #[test] fn fuse_into_for_in1() { - fuse("a;b;c;for(x in y){}", "for(x in a,b,c,y){}"); + test("a;b;c;for(x in y){}", "for(x in a,b,c,y);"); } #[test] fn fuse_into_for_in2() { - fuse_same("a();for(var x = b() in y){}"); + test_same("a();for(var x = b() in y);"); } #[test] fn fuse_into_vanilla_for1() { - fuse("a;b;c;for(;g;){}", "for(a,b,c;g;){}"); - fuse("a;b;c;for(d;g;){}", "for(a,b,c,d;g;){}"); - fuse("a;b;c;for(d,e;g;){}", "for(a,b,c,d,e;g;){}"); - fuse_same("a();for(var x;g;){}"); + test("a;b;c;for(;g;){}", "for(a,b,c;g;);"); + test("a;b;c;for(d;g;){}", "for(a,b,c,d;g;);"); + test("a;b;c;for(d,e;g;){}", "for(a,b,c,d,e;g;);"); + test_same("a();for(var x;g;);"); } #[test] fn fuse_into_vanilla_for2() { - fuse("a;b;c;for(var d;g;){}", "a,b,c;for(var d;g;){}"); - fuse("a;b;c;for(let d;g;){}", "a,b,c;for(let d;g;){}"); - fuse("a;b;c;for(const d = 5;g;){}", "a,b,c;for(const d = 5;g;){}"); + test("a;b;c;for(var d;g;){}", "a,b,c;for(var d;g;);"); + test("a;b;c;for(let d;g;){}", "a,b,c;for(let d;g;);"); + test("a;b;c;for(const d = 5;g;){}", "a,b,c;for(const d = 5;g;);"); } #[test] fn fuse_into_label() { - fuse("a;b;c;label:for(x in y){}", "label:for(x in a,b,c,y){}"); - fuse("a;b;c;label:for(;g;){}", "label:for(a,b,c;g;){}"); - fuse("a;b;c;l1:l2:l3:for(;g;){}", "l1:l2:l3:for(a,b,c;g;){}"); - fuse("a;b;c;label:while(true){}", "label:for(a,b,c;true;){}"); + test("a;b;c;label:for(x in y){}", "label:for(x in a,b,c,y);"); + test("a;b;c;label:for(;g;){}", "label:for(a,b,c;g;);"); + test("a;b;c;l1:l2:l3:for(;g;){}", "l1:l2:l3:for(a,b,c;g;);"); + test("a;b;c;label:while(true){}", "label:for(a,b,c;;);"); } #[test] fn fuse_into_block() { - fuse("a;b;c;{d;e;f}", "{a,b,c,d,e,f}"); - fuse( + test("a;b;c;{d;e;f}", "a,b,c,d,e,f"); + test( "a;b; label: { if(q) break label; bar(); }", "label: { if(a,b,q) break label; bar(); }", ); - fuse("a;b;c;{var x;d;e;}", "a,b,c;{var x;d,e;}"); - fuse("a;b;c;label:{break label;d;e;}", "a,b,c;label:{break label;d,e;}"); + test("a;b;c;{var x;d;e;}", "a,b,c;{var x;d,e;}"); + test("a;b;c;label:{break label;d;e;}", "a,b,c"); } #[test] fn fuse_into_switch_cases() { - fuse("switch (_) { case _: a; return b }", "switch (_) { case _: return a, b }"); + test("switch (_) { case _: a; return b }", "switch (_) { case _: return a, b }"); } #[test] fn no_fuse_into_while() { - fuse("a;b;c;while(x){}", "for(a,b,c;x;){}"); + test("a;b;c;while(x){}", "for(a,b,c;x;);"); } #[test] fn no_fuse_into_do() { - fuse("a;b;c;do{}while(x)", "a,b,c;do{}while(x)"); + test("a;b;c;do;while(x)", "a,b,c;do;while(x)"); } #[test] fn no_fuse_into_block() { // Never fuse a statement into a block that contains let/const/class declarations, or you risk // colliding variable names. (unless the AST is normalized). - fuse("a; {b;}", "{a,b;}"); - fuse("a; {b; var a = 1;}", "{a,b; var a = 1;}"); - fuse_same("a; { b; let a = 1; }"); - fuse_same("a; { b; const a = 1; }"); - fuse_same("a; { b; class a {} }"); - fuse_same("a; { b; function a() {} }"); - fuse_same("a; { b; const otherVariable = 1; }"); + test("a; {b;}", "a,b"); + test("a; {b; var a = 1;}", "{a,b; var a = 1;}"); + test_same("a; { b; let a = 1; }"); + test_same("a; { b; const a = 1; }"); + test_same("a; { b; class a {} }"); + test_same("a; { b; function a() {} }"); + test_same("a; { b; const otherVariable = 1; }"); // test( // "function f(a) { if (COND) { a; { b; let a = 1; } } }", diff --git a/crates/oxc_minifier/src/tester.rs b/crates/oxc_minifier/src/tester.rs index 7218fc13f1351..2b8a3c2915c08 100644 --- a/crates/oxc_minifier/src/tester.rs +++ b/crates/oxc_minifier/src/tester.rs @@ -1,59 +1,31 @@ use oxc_allocator::Allocator; use oxc_codegen::{CodeGenerator, CodegenOptions}; use oxc_parser::Parser; -use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; -use oxc_traverse::ReusableTraverseCtx; -use crate::{ - ast_passes::{CompressorPass, Normalize, NormalizeOptions}, - CompressOptions, -}; +use crate::{CompressOptions, Compressor}; -pub fn test<'a, P: CompressorPass<'a>>( - allocator: &'a Allocator, - source_text: &'a str, - expected: &'a str, - pass: &mut P, -) { - test_impl(allocator, source_text, expected, pass, false); +pub fn test_same(source_text: &str) { + test(source_text, source_text); } -pub fn test_impl<'a, P: CompressorPass<'a>>( - allocator: &'a Allocator, - source_text: &'a str, - expected: &'a str, - pass: &mut P, - remove_whitespace: bool, -) { - let result = run(allocator, source_text, Some(pass), remove_whitespace); - let expected = run::

    (allocator, expected, None, remove_whitespace); +pub fn test(source_text: &str, expected: &str) { + let result = run(source_text, Some(CompressOptions::all_true())); + let expected = run(expected, None); assert_eq!(result, expected, "\nfor source\n{source_text}\nexpect\n{expected}\ngot\n{result}"); } -fn run<'a, P: CompressorPass<'a>>( - allocator: &'a Allocator, - source_text: &'a str, - pass: Option<&mut P>, - remove_whitespace: bool, -) -> String { +pub fn run(source_text: &str, options: Option) -> String { + let allocator = Allocator::default(); let source_type = SourceType::mjs(); - let mut program = Parser::new(allocator, source_text, source_type).parse().program; - - if let Some(pass) = pass { - let (symbols, scopes) = - SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree(); - let mut ctx = ReusableTraverseCtx::new(scopes, symbols, allocator); - let normalize_options = NormalizeOptions { convert_while_to_fors: true }; - Normalize::new(normalize_options, CompressOptions::all_false()) - .build(&mut program, &mut ctx); - pass.build(&mut program, &mut ctx); + let mut program = Parser::new(&allocator, source_text, source_type).parse().program; + if let Some(options) = options { + Compressor::new(&allocator, options).build(&mut program); } - CodeGenerator::new() .with_options(CodegenOptions { single_quote: true, - minify: remove_whitespace, + minify: false, ..CodegenOptions::default() }) .build(&program)