diff --git a/Cargo.lock b/Cargo.lock index e3d81e32d8bdd..3347401638477 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2037,7 +2037,6 @@ dependencies = [ "oxc_ast", "oxc_ast_visit", "oxc_codegen", - "oxc_data_structures", "oxc_ecmascript", "oxc_mangler", "oxc_parser", diff --git a/crates/oxc_minifier/Cargo.toml b/crates/oxc_minifier/Cargo.toml index 822e409364500..2ad308d74bfba 100644 --- a/crates/oxc_minifier/Cargo.toml +++ b/crates/oxc_minifier/Cargo.toml @@ -25,7 +25,6 @@ oxc_allocator = { workspace = true } oxc_ast = { workspace = true } oxc_ast_visit = { workspace = true } oxc_codegen = { workspace = true } -oxc_data_structures = { workspace = true, features = ["stack"] } oxc_ecmascript = { workspace = true } oxc_mangler = { workspace = true } oxc_parser = { workspace = true } diff --git a/crates/oxc_minifier/src/peephole/fold_constants.rs b/crates/oxc_minifier/src/peephole/fold_constants.rs index 596bd81855f8d..a6e3f6732dae0 100644 --- a/crates/oxc_minifier/src/peephole/fold_constants.rs +++ b/crates/oxc_minifier/src/peephole/fold_constants.rs @@ -11,23 +11,18 @@ use oxc_traverse::Ancestor; use crate::ctx::Ctx; -use super::{PeepholeOptimizations, State}; +use super::PeepholeOptimizations; impl<'a> PeepholeOptimizations { /// Constant Folding /// /// - pub fn fold_constants_exit_expression( - &self, - expr: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + pub fn fold_constants_exit_expression(&self, expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { match expr { Expression::TemplateLiteral(t) => { - self.try_inline_values_in_template_literal(t, state, ctx); + self.try_inline_values_in_template_literal(t, ctx); } - Expression::ObjectExpression(e) => self.fold_object_spread(e, state, ctx), + Expression::ObjectExpression(e) => self.fold_object_spread(e, ctx), _ => {} } @@ -43,7 +38,7 @@ impl<'a> PeepholeOptimizations { _ => None, } { *expr = folded_expr; - state.changed = true; + ctx.state.changed = true; } // Save `const value = false` into constant values. @@ -664,12 +659,7 @@ impl<'a> PeepholeOptimizations { None } - fn fold_object_spread( - &self, - e: &mut ObjectExpression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn fold_object_spread(&self, e: &mut ObjectExpression<'a>, ctx: &mut Ctx<'a, '_>) { let (new_size, should_fold) = e.properties.iter().fold((0, false), |(new_size, should_fold), p| { let ObjectPropertyKind::SpreadProperty(spread_element) = p else { @@ -745,7 +735,7 @@ impl<'a> PeepholeOptimizations { } e.properties = new_properties; - state.changed = true; + ctx.state.changed = true; } fn is_spread_inlineable_object_literal( @@ -774,7 +764,7 @@ impl<'a> PeepholeOptimizations { fn try_inline_values_in_template_literal( &self, t: &mut TemplateLiteral<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { let has_expr_to_inline = t @@ -824,7 +814,7 @@ impl<'a> PeepholeOptimizations { } } - state.changed = true; + ctx.state.changed = true; } } diff --git a/crates/oxc_minifier/src/peephole/minimize_conditions.rs b/crates/oxc_minifier/src/peephole/minimize_conditions.rs index 6994e2c32e561..44e8964b6d1b1 100644 --- a/crates/oxc_minifier/src/peephole/minimize_conditions.rs +++ b/crates/oxc_minifier/src/peephole/minimize_conditions.rs @@ -6,7 +6,7 @@ use oxc_syntax::es_target::ESTarget; use crate::ctx::Ctx; -use super::{PeepholeOptimizations, State}; +use super::PeepholeOptimizations; /// Minimize Conditions /// @@ -15,7 +15,6 @@ impl<'a> PeepholeOptimizations { pub fn minimize_conditions_exit_expression( &self, expr: &mut Expression<'a>, - state: &mut State, ctx: &mut Ctx<'a, '_>, ) { let mut changed = false; @@ -57,7 +56,7 @@ impl<'a> PeepholeOptimizations { } } if changed { - state.changed = true; + ctx.state.changed = true; } } diff --git a/crates/oxc_minifier/src/peephole/minimize_for_statement.rs b/crates/oxc_minifier/src/peephole/minimize_for_statement.rs index fc196645fca3f..665d4a97746ea 100644 --- a/crates/oxc_minifier/src/peephole/minimize_for_statement.rs +++ b/crates/oxc_minifier/src/peephole/minimize_for_statement.rs @@ -4,16 +4,11 @@ use oxc_span::GetSpan; use crate::ctx::Ctx; -use super::{PeepholeOptimizations, State}; +use super::PeepholeOptimizations; impl<'a> PeepholeOptimizations { /// `mangleFor`: - pub fn minimize_for_statement( - &self, - for_stmt: &mut ForStatement<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + pub fn minimize_for_statement(&self, for_stmt: &mut ForStatement<'a>, ctx: &mut Ctx<'a, '_>) { // Get the first statement in the loop let mut first = &for_stmt.body; if let Statement::BlockStatement(block_stmt) = first { @@ -66,7 +61,7 @@ impl<'a> PeepholeOptimizations { let alternate = if_stmt.alternate.take(); for_stmt.body = Self::drop_first_statement(span, body, alternate, ctx); - state.changed = true; + ctx.state.changed = true; return; } // "for (;;) if (x) y(); else break;" => "for (; x;) y();" @@ -103,7 +98,7 @@ impl<'a> PeepholeOptimizations { let consequent = if_stmt.consequent.take_in(ctx.ast); for_stmt.body = Self::drop_first_statement(span, body, Some(consequent), ctx); - state.changed = true; + ctx.state.changed = true; } } diff --git a/crates/oxc_minifier/src/peephole/minimize_if_statement.rs b/crates/oxc_minifier/src/peephole/minimize_if_statement.rs index ba6e06158690b..a2eb6e70a04b9 100644 --- a/crates/oxc_minifier/src/peephole/minimize_if_statement.rs +++ b/crates/oxc_minifier/src/peephole/minimize_if_statement.rs @@ -6,17 +6,16 @@ use oxc_span::GetSpan; use crate::ctx::Ctx; -use super::{PeepholeOptimizations, State}; +use super::PeepholeOptimizations; impl<'a> PeepholeOptimizations { /// `MangleIf`: pub fn try_minimize_if( &self, if_stmt: &mut IfStatement<'a>, - state: &mut State, ctx: &mut Ctx<'a, '_>, ) -> Option> { - self.wrap_to_avoid_ambiguous_else(if_stmt, state, ctx); + self.wrap_to_avoid_ambiguous_else(if_stmt, ctx); if let Statement::ExpressionStatement(expr_stmt) = &mut if_stmt.consequent { if if_stmt.alternate.is_none() { let (op, e) = match &mut if_stmt.test { @@ -48,7 +47,7 @@ impl<'a> PeepholeOptimizations { { // "if (a) {}" => "a;" let mut expr = if_stmt.test.take_in(ctx.ast); - self.remove_unused_expression(&mut expr, state, ctx); + self.remove_unused_expression(&mut expr, ctx); return Some(ctx.ast.statement_expression(if_stmt.span, expr)); } else if let Some(Statement::ExpressionStatement(expr_stmt)) = &mut if_stmt.alternate { let (op, e) = match &mut if_stmt.test { @@ -71,7 +70,7 @@ impl<'a> PeepholeOptimizations { if_stmt.test = unary_expr.argument.take_in(ctx.ast); if_stmt.consequent = stmt.take_in(ctx.ast); if_stmt.alternate = None; - state.changed = true; + ctx.state.changed = true; } // "if (a) {} else return b;" => "if (!a) return b;" _ => { @@ -82,8 +81,8 @@ impl<'a> PeepholeOptimizations { ); if_stmt.consequent = stmt.take_in(ctx.ast); if_stmt.alternate = None; - self.try_minimize_if(if_stmt, state, ctx); - state.changed = true; + self.try_minimize_if(if_stmt, ctx); + ctx.state.changed = true; } } } @@ -96,8 +95,8 @@ impl<'a> PeepholeOptimizations { // "if (!a) return b; else return c;" => "if (a) return c; else return b;" if_stmt.test = unary_expr.argument.take_in(ctx.ast); std::mem::swap(&mut if_stmt.consequent, alternate); - self.wrap_to_avoid_ambiguous_else(if_stmt, state, ctx); - state.changed = true; + self.wrap_to_avoid_ambiguous_else(if_stmt, ctx); + ctx.state.changed = true; } } // "if (a) return b; else {}" => "if (a) return b;" is handled by remove_dead_code @@ -116,7 +115,7 @@ impl<'a> PeepholeOptimizations { ctx, ); if_stmt.consequent = if2_stmt.consequent.take_in(ctx.ast); - state.changed = true; + ctx.state.changed = true; } } } @@ -127,12 +126,7 @@ impl<'a> PeepholeOptimizations { /// Wrap to avoid ambiguous else. /// `if (foo) if (bar) baz else quaz` -> `if (foo) { if (bar) baz else quaz }` #[expect(clippy::cast_possible_truncation)] - fn wrap_to_avoid_ambiguous_else( - &self, - if_stmt: &mut IfStatement<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn wrap_to_avoid_ambiguous_else(&self, if_stmt: &mut IfStatement<'a>, ctx: &mut Ctx<'a, '_>) { if let Statement::IfStatement(if2) = &mut if_stmt.consequent { if if2.consequent.is_jump_statement() && if2.alternate.is_some() { let scope_id = ScopeId::new(ctx.scoping.scoping().scopes_len() as u32); @@ -143,7 +137,7 @@ impl<'a> PeepholeOptimizations { scope_id, ), )); - state.changed = true; + ctx.state.changed = true; } } } diff --git a/crates/oxc_minifier/src/peephole/minimize_statements.rs b/crates/oxc_minifier/src/peephole/minimize_statements.rs index bcfe6dc56c3a4..008e695a53b7b 100644 --- a/crates/oxc_minifier/src/peephole/minimize_statements.rs +++ b/crates/oxc_minifier/src/peephole/minimize_statements.rs @@ -10,7 +10,7 @@ use oxc_traverse::Ancestor; use crate::{ctx::Ctx, keep_var::KeepVar}; -use super::{PeepholeOptimizations, State}; +use super::PeepholeOptimizations; impl<'a> PeepholeOptimizations { /// `mangleStmts`: @@ -30,12 +30,7 @@ impl<'a> PeepholeOptimizations { /// /// ## MinimizeExitPoints: /// - pub fn minimize_statements( - &self, - stmts: &mut Vec<'a, Statement<'a>>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + pub fn minimize_statements(&self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut Ctx<'a, '_>) { let mut result: Vec<'a, Statement<'a>> = ctx.ast.vec_with_capacity(stmts.len()); let mut is_control_flow_dead = false; let mut keep_var = KeepVar::new(ctx.ast); @@ -56,7 +51,6 @@ impl<'a> PeepholeOptimizations { &mut new_stmts, &mut result, &mut is_control_flow_dead, - state, ctx, ) .is_break() @@ -73,16 +67,20 @@ impl<'a> PeepholeOptimizations { match last_stmt { // "while (x) { y(); continue; }" => "while (x) { y(); }" Statement::ContinueStatement(s) if s.label.is_none() => { + let mut changed = false; if let Some(Ancestor::ForStatementBody(_)) = ctx.ancestors().nth(1) { result.pop(); - state.changed = true; + changed = true; + } + if changed { + ctx.state.changed = true; } } // "function f() { x(); return; }" => "function f() { x(); }" Statement::ReturnStatement(s) if s.argument.is_none() => { if let Ancestor::FunctionBodyStatements(_) = ctx.parent() { result.pop(); - state.changed = true; + ctx.state.changed = true; } } _ => {} @@ -102,7 +100,7 @@ impl<'a> PeepholeOptimizations { break 'return_loop; } } - state.changed = true; + ctx.state.changed = true; // "a(); return b;" => "return a(), b;" let last_stmt = result.pop().unwrap(); let Statement::ReturnStatement(mut last_return) = last_stmt else { @@ -130,7 +128,7 @@ impl<'a> PeepholeOptimizations { break 'return_loop; }; - state.changed = true; + ctx.state.changed = true; let last_stmt = result.pop().unwrap(); let Statement::ReturnStatement(last_return) = last_stmt else { unreachable!() @@ -180,7 +178,7 @@ impl<'a> PeepholeOptimizations { right, ctx, ); - self.minimize_conditions_exit_expression(&mut expr, state, ctx); + self.minimize_conditions_exit_expression(&mut expr, ctx); expr }; let last_return_stmt = @@ -196,7 +194,7 @@ impl<'a> PeepholeOptimizations { let prev_stmt = &result[prev_index]; match prev_stmt { Statement::ExpressionStatement(_) => { - state.changed = true; + ctx.state.changed = true; // "a(); throw b;" => "throw a(), b;" let last_stmt = result.pop().unwrap(); let Statement::ThrowStatement(mut last_throw) = last_stmt else { @@ -226,7 +224,7 @@ impl<'a> PeepholeOptimizations { break 'throw_loop; }; - state.changed = true; + ctx.state.changed = true; let last_stmt = result.pop().unwrap(); let Statement::ThrowStatement(last_throw) = last_stmt else { unreachable!() @@ -267,7 +265,7 @@ impl<'a> PeepholeOptimizations { right, ctx, ); - self.minimize_conditions_exit_expression(&mut expr, state, ctx); + self.minimize_conditions_exit_expression(&mut expr, ctx); expr }; let last_throw_stmt = ctx.ast.statement_throw(right_span, argument); @@ -289,7 +287,7 @@ impl<'a> PeepholeOptimizations { stmts: &mut Vec<'a, Statement<'a>>, result: &mut Vec<'a, Statement<'a>>, is_control_flow_dead: &mut bool, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) -> ControlFlow<()> { match stmt { @@ -303,35 +301,35 @@ impl<'a> PeepholeOptimizations { result.push(Statement::ContinueStatement(s)); } Statement::VariableDeclaration(var_decl) => { - self.handle_variable_declaration(var_decl, result, state, ctx); + self.handle_variable_declaration(var_decl, result, ctx); } Statement::ExpressionStatement(expr_stmt) => { - self.handle_expression_statement(expr_stmt, result, state, ctx); + self.handle_expression_statement(expr_stmt, result, ctx); } Statement::SwitchStatement(switch_stmt) => { - self.handle_switch_statement(switch_stmt, result, state, ctx); + self.handle_switch_statement(switch_stmt, result, ctx); } Statement::IfStatement(if_stmt) => { - if self.handle_if_statement(i, stmts, if_stmt, result, state, ctx).is_break() { + if self.handle_if_statement(i, stmts, if_stmt, result, ctx).is_break() { return ControlFlow::Break(()); } } Statement::ReturnStatement(ret_stmt) => { - self.handle_return_statement(ret_stmt, result, is_control_flow_dead, state, ctx); + self.handle_return_statement(ret_stmt, result, is_control_flow_dead, ctx); } Statement::ThrowStatement(throw_stmt) => { - self.handle_throw_statement(throw_stmt, result, is_control_flow_dead, state, ctx); + self.handle_throw_statement(throw_stmt, result, is_control_flow_dead, ctx); } Statement::ForStatement(for_stmt) => { - self.handle_for_statement(for_stmt, result, state, ctx); + self.handle_for_statement(for_stmt, result, ctx); } Statement::ForInStatement(for_in_stmt) => { - self.handle_for_in_statement(for_in_stmt, result, state, ctx); + self.handle_for_in_statement(for_in_stmt, result, ctx); } Statement::ForOfStatement(for_of_stmt) => { - self.handle_for_of_statement(for_of_stmt, result, state); + self.handle_for_of_statement(for_of_stmt, result, ctx); } - Statement::BlockStatement(block_stmt) => self.handle_block(result, block_stmt, state), + Statement::BlockStatement(block_stmt) => self.handle_block(result, block_stmt, ctx), stmt => result.push(stmt), } ControlFlow::Continue(()) @@ -375,7 +373,7 @@ impl<'a> PeepholeOptimizations { &self, var_decl: Box<'a, VariableDeclaration<'a>>, result: &mut Vec<'a, Statement<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { // If `join_vars` is off, but there are unused declarators ... just join them to make our code simpler. @@ -388,13 +386,13 @@ impl<'a> PeepholeOptimizations { if let Some(Statement::VariableDeclaration(prev_var_decl)) = result.last() { if var_decl.kind == prev_var_decl.kind { - state.changed = true; + ctx.state.changed = true; } } let VariableDeclaration { span, kind, declarations, declare } = var_decl.unbox(); for mut decl in declarations { if Self::should_remove_unused_declarator(&decl, ctx) { - state.changed = true; + ctx.state.changed = true; if let Some(init) = decl.init.take() { if init.may_have_side_effects(ctx) { result.push(ctx.ast.statement_expression(init.span(), init)); @@ -418,7 +416,7 @@ impl<'a> PeepholeOptimizations { &self, mut expr_stmt: Box<'a, ExpressionStatement<'a>>, result: &mut Vec<'a, Statement<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if ctx.options().sequences { @@ -427,7 +425,7 @@ impl<'a> PeepholeOptimizations { let b = &mut expr_stmt.expression; expr_stmt.expression = Self::join_sequence(a, b, ctx); result.pop(); - state.changed = true; + ctx.state.changed = true; } } result.push(Statement::ExpressionStatement(expr_stmt)); @@ -437,7 +435,7 @@ impl<'a> PeepholeOptimizations { &self, mut switch_stmt: Box<'a, SwitchStatement<'a>>, result: &mut Vec<'a, Statement<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if ctx.options().sequences { @@ -446,7 +444,7 @@ impl<'a> PeepholeOptimizations { let b = &mut switch_stmt.discriminant; switch_stmt.discriminant = Self::join_sequence(a, b, ctx); result.pop(); - state.changed = true; + ctx.state.changed = true; } } result.push(Statement::SwitchStatement(switch_stmt)); @@ -459,7 +457,7 @@ impl<'a> PeepholeOptimizations { stmts: &mut Vec<'a, Statement<'a>>, mut if_stmt: Box<'a, IfStatement<'a>>, result: &mut Vec<'a, Statement<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) -> ControlFlow<()> { // Absorb a previous expression statement @@ -469,7 +467,7 @@ impl<'a> PeepholeOptimizations { let b = &mut if_stmt.test; if_stmt.test = Self::join_sequence(a, b, ctx); result.pop(); - state.changed = true; + ctx.state.changed = true; } if if_stmt.consequent.is_jump_statement() { @@ -493,7 +491,7 @@ impl<'a> PeepholeOptimizations { ctx, ); result.pop(); - state.changed = true; + ctx.state.changed = true; } } @@ -554,7 +552,7 @@ impl<'a> PeepholeOptimizations { ctx.ast.vec_from_iter(drained_stmts) }; - self.minimize_statements(&mut body, state, ctx); + self.minimize_statements(&mut body, ctx); let span = if body.is_empty() { if_stmt.consequent.span() } else { @@ -573,10 +571,10 @@ impl<'a> PeepholeOptimizations { }; let mut if_stmt = ctx.ast.if_statement(test.span(), test, consequent, None); let if_stmt = self - .try_minimize_if(&mut if_stmt, state, ctx) + .try_minimize_if(&mut if_stmt, ctx) .unwrap_or_else(|| Statement::IfStatement(ctx.ast.alloc(if_stmt))); result.push(if_stmt); - state.changed = true; + ctx.state.changed = true; return ControlFlow::Break(()); } } @@ -589,10 +587,10 @@ impl<'a> PeepholeOptimizations { if if_stmt.consequent.is_jump_statement() { if let Some(stmt) = if_stmt.alternate.take() { if let Statement::BlockStatement(block_stmt) = stmt { - self.handle_block(result, block_stmt, state); + self.handle_block(result, block_stmt, ctx); } else { result.push(stmt); - state.changed = true; + ctx.state.changed = true; } continue; } @@ -614,7 +612,7 @@ impl<'a> PeepholeOptimizations { mut ret_stmt: Box<'a, ReturnStatement<'a>>, result: &mut Vec<'a, Statement<'a>>, is_control_flow_dead: &mut bool, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if ctx.options().sequences { @@ -623,7 +621,7 @@ impl<'a> PeepholeOptimizations { let a = &mut prev_expr_stmt.expression; *argument = Self::join_sequence(a, argument, ctx); result.pop(); - state.changed = true; + ctx.state.changed = true; } } } @@ -636,7 +634,7 @@ impl<'a> PeepholeOptimizations { mut throw_stmt: Box<'a, ThrowStatement<'a>>, result: &mut Vec<'a, Statement<'a>>, is_control_flow_dead: &mut bool, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if ctx.options().sequences { @@ -645,7 +643,7 @@ impl<'a> PeepholeOptimizations { let b = &mut throw_stmt.argument; throw_stmt.argument = Self::join_sequence(a, b, ctx); result.pop(); - state.changed = true; + ctx.state.changed = true; } } result.push(Statement::ThrowStatement(throw_stmt)); @@ -656,7 +654,7 @@ impl<'a> PeepholeOptimizations { &self, mut for_stmt: Box<'a, ForStatement<'a>>, result: &mut Vec<'a, Statement<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if ctx.options().sequences { @@ -667,14 +665,14 @@ impl<'a> PeepholeOptimizations { let a = &mut prev_expr_stmt.expression; *init = Self::join_sequence(a, init, ctx); result.pop(); - state.changed = true; + ctx.state.changed = true; } } else { for_stmt.init = Some(ForStatementInit::from( prev_expr_stmt.expression.take_in(ctx.ast), )); result.pop(); - state.changed = true; + ctx.state.changed = true; } } Some(Statement::VariableDeclaration(prev_var_decl)) => { @@ -686,7 +684,7 @@ impl<'a> PeepholeOptimizations { .declarations .splice(0..0, prev_var_decl.declarations.drain(..)); result.pop(); - state.changed = true; + ctx.state.changed = true; } } } @@ -696,7 +694,7 @@ impl<'a> PeepholeOptimizations { unreachable!() }; for_stmt.init = Some(ForStatementInit::VariableDeclaration(prev_var_decl)); - state.changed = true; + ctx.state.changed = true; } } _ => {} @@ -709,7 +707,7 @@ impl<'a> PeepholeOptimizations { &self, mut for_in_stmt: Box<'a, ForInStatement<'a>>, result: &mut Vec<'a, Statement<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if ctx.options().sequences { @@ -740,7 +738,7 @@ impl<'a> PeepholeOptimizations { let a = &mut prev_expr_stmt.expression; for_in_stmt.right = Self::join_sequence(a, &mut for_in_stmt.right, ctx); result.pop(); - state.changed = true; + ctx.state.changed = true; } } // "var a; for (a in b) c" => "for (var a in b) c" @@ -768,7 +766,7 @@ impl<'a> PeepholeOptimizations { }; for_in_stmt.left = ForStatementLeft::VariableDeclaration(prev_var_decl); - state.changed = true; + ctx.state.changed = true; } } } @@ -784,7 +782,7 @@ impl<'a> PeepholeOptimizations { &self, mut for_of_stmt: Box<'a, ForOfStatement<'a>>, result: &mut Vec<'a, Statement<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { // "var a; for (a of b) c" => "for (var a of b) c" if let Some(Statement::VariableDeclaration(prev_var_decl)) = result.last_mut() { @@ -809,7 +807,7 @@ impl<'a> PeepholeOptimizations { unreachable!() }; for_of_stmt.left = ForStatementLeft::VariableDeclaration(prev_var_decl); - state.changed = true; + ctx.state.changed = true; } } } @@ -823,14 +821,14 @@ impl<'a> PeepholeOptimizations { &self, result: &mut Vec<'a, Statement<'a>>, block_stmt: Box<'a, BlockStatement<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { let keep_block = block_stmt.body.iter().any(Self::statement_cares_about_scope); if keep_block { result.push(Statement::BlockStatement(block_stmt)); } else { result.append(&mut block_stmt.unbox().body); - state.changed = true; + ctx.state.changed = true; } } diff --git a/crates/oxc_minifier/src/peephole/mod.rs b/crates/oxc_minifier/src/peephole/mod.rs index d7382d921017b..c130808e1b5a3 100644 --- a/crates/oxc_minifier/src/peephole/mod.rs +++ b/crates/oxc_minifier/src/peephole/mod.rs @@ -23,8 +23,6 @@ use rustc_hash::FxHashSet; use oxc_allocator::Vec; use oxc_ast::ast::*; -use oxc_data_structures::stack::NonEmptyStack; -use oxc_syntax::scope::ScopeId; use oxc_traverse::{ReusableTraverseCtx, Traverse, traverse_mut_with_ctx}; use crate::{ @@ -34,32 +32,18 @@ use crate::{ pub use self::normalize::{Normalize, NormalizeOptions}; -#[derive(Debug, Default, Clone, Copy)] -pub struct State { - pub changed: bool, -} - pub struct PeepholeOptimizations { /// Walk the ast in a fixed point loop until no changes are made. /// `prev_function_changed`, `functions_changed` and `current_function` track changes /// in top level and each function. No minification code are run if the function is not changed /// in the previous walk. iteration: u8, - prev_functions_changed: FxHashSet, - functions_changed: FxHashSet, - /// Track the current function as a stack. - current_function: - NonEmptyStack<(ScopeId, /* prev changed */ bool, /* current changed */ bool)>, + changed: bool, } impl<'a> PeepholeOptimizations { pub fn new() -> Self { - Self { - iteration: 0, - prev_functions_changed: FxHashSet::default(), - functions_changed: FxHashSet::default(), - current_function: NonEmptyStack::new((ScopeId::new(0), true, false)), - } + Self { iteration: 0, changed: false } } pub fn build( @@ -76,12 +60,11 @@ impl<'a> PeepholeOptimizations { ctx: &mut ReusableTraverseCtx<'a, MinifierState<'a>>, ) { loop { + self.changed = false; self.build(program, ctx); - if self.functions_changed.is_empty() { + if !self.changed { break; } - self.prev_functions_changed.clear(); - std::mem::swap(&mut self.prev_functions_changed, &mut self.functions_changed); if self.iteration > 10 { debug_assert!(false, "Ran loop more than 10 times."); break; @@ -90,33 +73,6 @@ impl<'a> PeepholeOptimizations { } } - fn mark_current_function_as_changed(&mut self) { - let (_scope_id, _prev_changed, current_changed) = self.current_function.last_mut(); - *current_changed = true; - } - - #[inline] - fn is_prev_function_changed(&self) -> bool { - true - // let (_, prev_changed, _) = self.current_function.last(); - // *prev_changed - } - - fn enter_program_or_function(&mut self, scope_id: ScopeId) { - self.current_function.push(( - scope_id, - self.iteration == 0 || self.prev_functions_changed.contains(&scope_id), - false, - )); - } - - fn exit_program_or_function(&mut self) { - let (scope_id, _, changed) = self.current_function.pop(); - if changed { - self.functions_changed.insert(scope_id); - } - } - pub fn commutative_pair<'x, A, F, G, RetF: 'x, RetG: 'x>( pair: (&'x A, &'x A), check_a: F, @@ -145,13 +101,11 @@ impl<'a> PeepholeOptimizations { } impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { - fn enter_program(&mut self, program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) { - self.enter_program_or_function(program.scope_id()); + fn enter_program(&mut self, _program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + ctx.state.changed = false; } fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { - self.exit_program_or_function(); - let refs_before = ctx.scoping().resolved_references().flatten().copied().collect::>(); let mut counter = ReferencesCounter::default(); @@ -159,26 +113,12 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { for reference_id_to_remove in refs_before.difference(&counter.refs) { ctx.scoping_mut().delete_reference(*reference_id_to_remove); } - } - - fn enter_function(&mut self, func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) { - self.enter_program_or_function(func.scope_id()); - } - - fn exit_function(&mut self, _: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) { - self.exit_program_or_function(); + self.changed = ctx.state.changed; } fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.minimize_statements(stmts, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + self.minimize_statements(stmts, &mut ctx); } fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { @@ -187,46 +127,26 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { } fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); self.try_fold_stmt_in_boolean_context(stmt, &mut ctx); - self.remove_dead_code_exit_statement(stmt, &mut state, &mut ctx); + self.remove_dead_code_exit_statement(stmt, &mut ctx); if let Statement::IfStatement(if_stmt) = stmt { - if let Some(folded_stmt) = self.try_minimize_if(if_stmt, &mut state, &mut ctx) { + if let Some(folded_stmt) = self.try_minimize_if(if_stmt, &mut ctx) { *stmt = folded_stmt; - self.mark_current_function_as_changed(); + ctx.state.changed = true; } } - if state.changed { - self.mark_current_function_as_changed(); - } } fn exit_for_statement(&mut self, stmt: &mut ForStatement<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } - let mut state = State::default(); let mut ctx = Ctx::new(ctx); - self.minimize_for_statement(stmt, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + self.minimize_for_statement(stmt, &mut ctx); } fn exit_return_statement(&mut self, stmt: &mut ReturnStatement<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_return_statement(stmt, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_return_statement(stmt, &mut ctx); } fn exit_variable_declaration( @@ -234,80 +154,46 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { decl: &mut VariableDeclaration<'a>, ctx: &mut TraverseCtx<'a>, ) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_variable_declaration(decl, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_variable_declaration(decl, &mut ctx); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.fold_constants_exit_expression(expr, &mut state, &mut ctx); - self.minimize_conditions_exit_expression(expr, &mut state, &mut ctx); - self.remove_dead_code_exit_expression(expr, &mut state, &mut ctx); - self.replace_known_methods_exit_expression(expr, &mut state, &mut ctx); - self.substitute_exit_expression(expr, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.fold_constants_exit_expression(expr, &mut ctx); + self.minimize_conditions_exit_expression(expr, &mut ctx); + self.remove_dead_code_exit_expression(expr, &mut ctx); + self.replace_known_methods_exit_expression(expr, &mut ctx); + self.substitute_exit_expression(expr, &mut ctx); } fn exit_unary_expression(&mut self, expr: &mut UnaryExpression<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - if expr.operator.is_not() && self.try_fold_expr_in_boolean_context(&mut expr.argument, &mut ctx) { - self.mark_current_function_as_changed(); + ctx.state.changed = true; } } fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_call_expression(expr, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_call_expression(expr, &mut ctx); } fn exit_new_expression(&mut self, expr: &mut NewExpression<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_new_expression(expr, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_new_expression(expr, &mut ctx); } fn exit_object_property(&mut self, prop: &mut ObjectProperty<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_object_property(prop, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_object_property(prop, &mut ctx); } fn exit_assignment_target_property( @@ -315,15 +201,9 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { node: &mut AssignmentTargetProperty<'a>, ctx: &mut TraverseCtx<'a>, ) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_assignment_target_property(node, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_assignment_target_property(node, &mut ctx); } fn exit_assignment_target_property_property( @@ -331,27 +211,15 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { prop: &mut AssignmentTargetPropertyProperty<'a>, ctx: &mut TraverseCtx<'a>, ) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_assignment_target_property_property(prop, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_assignment_target_property_property(prop, &mut ctx); } fn exit_binding_property(&mut self, prop: &mut BindingProperty<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_binding_property(prop, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_binding_property(prop, &mut ctx); } fn exit_method_definition( @@ -359,15 +227,9 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { prop: &mut MethodDefinition<'a>, ctx: &mut TraverseCtx<'a>, ) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_method_definition(prop, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_method_definition(prop, &mut ctx); } fn exit_property_definition( @@ -375,15 +237,9 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { prop: &mut PropertyDefinition<'a>, ctx: &mut TraverseCtx<'a>, ) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_property_definition(prop, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_property_definition(prop, &mut ctx); } fn exit_accessor_property( @@ -391,15 +247,9 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { prop: &mut AccessorProperty<'a>, ctx: &mut TraverseCtx<'a>, ) { - if !self.is_prev_function_changed() { - return; - } let mut ctx = Ctx::new(ctx); - let mut state = State::default(); - self.substitute_accessor_property(prop, &mut state, &mut ctx); - if state.changed { - self.mark_current_function_as_changed(); - } + + self.substitute_accessor_property(prop, &mut ctx); } } @@ -475,26 +325,27 @@ impl<'a> DeadCodeElimination { impl<'a> Traverse<'a, MinifierState<'a>> for DeadCodeElimination { fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - let mut state = State::default(); let mut ctx = Ctx::new(ctx); - self.inner.remove_dead_code_exit_statement(stmt, &mut state, &mut ctx); + self.inner.remove_dead_code_exit_statement(stmt, &mut ctx); } fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { - let mut state = State::default(); let mut ctx = Ctx::new(ctx); - self.inner.minimize_statements(stmts, &mut state, &mut ctx); - if state.changed { - self.inner.minimize_statements(stmts, &mut state, &mut ctx); + let changed = ctx.state.changed; + ctx.state.changed = false; + self.inner.minimize_statements(stmts, &mut ctx); + if ctx.state.changed { + self.inner.minimize_statements(stmts, &mut ctx); + } else { + ctx.state.changed = changed; } stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_))); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - let mut state = State::default(); let mut ctx = Ctx::new(ctx); - self.inner.fold_constants_exit_expression(expr, &mut state, &mut ctx); - self.inner.remove_dead_code_exit_expression(expr, &mut state, &mut ctx); + self.inner.fold_constants_exit_expression(expr, &mut ctx); + self.inner.remove_dead_code_exit_expression(expr, &mut ctx); } } diff --git a/crates/oxc_minifier/src/peephole/remove_dead_code.rs b/crates/oxc_minifier/src/peephole/remove_dead_code.rs index e87a807374c6a..de006e81d5d3b 100644 --- a/crates/oxc_minifier/src/peephole/remove_dead_code.rs +++ b/crates/oxc_minifier/src/peephole/remove_dead_code.rs @@ -8,7 +8,7 @@ use oxc_traverse::Ancestor; use crate::{ctx::Ctx, keep_var::KeepVar}; -use super::{LatePeepholeOptimizations, PeepholeOptimizations, State}; +use super::{LatePeepholeOptimizations, PeepholeOptimizations}; /// Remove Dead Code from the AST. /// @@ -17,16 +17,11 @@ use super::{LatePeepholeOptimizations, PeepholeOptimizations, State}; /// See `KeepVar` at the end of this file for `var` hoisting logic. /// impl<'a> PeepholeOptimizations { - pub fn remove_dead_code_exit_statement( - &self, - stmt: &mut Statement<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + pub fn remove_dead_code_exit_statement(&self, stmt: &mut Statement<'a>, ctx: &mut Ctx<'a, '_>) { if let Some(new_stmt) = match stmt { Statement::BlockStatement(s) => Self::try_optimize_block(s, ctx), - Statement::IfStatement(s) => Self::try_fold_if(s, state, ctx), - Statement::ForStatement(s) => self.try_fold_for(s, state, ctx), + Statement::IfStatement(s) => Self::try_fold_if(s, ctx), + Statement::ForStatement(s) => self.try_fold_for(s, ctx), Statement::TryStatement(s) => Self::try_fold_try(s, ctx), Statement::LabeledStatement(s) => Self::try_fold_labeled(s, ctx), Statement::FunctionDeclaration(f) => Self::remove_unused_function_declaration(f, ctx), @@ -34,25 +29,23 @@ impl<'a> PeepholeOptimizations { _ => None, } { *stmt = new_stmt; - state.changed = true; + ctx.state.changed = true; } - self.try_fold_expression_stmt(stmt, state, ctx); + self.try_fold_expression_stmt(stmt, ctx); } pub fn remove_dead_code_exit_expression( &self, expr: &mut Expression<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if let Some(folded_expr) = match expr { - Expression::ConditionalExpression(e) => { - self.try_fold_conditional_expression(e, state, ctx) - } - Expression::SequenceExpression(e) => self.try_fold_sequence_expression(e, state, ctx), + Expression::ConditionalExpression(e) => self.try_fold_conditional_expression(e, ctx), + Expression::SequenceExpression(e) => self.try_fold_sequence_expression(e, ctx), Expression::AssignmentExpression(_) => { - self.remove_unused_assignment_expression(expr, state, ctx); + self.remove_unused_assignment_expression(expr, ctx); None } Expression::CallExpression(call_expr) => self.remove_call_expression(call_expr, ctx), @@ -60,7 +53,7 @@ impl<'a> PeepholeOptimizations { _ => None, } { *expr = folded_expr; - state.changed = true; + ctx.state.changed = true; } } @@ -100,30 +93,26 @@ impl<'a> PeepholeOptimizations { } } - fn try_fold_if( - if_stmt: &mut IfStatement<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> Option> { + fn try_fold_if(if_stmt: &mut IfStatement<'a>, ctx: &mut Ctx<'a, '_>) -> Option> { // Descend and remove `else` blocks first. match &mut if_stmt.alternate { Some(Statement::IfStatement(alternate)) => { - if let Some(new_stmt) = Self::try_fold_if(alternate, state, ctx) { + if let Some(new_stmt) = Self::try_fold_if(alternate, ctx) { if matches!(new_stmt, Statement::EmptyStatement(_)) { if_stmt.alternate = None; } else { if_stmt.alternate = Some(new_stmt); } - state.changed = true; + ctx.state.changed = true; } } Some(Statement::BlockStatement(s)) if s.body.is_empty() => { if_stmt.alternate = None; - state.changed = true; + ctx.state.changed = true; } Some(Statement::EmptyStatement(_)) => { if_stmt.alternate = None; - state.changed = true; + ctx.state.changed = true; } _ => {} } @@ -188,21 +177,21 @@ impl<'a> PeepholeOptimizations { fn try_fold_for( &self, for_stmt: &mut ForStatement<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) -> Option> { if let Some(init) = &mut for_stmt.init { if let Some(init) = init.as_expression_mut() { - if self.remove_unused_expression(init, state, ctx) { + if self.remove_unused_expression(init, ctx) { for_stmt.init = None; - state.changed = true; + ctx.state.changed = true; } } } if let Some(update) = &mut for_stmt.update { - if self.remove_unused_expression(update, state, ctx) { + if self.remove_unused_expression(update, ctx) { for_stmt.update = None; - state.changed = true; + ctx.state.changed = true; } } @@ -244,7 +233,7 @@ impl<'a> PeepholeOptimizations { Some(true) => { // Remove the test expression. for_stmt.test = None; - state.changed = true; + ctx.state.changed = true; None } None => None, @@ -278,12 +267,7 @@ impl<'a> PeepholeOptimizations { var_decl.unwrap_or_else(|| ctx.ast.statement_empty(s.span)).into() } - fn try_fold_expression_stmt( - &self, - stmt: &mut Statement<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn try_fold_expression_stmt(&self, stmt: &mut Statement<'a>, ctx: &mut Ctx<'a, '_>) { let Statement::ExpressionStatement(expr_stmt) = stmt else { return }; // We need to check if it is in arrow function with `expression: true`. // This is the only scenario where we can't remove it even if `ExpressionStatement`. @@ -293,9 +277,9 @@ impl<'a> PeepholeOptimizations { } } - if self.remove_unused_expression(&mut expr_stmt.expression, state, ctx) { + if self.remove_unused_expression(&mut expr_stmt.expression, ctx) { *stmt = ctx.ast.statement_empty(expr_stmt.span); - state.changed = true; + ctx.state.changed = true; } } @@ -336,7 +320,7 @@ impl<'a> PeepholeOptimizations { fn try_fold_conditional_expression( &self, expr: &mut ConditionalExpression<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) -> Option> { expr.test.evaluate_value_to_boolean(ctx).map(|v| { @@ -345,7 +329,7 @@ impl<'a> PeepholeOptimizations { let exprs = ctx.ast.vec_from_array([ { let mut test = expr.test.take_in(ctx.ast); - self.remove_unused_expression(&mut test, state, ctx); + self.remove_unused_expression(&mut test, ctx); test }, if v { @@ -388,7 +372,7 @@ impl<'a> PeepholeOptimizations { fn try_fold_sequence_expression( &self, sequence_expr: &mut SequenceExpression<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) -> Option> { let should_keep_as_sequence_expr = sequence_expr @@ -407,28 +391,28 @@ impl<'a> PeepholeOptimizations { sequence_expr.expressions.retain_mut(|e| { i += 1; if should_keep_as_sequence_expr && i == old_len - 1 { - if self.remove_unused_expression(e, state, ctx) { + if self.remove_unused_expression(e, ctx) { *e = ctx.ast.expression_numeric_literal( e.span(), 0.0, None, NumberBase::Decimal, ); - state.changed = true; + ctx.state.changed = true; } return true; } if i == old_len { return true; } - !self.remove_unused_expression(e, state, ctx) + !self.remove_unused_expression(e, ctx) }); if sequence_expr.expressions.len() == 1 { return Some(sequence_expr.expressions.pop().unwrap()); } if sequence_expr.expressions.len() != old_len { - state.changed = true; + ctx.state.changed = true; } None } diff --git a/crates/oxc_minifier/src/peephole/remove_unused_expression.rs b/crates/oxc_minifier/src/peephole/remove_unused_expression.rs index aee6b3e681171..360426b2669e5 100644 --- a/crates/oxc_minifier/src/peephole/remove_unused_expression.rs +++ b/crates/oxc_minifier/src/peephole/remove_unused_expression.rs @@ -11,93 +11,71 @@ use oxc_syntax::es_target::ESTarget; use crate::ctx::Ctx; -use super::{PeepholeOptimizations, State}; +use super::PeepholeOptimizations; impl<'a> PeepholeOptimizations { /// `SimplifyUnusedExpr`: - pub fn remove_unused_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + pub fn remove_unused_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { match e { - Expression::ArrayExpression(_) => self.fold_array_expression(e, state, ctx), - Expression::UnaryExpression(_) => self.fold_unary_expression(e, state, ctx), - Expression::NewExpression(_) => self.fold_new_constructor(e, state, ctx), - Expression::LogicalExpression(_) => self.fold_logical_expression(e, state, ctx), - Expression::SequenceExpression(_) => self.fold_sequence_expression(e, state, ctx), - Expression::TemplateLiteral(_) => self.fold_template_literal(e, state, ctx), - Expression::ObjectExpression(_) => self.fold_object_expression(e, state, ctx), - Expression::ConditionalExpression(_) => self.fold_conditional_expression(e, state, ctx), - Expression::BinaryExpression(_) => self.fold_binary_expression(e, state, ctx), - Expression::CallExpression(_) => self.fold_call_expression(e, state, ctx), - Expression::AssignmentExpression(_) => { - self.remove_unused_assignment_expression(e, state, ctx) - } + Expression::ArrayExpression(_) => self.fold_array_expression(e, ctx), + Expression::UnaryExpression(_) => self.fold_unary_expression(e, ctx), + Expression::NewExpression(_) => self.fold_new_constructor(e, ctx), + Expression::LogicalExpression(_) => self.fold_logical_expression(e, ctx), + Expression::SequenceExpression(_) => self.fold_sequence_expression(e, ctx), + Expression::TemplateLiteral(_) => self.fold_template_literal(e, ctx), + Expression::ObjectExpression(_) => self.fold_object_expression(e, ctx), + Expression::ConditionalExpression(_) => self.fold_conditional_expression(e, ctx), + Expression::BinaryExpression(_) => self.fold_binary_expression(e, ctx), + Expression::CallExpression(_) => self.fold_call_expression(e, ctx), + Expression::AssignmentExpression(_) => self.remove_unused_assignment_expression(e, ctx), _ => !e.may_have_side_effects(ctx), } } - fn fold_unary_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_unary_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::UnaryExpression(unary_expr) = e else { return false }; match unary_expr.operator { UnaryOperator::Void | UnaryOperator::LogicalNot => { *e = unary_expr.argument.take_in(ctx.ast); - state.changed = true; - self.remove_unused_expression(e, state, ctx) + ctx.state.changed = true; + self.remove_unused_expression(e, ctx) } UnaryOperator::Typeof => { if unary_expr.argument.is_identifier_reference() { true } else { *e = unary_expr.argument.take_in(ctx.ast); - state.changed = true; - self.remove_unused_expression(e, state, ctx) + ctx.state.changed = true; + self.remove_unused_expression(e, ctx) } } _ => !e.may_have_side_effects(ctx), } } - fn fold_sequence_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_sequence_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::SequenceExpression(sequence_expr) = e else { return false }; let old_len = sequence_expr.expressions.len(); - sequence_expr.expressions.retain_mut(|e| !self.remove_unused_expression(e, state, ctx)); + sequence_expr.expressions.retain_mut(|e| !self.remove_unused_expression(e, ctx)); if sequence_expr.expressions.len() != old_len { - state.changed = true; + ctx.state.changed = true; } sequence_expr.expressions.is_empty() } - fn fold_logical_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_logical_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::LogicalExpression(logical_expr) = e else { return false }; if !logical_expr.operator.is_coalesce() && self.try_fold_expr_in_boolean_context(&mut logical_expr.left, ctx) { - state.changed = true; + ctx.state.changed = true; } - if self.remove_unused_expression(&mut logical_expr.right, state, ctx) { - self.remove_unused_expression(&mut logical_expr.left, state, ctx); + if self.remove_unused_expression(&mut logical_expr.right, ctx) { + self.remove_unused_expression(&mut logical_expr.left, ctx); *e = logical_expr.left.take_in(ctx.ast); - state.changed = true; + ctx.state.changed = true; return false; } @@ -132,7 +110,7 @@ impl<'a> PeepholeOptimizations { ctx, ) { *e = logical_right.take_in(ctx.ast); - state.changed = true; + ctx.state.changed = true; return false; } } @@ -163,7 +141,7 @@ impl<'a> PeepholeOptimizations { assignment_expr.span = *logical_span; assignment_expr.operator = AssignmentOperator::LogicalNullish; *e = logical_right.take_in(ctx.ast); - state.changed = true; + ctx.state.changed = true; return false; } } @@ -174,7 +152,7 @@ impl<'a> PeepholeOptimizations { LogicalOperator::Coalesce, logical_right.take_in(ctx.ast), ); - state.changed = true; + ctx.state.changed = true; return false; } } @@ -187,12 +165,7 @@ impl<'a> PeepholeOptimizations { } // `([1,2,3, foo()])` -> `foo()` - fn fold_array_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_array_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::ArrayExpression(array_expr) = e else { return false; }; @@ -206,11 +179,11 @@ impl<'a> PeepholeOptimizations { ArrayExpressionElement::Elision(_) => false, match_expression!(ArrayExpressionElement) => { let el_expr = el.to_expression_mut(); - !self.remove_unused_expression(el_expr, state, ctx) + !self.remove_unused_expression(el_expr, ctx) } }); if array_expr.elements.len() != old_len { - state.changed = true; + ctx.state.changed = true; } if array_expr.elements.is_empty() { @@ -242,37 +215,27 @@ impl<'a> PeepholeOptimizations { false } - fn fold_new_constructor( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_new_constructor(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::NewExpression(new_expr) = e else { return false }; if new_expr.pure && ctx.annotations() { let mut exprs = - self.fold_arguments_into_needed_expressions(&mut new_expr.arguments, state, ctx); + self.fold_arguments_into_needed_expressions(&mut new_expr.arguments, ctx); if exprs.is_empty() { return true; } else if exprs.len() == 1 { *e = exprs.pop().unwrap(); - state.changed = true; + ctx.state.changed = true; return false; } *e = ctx.ast.expression_sequence(new_expr.span, exprs); - state.changed = true; + ctx.state.changed = true; return false; } false } // "`${1}2${foo()}3`" -> "`${foo()}`" - fn fold_template_literal( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_template_literal(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::TemplateLiteral(temp_lit) = e else { return false }; if temp_lit.expressions.is_empty() { return true; @@ -289,7 +252,7 @@ impl<'a> PeepholeOptimizations { for mut e in temp_lit.expressions.drain(..) { if e.to_primitive(ctx).is_symbol() != Some(false) { pending_to_string_required_exprs.push(e); - } else if !self.remove_unused_expression(&mut e, state, ctx) { + } else if !self.remove_unused_expression(&mut e, ctx) { if !pending_to_string_required_exprs.is_empty() { // flush pending to string required expressions let expressions = @@ -342,22 +305,17 @@ impl<'a> PeepholeOptimizations { return true; } else if transformed_elements.len() == 1 { *e = transformed_elements.pop().unwrap(); - state.changed = true; + ctx.state.changed = true; return false; } *e = ctx.ast.expression_sequence(temp_lit.span, transformed_elements); - state.changed = true; + ctx.state.changed = true; false } // `({ 1: 1, [foo()]: bar() })` -> `foo(), bar()` - fn fold_object_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_object_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::ObjectExpression(object_expr) = e else { return false; }; @@ -391,13 +349,13 @@ impl<'a> PeepholeOptimizations { PropertyKey::StaticIdentifier(_) | PropertyKey::PrivateIdentifier(_) => {} match_expression!(PropertyKey) => { let mut prop_key = key.into_expression(); - if !self.remove_unused_expression(&mut prop_key, state, ctx) { + if !self.remove_unused_expression(&mut prop_key, ctx) { transformed_elements.push(prop_key); } } } - if !self.remove_unused_expression(&mut value, state, ctx) { + if !self.remove_unused_expression(&mut value, ctx) { transformed_elements.push(value); } } @@ -413,37 +371,31 @@ impl<'a> PeepholeOptimizations { return true; } else if transformed_elements.len() == 1 { *e = transformed_elements.pop().unwrap(); - state.changed = true; + ctx.state.changed = true; return false; } *e = ctx.ast.expression_sequence(object_expr.span, transformed_elements); - state.changed = true; + ctx.state.changed = true; false } - fn fold_conditional_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_conditional_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::ConditionalExpression(conditional_expr) = e else { return false; }; - let consequent = - self.remove_unused_expression(&mut conditional_expr.consequent, state, ctx); - let alternate = self.remove_unused_expression(&mut conditional_expr.alternate, state, ctx); + let consequent = self.remove_unused_expression(&mut conditional_expr.consequent, ctx); + let alternate = self.remove_unused_expression(&mut conditional_expr.alternate, ctx); // "foo() ? 1 : 2" => "foo()" if consequent && alternate { - let test = self.remove_unused_expression(&mut conditional_expr.test, state, ctx); + let test = self.remove_unused_expression(&mut conditional_expr.test, ctx); if test { return true; } *e = conditional_expr.test.take_in(ctx.ast); - state.changed = true; + ctx.state.changed = true; return false; } @@ -456,7 +408,7 @@ impl<'a> PeepholeOptimizations { conditional_expr.alternate.take_in(ctx.ast), ctx, ); - state.changed = true; + ctx.state.changed = true; return false; } @@ -469,19 +421,14 @@ impl<'a> PeepholeOptimizations { conditional_expr.consequent.take_in(ctx.ast), ctx, ); - state.changed = true; + ctx.state.changed = true; return false; } false } - fn fold_binary_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_binary_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::BinaryExpression(binary_expr) = e else { return false; }; @@ -495,18 +442,18 @@ impl<'a> PeepholeOptimizations { | BinaryOperator::LessEqualThan | BinaryOperator::GreaterThan | BinaryOperator::GreaterEqualThan => { - let left = self.remove_unused_expression(&mut binary_expr.left, state, ctx); - let right = self.remove_unused_expression(&mut binary_expr.right, state, ctx); + let left = self.remove_unused_expression(&mut binary_expr.left, ctx); + let right = self.remove_unused_expression(&mut binary_expr.right, ctx); match (left, right) { (true, true) => true, (true, false) => { *e = binary_expr.right.take_in(ctx.ast); - state.changed = true; + ctx.state.changed = true; false } (false, true) => { *e = binary_expr.left.take_in(ctx.ast); - state.changed = true; + ctx.state.changed = true; false } (false, false) => { @@ -517,13 +464,13 @@ impl<'a> PeepholeOptimizations { binary_expr.right.take_in(ctx.ast), ]), ); - state.changed = true; + ctx.state.changed = true; false } } } BinaryOperator::Addition => { - Self::fold_string_addition_chain(e, state, ctx); + Self::fold_string_addition_chain(e, ctx); matches!(e, Expression::StringLiteral(_)) } _ => !e.may_have_side_effects(ctx), @@ -531,11 +478,7 @@ impl<'a> PeepholeOptimizations { } /// returns whether the passed expression is a string - fn fold_string_addition_chain( - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_string_addition_chain(e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::BinaryExpression(binary_expr) = e else { return e.to_primitive(ctx).is_string() == Some(true); }; @@ -543,14 +486,14 @@ impl<'a> PeepholeOptimizations { return e.to_primitive(ctx).is_string() == Some(true); } - let left_is_string = Self::fold_string_addition_chain(&mut binary_expr.left, state, ctx); + let left_is_string = Self::fold_string_addition_chain(&mut binary_expr.left, ctx); if left_is_string { if !binary_expr.left.may_have_side_effects(ctx) && !binary_expr.left.is_specific_string_literal("") { binary_expr.left = ctx.ast.expression_string_literal(binary_expr.left.span(), "", None); - state.changed = true; + ctx.state.changed = true; } let right_as_primitive = binary_expr.right.to_primitive(ctx); @@ -558,7 +501,7 @@ impl<'a> PeepholeOptimizations { && !binary_expr.right.may_have_side_effects(ctx) { *e = binary_expr.left.take_in(ctx.ast); - state.changed = true; + ctx.state.changed = true; return true; } return true; @@ -571,33 +514,28 @@ impl<'a> PeepholeOptimizations { { binary_expr.right = ctx.ast.expression_string_literal(binary_expr.right.span(), "", None); - state.changed = true; + ctx.state.changed = true; } return true; } false } - fn fold_call_expression( - &self, - e: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) -> bool { + fn fold_call_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { let Expression::CallExpression(call_expr) = e else { return false }; if call_expr.pure && ctx.annotations() { let mut exprs = - self.fold_arguments_into_needed_expressions(&mut call_expr.arguments, state, ctx); + self.fold_arguments_into_needed_expressions(&mut call_expr.arguments, ctx); if exprs.is_empty() { return true; } else if exprs.len() == 1 { *e = exprs.pop().unwrap(); - state.changed = true; + ctx.state.changed = true; return false; } *e = ctx.ast.expression_sequence(call_expr.span, exprs); - state.changed = true; + ctx.state.changed = true; return false; } @@ -619,19 +557,19 @@ impl<'a> PeepholeOptimizations { // Replace "(() => foo())()" with "foo()" let expr = f.get_expression_mut().unwrap(); *e = expr.take_in(ctx.ast); - return self.remove_unused_expression(e, state, ctx); + return self.remove_unused_expression(e, ctx); } match &mut f.body.statements[0] { Statement::ExpressionStatement(expr_stmt) => { // Replace "(() => { foo() })" with "foo()" *e = expr_stmt.expression.take_in(ctx.ast); - return self.remove_unused_expression(e, state, ctx); + return self.remove_unused_expression(e, ctx); } Statement::ReturnStatement(ret_stmt) => { if let Some(argument) = &mut ret_stmt.argument { // Replace "(() => { return foo() })" with "foo()" *e = argument.take_in(ctx.ast); - return self.remove_unused_expression(e, state, ctx); + return self.remove_unused_expression(e, ctx); } // Replace "(() => { return })" with "" return true; @@ -648,7 +586,7 @@ impl<'a> PeepholeOptimizations { fn fold_arguments_into_needed_expressions( &self, args: &mut Vec<'a, Argument<'a>>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) -> Vec<'a, Expression<'a>> { ctx.ast.vec_from_iter(args.drain(..).filter_map(|arg| { @@ -659,7 +597,7 @@ impl<'a> PeepholeOptimizations { ), match_expression!(Argument) => arg.into_expression(), }; - (!self.remove_unused_expression(&mut expr, state, ctx)).then_some(expr) + (!self.remove_unused_expression(&mut expr, ctx)).then_some(expr) })) } } diff --git a/crates/oxc_minifier/src/peephole/remove_unused_variable_declaration.rs b/crates/oxc_minifier/src/peephole/remove_unused_variable_declaration.rs index 0494fe57be5ca..109886bbde214 100644 --- a/crates/oxc_minifier/src/peephole/remove_unused_variable_declaration.rs +++ b/crates/oxc_minifier/src/peephole/remove_unused_variable_declaration.rs @@ -2,7 +2,7 @@ use oxc_ast::ast::*; use crate::{CompressOptionsUnused, ctx::Ctx}; -use super::{PeepholeOptimizations, State}; +use super::PeepholeOptimizations; impl<'a> PeepholeOptimizations { pub fn should_remove_unused_declarator( @@ -89,7 +89,6 @@ impl<'a> PeepholeOptimizations { pub fn remove_unused_assignment_expression( &self, _e: &mut Expression<'a>, - _state: &mut State, _ctx: &mut Ctx<'a, '_>, ) -> bool { // let Expression::AssignmentExpression(assign_expr) = e else { return false }; diff --git a/crates/oxc_minifier/src/peephole/replace_known_methods.rs b/crates/oxc_minifier/src/peephole/replace_known_methods.rs index 22e3c72258ddc..757d54bad8bb8 100644 --- a/crates/oxc_minifier/src/peephole/replace_known_methods.rs +++ b/crates/oxc_minifier/src/peephole/replace_known_methods.rs @@ -16,7 +16,7 @@ use oxc_traverse::Ancestor; use crate::ctx::Ctx; -use super::{PeepholeOptimizations, State}; +use super::PeepholeOptimizations; type Arguments<'a> = oxc_allocator::Vec<'a, Argument<'a>>; @@ -26,20 +26,15 @@ impl<'a> PeepholeOptimizations { pub fn replace_known_methods_exit_expression( &self, node: &mut Expression<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { - self.try_fold_concat_chain(node, state, ctx); - self.try_fold_known_global_methods(node, state, ctx); - self.try_fold_known_property_access(node, state, ctx); + self.try_fold_concat_chain(node, ctx); + self.try_fold_known_global_methods(node, ctx); + self.try_fold_known_property_access(node, ctx); } - fn try_fold_known_global_methods( - &self, - node: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn try_fold_known_global_methods(&self, node: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::CallExpression(ce) = node else { return }; let CallExpression { span, callee, arguments, .. } = ce.as_mut(); let (name, object) = match &callee { @@ -86,7 +81,7 @@ impl<'a> PeepholeOptimizations { _ => None, }; if let Some(replacement) = replacement { - state.changed = true; + ctx.state.changed = true; *node = replacement; } } @@ -573,12 +568,7 @@ impl<'a> PeepholeOptimizations { /// `[].concat(a).concat(b)` -> `[].concat(a, b)` /// `"".concat(a).concat(b)` -> `"".concat(a, b)` - fn try_fold_concat_chain( - &self, - node: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn try_fold_concat_chain(&self, node: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let original_span = if let Expression::CallExpression(root_call_expr) = node { root_call_expr.span } else { @@ -654,7 +644,7 @@ impl<'a> PeepholeOptimizations { ), false, ); - state.changed = true; + ctx.state.changed = true; } /// `[].concat(1, 2)` -> `[1, 2]` @@ -818,12 +808,7 @@ impl<'a> PeepholeOptimizations { } } - fn try_fold_known_property_access( - &self, - node: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn try_fold_known_property_access(&self, node: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let (name, object, span) = match node { Expression::StaticMemberExpression(member) if !member.optional => { (member.property.name.as_str(), &member.object, member.span) @@ -840,7 +825,7 @@ impl<'a> PeepholeOptimizations { span, ctx, ) { - state.changed = true; + ctx.state.changed = true; *node = replacement; } } @@ -858,7 +843,7 @@ impl<'a> PeepholeOptimizations { span, ctx, ) { - state.changed = true; + ctx.state.changed = true; *node = replacement; } } @@ -881,7 +866,7 @@ impl<'a> PeepholeOptimizations { _ => None, }; if let Some(replacement) = replacement { - state.changed = true; + ctx.state.changed = true; *node = replacement; } } diff --git a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs index 8df94b7523c71..f16bfc8959a13 100644 --- a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs @@ -16,19 +16,14 @@ use oxc_traverse::Ancestor; use crate::ctx::Ctx; -use super::{LatePeepholeOptimizations, PeepholeOptimizations, State}; +use super::{LatePeepholeOptimizations, PeepholeOptimizations}; /// A peephole optimization that minimizes code by simplifying conditional /// expressions, replacing IFs with HOOKs, replacing object constructors /// with literals, and simplifying returns. /// impl<'a> PeepholeOptimizations { - pub fn substitute_object_property( - &self, - prop: &mut ObjectProperty<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + pub fn substitute_object_property(&self, prop: &mut ObjectProperty<'a>, ctx: &mut Ctx<'a, '_>) { // if !prop.method { if let PropertyKey::StringLiteral(str) = &prop.key { @@ -39,31 +34,31 @@ impl<'a> PeepholeOptimizations { } } - self.try_compress_property_key(&mut prop.key, &mut prop.computed, state, ctx); + self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } pub fn substitute_assignment_target_property_property( &self, prop: &mut AssignmentTargetPropertyProperty<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { - self.try_compress_property_key(&mut prop.name, &mut prop.computed, state, ctx); + self.try_compress_property_key(&mut prop.name, &mut prop.computed, ctx); } pub fn substitute_assignment_target_property( &self, prop: &mut AssignmentTargetProperty<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { - self.try_compress_assignment_target_property(prop, state, ctx); + self.try_compress_assignment_target_property(prop, ctx); } pub fn try_compress_assignment_target_property( &self, prop: &mut AssignmentTargetProperty<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { // `a: a` -> `a` @@ -83,7 +78,7 @@ impl<'a> PeepholeOptimizations { ), None, ); - state.changed = true; + ctx.state.changed = true; } } } @@ -91,16 +86,16 @@ impl<'a> PeepholeOptimizations { pub fn substitute_binding_property( &self, prop: &mut BindingProperty<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { - self.try_compress_property_key(&mut prop.key, &mut prop.computed, state, ctx); + self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } pub fn substitute_method_definition( &self, prop: &mut MethodDefinition<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { let property_key_parent: ClassPropertyKeyParent = prop.into(); @@ -109,13 +104,13 @@ impl<'a> PeepholeOptimizations { return; } } - self.try_compress_property_key(&mut prop.key, &mut prop.computed, state, ctx); + self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } pub fn substitute_property_definition( &self, prop: &mut PropertyDefinition<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { let property_key_parent: ClassPropertyKeyParent = prop.into(); @@ -124,13 +119,13 @@ impl<'a> PeepholeOptimizations { return; } } - self.try_compress_property_key(&mut prop.key, &mut prop.computed, state, ctx); + self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } pub fn substitute_accessor_property( &self, prop: &mut AccessorProperty<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { let property_key_parent: ClassPropertyKeyParent = prop.into(); @@ -139,64 +134,49 @@ impl<'a> PeepholeOptimizations { return; } } - self.try_compress_property_key(&mut prop.key, &mut prop.computed, state, ctx); + self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } pub fn substitute_return_statement( &self, stmt: &mut ReturnStatement<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { - self.compress_return_statement(stmt, state, ctx); + self.compress_return_statement(stmt, ctx); } pub fn substitute_variable_declaration( &self, decl: &mut VariableDeclaration<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { for declarator in &mut decl.declarations { - self.compress_variable_declarator(declarator, state, ctx); + self.compress_variable_declarator(declarator, ctx); } } - pub fn substitute_call_expression( - &self, - expr: &mut CallExpression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { - self.try_flatten_arguments(&mut expr.arguments, state, ctx); + pub fn substitute_call_expression(&self, expr: &mut CallExpression<'a>, ctx: &mut Ctx<'a, '_>) { + self.try_flatten_arguments(&mut expr.arguments, ctx); } - pub fn substitute_new_expression( - &self, - expr: &mut NewExpression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { - self.try_flatten_arguments(&mut expr.arguments, state, ctx); + pub fn substitute_new_expression(&self, expr: &mut NewExpression<'a>, ctx: &mut Ctx<'a, '_>) { + self.try_flatten_arguments(&mut expr.arguments, ctx); } - pub fn substitute_exit_expression( - &self, - expr: &mut Expression<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + pub fn substitute_exit_expression(&self, expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { // Change syntax match expr { Expression::ArrowFunctionExpression(e) => { - self.try_compress_arrow_expression(e, state, ctx); + self.try_compress_arrow_expression(e, ctx); } Expression::ChainExpression(e) => { - self.try_compress_chain_call_expression(e, state, ctx); + self.try_compress_chain_call_expression(e, ctx); } Expression::BinaryExpression(e) => Self::swap_binary_expressions(e), - Expression::FunctionExpression(e) => self.try_remove_name_from_functions(e, state, ctx), - Expression::ClassExpression(e) => self.try_remove_name_from_classes(e, state, ctx), + Expression::FunctionExpression(e) => self.try_remove_name_from_functions(e, ctx), + Expression::ClassExpression(e) => self.try_remove_name_from_classes(e, ctx), _ => {} } @@ -221,7 +201,7 @@ impl<'a> PeepholeOptimizations { _ => None, } { *expr = folded_expr; - state.changed = true; + ctx.state.changed = true; } } @@ -238,7 +218,7 @@ impl<'a> PeepholeOptimizations { fn try_compress_arrow_expression( &self, arrow_expr: &mut ArrowFunctionExpression<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if !arrow_expr.expression @@ -252,7 +232,7 @@ impl<'a> PeepholeOptimizations { if let Some(arg) = return_stmt_arg { *body = ctx.ast.statement_expression(arg.span(), arg); arrow_expr.expression = true; - state.changed = true; + ctx.state.changed = true; } } } @@ -597,12 +577,7 @@ impl<'a> PeepholeOptimizations { /// /// `return undefined` -> `return` /// `return void 0` -> `return` - fn compress_return_statement( - &self, - stmt: &mut ReturnStatement<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn compress_return_statement(&self, stmt: &mut ReturnStatement<'a>, ctx: &mut Ctx<'a, '_>) { let Some(argument) = &stmt.argument else { return }; if !match argument { Expression::Identifier(ident) => ctx.is_identifier_undefined(ident), @@ -622,13 +597,13 @@ impl<'a> PeepholeOptimizations { } } stmt.argument = None; - state.changed = true; + ctx.state.changed = true; } fn compress_variable_declarator( &self, decl: &mut VariableDeclarator<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { // Destructuring Pattern has error throwing side effect. @@ -645,7 +620,7 @@ impl<'a> PeepholeOptimizations { && decl.init.as_ref().is_some_and(|init| ctx.is_expression_undefined(init)) { decl.init = None; - state.changed = true; + ctx.state.changed = true; } } @@ -884,7 +859,7 @@ impl<'a> PeepholeOptimizations { fn try_compress_chain_call_expression( &self, chain_expr: &mut ChainExpression<'a>, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if let ChainElement::CallExpression(call_expr) = &mut chain_expr.expression { @@ -896,7 +871,7 @@ impl<'a> PeepholeOptimizations { .is_some_and(|mem_expr| mem_expr.is_specific_member_access("window", "Object")) { call_expr.callee = ctx.ast.expression_identifier(call_expr.callee.span(), "Object"); - state.changed = true; + ctx.state.changed = true; } } } @@ -915,7 +890,7 @@ impl<'a> PeepholeOptimizations { &self, key: &mut PropertyKey<'a>, computed: &mut bool, - state: &mut State, + ctx: &mut Ctx<'a, '_>, ) { if let PropertyKey::NumericLiteral(_) = key { @@ -929,7 +904,7 @@ impl<'a> PeepholeOptimizations { if is_identifier_name(value) { *computed = false; *key = PropertyKey::StaticIdentifier(ctx.ast.alloc_identifier_name(s.span, s.value)); - state.changed = true; + ctx.state.changed = true; return; } if let Some(value) = Ctx::string_to_equivalent_number_value(value) { @@ -941,7 +916,7 @@ impl<'a> PeepholeOptimizations { None, NumberBase::Decimal, )); - state.changed = true; + ctx.state.changed = true; return; } } @@ -952,12 +927,7 @@ impl<'a> PeepholeOptimizations { // `foo(...[1,2,3])` -> `foo(1,2,3)` // `new Foo(...[1,2,3])` -> `new Foo(1,2,3)` - fn try_flatten_arguments( - &self, - args: &mut Vec<'a, Argument<'a>>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn try_flatten_arguments(&self, args: &mut Vec<'a, Argument<'a>>, ctx: &mut Ctx<'a, '_>) { let (new_size, should_fold) = args.iter().fold((0, false), |(mut new_size, mut should_fold), arg| { new_size += if let Argument::SpreadElement(spread_el) = arg { @@ -1009,7 +979,7 @@ impl<'a> PeepholeOptimizations { new_args.push(arg); } } - state.changed = true; + ctx.state.changed = true; } /// Remove name from function expressions if it is not used. @@ -1017,19 +987,14 @@ impl<'a> PeepholeOptimizations { /// e.g. `var a = function f() {}` -> `var a = function () {}` /// /// This compression is not safe if the code relies on `Function::name`. - fn try_remove_name_from_functions( - &self, - func: &mut Function<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn try_remove_name_from_functions(&self, func: &mut Function<'a>, ctx: &mut Ctx<'a, '_>) { if ctx.options().keep_names.function { return; } if func.id.as_ref().is_some_and(|id| ctx.scoping().symbol_is_unused(id.symbol_id())) { func.id = None; - state.changed = true; + ctx.state.changed = true; } } @@ -1038,19 +1003,14 @@ impl<'a> PeepholeOptimizations { /// e.g. `var a = class C {}` -> `var a = class {}` /// /// This compression is not safe if the code relies on `Class::name`. - fn try_remove_name_from_classes( - &self, - class: &mut Class<'a>, - state: &mut State, - ctx: &mut Ctx<'a, '_>, - ) { + fn try_remove_name_from_classes(&self, class: &mut Class<'a>, ctx: &mut Ctx<'a, '_>) { if ctx.options().keep_names.class { return; } if class.id.as_ref().is_some_and(|id| ctx.scoping().symbol_is_unused(id.symbol_id())) { class.id = None; - state.changed = true; + ctx.state.changed = true; } } } diff --git a/crates/oxc_minifier/src/state.rs b/crates/oxc_minifier/src/state.rs index 2e27ecc3a1c42..d89f94e42f46b 100644 --- a/crates/oxc_minifier/src/state.rs +++ b/crates/oxc_minifier/src/state.rs @@ -19,6 +19,8 @@ pub struct MinifierState<'a> { /// Function declarations that are empty pub empty_functions: FxHashSet, + + pub changed: bool, } impl MinifierState<'_> { @@ -28,6 +30,7 @@ impl MinifierState<'_> { options, constant_values: FxHashMap::default(), empty_functions: FxHashSet::default(), + changed: false, } } }