diff --git a/crates/oxc_minifier/src/peephole/minimize_statements.rs b/crates/oxc_minifier/src/peephole/minimize_statements.rs index df16cf75e853d..e404713a16038 100644 --- a/crates/oxc_minifier/src/peephole/minimize_statements.rs +++ b/crates/oxc_minifier/src/peephole/minimize_statements.rs @@ -384,8 +384,12 @@ impl<'a> PeepholeOptimizations { if let Some(first_decl) = var_decl.declarations.first_mut() && let Some(first_decl_init) = first_decl.init.as_mut() { - let changed = - Self::substitute_single_use_symbol_in_statement(first_decl_init, result, ctx); + let changed = Self::substitute_single_use_symbol_in_statement( + first_decl_init, + result, + ctx, + false, + ); if changed { ctx.state.changed = true; } @@ -433,8 +437,12 @@ impl<'a> PeepholeOptimizations { ctx: &mut Ctx<'a, '_>, ) { - let changed = - Self::substitute_single_use_symbol_in_statement(&mut expr_stmt.expression, result, ctx); + let changed = Self::substitute_single_use_symbol_in_statement( + &mut expr_stmt.expression, + result, + ctx, + false, + ); if changed { ctx.state.changed = true; } @@ -562,6 +570,7 @@ impl<'a> PeepholeOptimizations { &mut switch_stmt.discriminant, result, ctx, + false, ); if changed { ctx.state.changed = true; @@ -588,7 +597,7 @@ impl<'a> PeepholeOptimizations { ctx: &mut Ctx<'a, '_>, ) -> ControlFlow<()> { let changed = - Self::substitute_single_use_symbol_in_statement(&mut if_stmt.test, result, ctx); + Self::substitute_single_use_symbol_in_statement(&mut if_stmt.test, result, ctx, false); if changed { ctx.state.changed = true; } @@ -747,8 +756,12 @@ impl<'a> PeepholeOptimizations { ctx: &mut Ctx<'a, '_>, ) { if let Some(ret_argument_expr) = &mut ret_stmt.argument { - let changed = - Self::substitute_single_use_symbol_in_statement(ret_argument_expr, result, ctx); + let changed = Self::substitute_single_use_symbol_in_statement( + ret_argument_expr, + result, + ctx, + false, + ); if changed { ctx.state.changed = true; } @@ -799,8 +812,12 @@ impl<'a> PeepholeOptimizations { ctx: &mut Ctx<'a, '_>, ) { - let changed = - Self::substitute_single_use_symbol_in_statement(&mut throw_stmt.argument, result, ctx); + let changed = Self::substitute_single_use_symbol_in_statement( + &mut throw_stmt.argument, + result, + ctx, + false, + ); if changed { ctx.state.changed = true; } @@ -830,10 +847,12 @@ impl<'a> PeepholeOptimizations { if let Some(first_decl) = var_decl.declarations.first_mut() && let Some(first_decl_init) = first_decl.init.as_mut() { + let is_block_scoped_decl = !first_decl.kind.is_var(); let changed = Self::substitute_single_use_symbol_in_statement( first_decl_init, result, ctx, + is_block_scoped_decl, ); if changed { ctx.state.changed = true; @@ -843,7 +862,7 @@ impl<'a> PeepholeOptimizations { match_expression!(ForStatementInit) => { let init = init.to_expression_mut(); let changed = - Self::substitute_single_use_symbol_in_statement(init, result, ctx); + Self::substitute_single_use_symbol_in_statement(init, result, ctx, false); if changed { ctx.state.changed = true; } @@ -922,10 +941,12 @@ impl<'a> PeepholeOptimizations { // That is evaluated before the right hand side is evaluated. So, in that case, skip the single use substitution. if !matches!(&for_in_stmt.left, ForStatementLeft::VariableDeclaration(var_decl) if var_decl.has_init()) { + let is_block_scoped_decl = matches!(&for_in_stmt.left, ForStatementLeft::VariableDeclaration(var_decl) if !var_decl.kind.is_var()); let changed = Self::substitute_single_use_symbol_in_statement( &mut for_in_stmt.right, result, ctx, + is_block_scoped_decl, ); if changed { ctx.state.changed = true; @@ -1005,8 +1026,13 @@ impl<'a> PeepholeOptimizations { result: &mut Vec<'a, Statement<'a>>, ctx: &mut Ctx<'a, '_>, ) { - let changed = - Self::substitute_single_use_symbol_in_statement(&mut for_of_stmt.right, result, ctx); + let is_block_scoped_decl = matches!(&for_of_stmt.left, ForStatementLeft::VariableDeclaration(var_decl) if !var_decl.kind.is_var()); + let changed = Self::substitute_single_use_symbol_in_statement( + &mut for_of_stmt.right, + result, + ctx, + is_block_scoped_decl, + ); if changed { ctx.state.changed = true; } @@ -1100,6 +1126,7 @@ impl<'a> PeepholeOptimizations { expr_in_stmt: &mut Expression<'a>, stmts: &mut Vec<'a, Statement<'a>>, ctx: &Ctx<'a, '_>, + non_scoped_literal_only: bool, ) -> bool { // TODO: we should skip this compression when direct eval exists // because the code inside eval may reference the variable @@ -1139,6 +1166,9 @@ impl<'a> PeepholeOptimizations { { return true; } + if non_scoped_literal_only && !prev_decl_init.is_literal_value(false, ctx) { + return true; + } let replaced = Self::substitute_single_use_symbol_in_expression( expr_in_stmt, &prev_decl_id.name, diff --git a/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs b/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs index 856d9d13da76e..c6642b2c473d8 100644 --- a/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs +++ b/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs @@ -156,17 +156,46 @@ fn test_inline_single_use_variable() { ); test( - "function wrapper() { var x = foo; for (var i = x; i < 10; i++) console.log(i) }", - "function wrapper() { for (var i = foo; i < 10; i++) console.log(i) }", + "function wrapper() { var x = 1; for (var i = x; i < 10; i++) console.log(i) }", + "function wrapper() { for (var i = 1; i < 10; i++) console.log(i) }", ); test( - "function wrapper() { var i, x = foo; for (i = x; i < 10; i++) console.log(i) }", - "function wrapper() { var i; for (i = foo; i < 10; i++) console.log(i) }", + "function wrapper() { var x = 1; for (let i = x; i < 10; i++) console.log(i) }", + "function wrapper() { for (let i = 1; i < 10; i++) console.log(i) }", + ); + test( + "function wrapper() { var x = function () { return console.log(i), 1 }; for (let i = x(); i < 10; i++) console.log(i) }", + "function wrapper() { var x = function () { return console.log(i), 1 }; for (let i = x(); i < 10; i++) console.log(i) }", + ); + // this is fine because `let j` inside the block cannot be referenced from `var i = j` + test( + "function wrapper() { var x = j; for (var i = x; i < 10; i++) { let j; console.log(i) } }", + "function wrapper() { for (var i = j; i < 10; i++) { let j; console.log(i) } }", + ); + test( + "function wrapper() { var i, x = 1; for (i = x; i < 10; i++) console.log(i) }", + "function wrapper() { var i; for (i = 1; i < 10; i++) console.log(i) }", + ); + test( + "function wrapper() { var x = i; for (var i = x; i < 10; i++) console.log(i) }", + "function wrapper() { for (var i = i; i < 10; i++) console.log(i) }", + ); + test( + "function wrapper() { var x = i; for (let i = x; i < 10; i++) console.log(i) }", + "function wrapper() { var x = i; for (let i = x; i < 10; i++) console.log(i) }", ); test( "function wrapper() { var x = {}; for (var a in x) console.log(a) }", "function wrapper() { for (var a in {}) console.log(a) }", ); + test( + "function wrapper() { var x = a; for (var a in x) console.log(a) }", + "function wrapper() { for (var a in a) console.log(a) }", + ); + test( + "function wrapper() { var x = a; for (let a in x) console.log(a) }", + "function wrapper() { var x = a; for (let a in x) console.log(a) }", + ); test( "function wrapper() { var x = {}; for (var a = 0 in x) console.log(a) }", "function wrapper() { var x = {}; for (var a = 0 in x) console.log(a) }", @@ -175,6 +204,14 @@ fn test_inline_single_use_variable() { "function wrapper() { var x = []; for (var a of x) console.log(a) }", "function wrapper() { for (var a of []) console.log(a) }", ); + test( + "function wrapper() { var x = a; for (var a of x) console.log(a) }", + "function wrapper() { for (var a of a) console.log(a) }", + ); + test( + "function wrapper() { var x = a; for (let a of x) console.log(a) }", + "function wrapper() { var x = a; for (let a of x) console.log(a) }", + ); } #[test] diff --git a/tasks/track_memory_allocations/allocs_minifier.snap b/tasks/track_memory_allocations/allocs_minifier.snap index b7dbe709c2be3..6572edae21201 100644 --- a/tasks/track_memory_allocations/allocs_minifier.snap +++ b/tasks/track_memory_allocations/allocs_minifier.snap @@ -1,12 +1,12 @@ File | File size || Sys allocs | Sys reallocs || Arena allocs | Arena reallocs | Arena bytes ------------------------------------------------------------------------------------------------------------------------------------------- -checker.ts | 2.92 MB || 84058 | 14190 || 153533 | 29395 | 5.615 MB +checker.ts | 2.92 MB || 84073 | 14190 || 153691 | 29463 | 5.625 MB cal.com.tsx | 1.06 MB || 40525 | 3033 || 37074 | 4733 | 1.654 MB RadixUIAdoptionSection.jsx | 2.52 kB || 82 | 8 || 30 | 6 | 992 B -pdf.mjs | 567.30 kB || 19572 | 2900 || 47384 | 7770 | 1.623 MB +pdf.mjs | 567.30 kB || 19576 | 2900 || 47400 | 7781 | 1.624 MB antd.js | 6.69 MB || 99854 | 13518 || 331725 | 70117 | 17.407 MB