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 2a76966dade95..99f964a9e3635 100644 --- a/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs +++ b/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs @@ -1,41 +1,27 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; -use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx}; - -use crate::CompressorPass; - -/// Collapse variable declarations. -/// -/// Join Vars: -/// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2` -/// -/// -/// Collapse into for statements: -/// `var a = 0; for(;a<0;a++) {}` => `for(var a = 0;a<0;a++) {}` -/// -pub struct CollapseVariableDeclarations { - pub(crate) changed: bool, -} - -impl<'a> CompressorPass<'a> for CollapseVariableDeclarations { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl<'a> Traverse<'a> for CollapseVariableDeclarations { - fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { +use oxc_traverse::TraverseCtx; + +use super::PeepholeOptimizations; + +impl<'a> PeepholeOptimizations { + /// Collapse variable declarations. + /// + /// Join Vars: + /// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2` + /// + /// + /// Collapse into for statements: + /// `var a = 0; for(;a<0;a++) {}` => `for(var a = 0;a<0;a++) {}` + /// + pub fn collapse_variable_declarations( + &mut self, + stmts: &mut Vec<'a, Statement<'a>>, + ctx: &mut TraverseCtx<'a>, + ) { self.join_vars(stmts, ctx); self.maybe_collapse_into_for_statements(stmts, ctx); } -} - -// Join Vars -impl<'a> CollapseVariableDeclarations { - pub fn new() -> Self { - Self { changed: false } - } fn is_require_call(var_decl: &VariableDeclaration) -> bool { var_decl @@ -112,7 +98,7 @@ impl<'a> CollapseVariableDeclarations { } // Collapse into for statements -impl<'a> CollapseVariableDeclarations { +impl<'a> PeepholeOptimizations { fn maybe_collapse_into_for_statements( &mut self, stmts: &mut Vec<'a, Statement<'a>>, @@ -240,18 +226,6 @@ mod test { mod join_vars { use super::{test, test_same}; - #[test] - fn cjs() { - // Do not join `require` calls for cjs-module-lexer. - test_same( - " Object.defineProperty(exports, '__esModule', { value: true }); - var compilerDom = require('@vue/compiler-dom'); - var runtimeDom = require('@vue/runtime-dom'); - var shared = require('@vue/shared'); - ", - ); - } - #[test] fn test_collapsing() { // Basic collapsing 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 9564e6d964fc3..78fe23cf477a7 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 @@ -1,27 +1,16 @@ use oxc_ast::ast::*; use oxc_syntax::identifier::is_identifier_name; -use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx}; +use oxc_traverse::TraverseCtx; -use crate::{ctx::Ctx, CompressorPass}; +use super::PeepholeOptimizations; +use crate::ctx::Ctx; -/// Converts property accesses from quoted string or bracket access syntax to dot or unquoted string -/// syntax, where possible. Dot syntax is more compact. -/// -/// -pub struct ConvertToDottedProperties { - pub(crate) changed: bool, - in_fixed_loop: bool, -} - -impl<'a> CompressorPass<'a> for ConvertToDottedProperties { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = true; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl<'a> Traverse<'a> for ConvertToDottedProperties { - fn exit_member_expression( +impl<'a> PeepholeOptimizations { + /// Converts property accesses from quoted string or bracket access syntax to dot or unquoted string + /// syntax, where possible. Dot syntax is more compact. + /// + /// + pub fn convert_to_dotted_properties( &mut self, expr: &mut MemberExpression<'a>, ctx: &mut TraverseCtx<'a>, @@ -30,12 +19,6 @@ impl<'a> Traverse<'a> for ConvertToDottedProperties { self.try_compress_computed_member_expression(expr, Ctx(ctx)); } } -} - -impl<'a> ConvertToDottedProperties { - pub fn new(in_fixed_loop: bool) -> Self { - Self { changed: false, in_fixed_loop } - } /// `foo['bar']` -> `foo.bar` /// `foo?.['bar']` -> `foo?.bar` diff --git a/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs b/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs index 492d023a7f96d..4363d036ed258 100644 --- a/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs +++ b/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs @@ -1,31 +1,19 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; -use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx}; - -use crate::CompressorPass; - -/// Tries to chain assignments together. -/// -/// -pub struct ExploitAssigns { - pub(crate) changed: bool, -} - -impl<'a> CompressorPass<'a> for ExploitAssigns { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl<'a> Traverse<'a> for ExploitAssigns { - fn exit_statements(&mut self, _stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) { - } -} - -impl ExploitAssigns { - pub fn new() -> Self { - Self { changed: false } +use oxc_traverse::TraverseCtx; + +use super::PeepholeOptimizations; + +impl<'a> PeepholeOptimizations { + /// Tries to chain assignments together. + /// + /// + #[expect(clippy::unused_self)] + pub fn exploit_assigns( + &mut self, + _stmts: &mut Vec<'a, Statement<'a>>, + _ctx: &mut TraverseCtx<'a>, + ) { } } 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 244a51d7739ae..af750481205fb 100644 --- a/crates/oxc_minifier/src/ast_passes/minimize_exit_points.rs +++ b/crates/oxc_minifier/src/ast_passes/minimize_exit_points.rs @@ -1,34 +1,21 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; -use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx}; - -use crate::CompressorPass; - -/// Transform the structure of the AST so that the number of explicit exits -/// are minimized and instead flows to implicit exits conditions. -/// -/// -pub struct MinimizeExitPoints { - pub(crate) changed: bool, -} - -impl<'a> CompressorPass<'a> for MinimizeExitPoints { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl Traverse<'_> for MinimizeExitPoints { - fn exit_function_body(&mut self, body: &mut FunctionBody<'_>, _ctx: &mut TraverseCtx<'_>) { +use oxc_traverse::TraverseCtx; + +use super::PeepholeOptimizations; + +impl<'a> PeepholeOptimizations { + /// Transform the structure of the AST so that the number of explicit exits + /// are minimized and instead flows to implicit exits conditions. + /// + /// + pub fn minimize_exit_points( + &mut self, + body: &mut FunctionBody<'_>, + _ctx: &mut TraverseCtx<'_>, + ) { self.remove_last_return(&mut body.statements); } -} - -impl<'a> MinimizeExitPoints { - pub fn new() -> Self { - Self { changed: false } - } // `function foo() { return }` -> `function foo() {}` fn remove_last_return(&mut self, stmts: &mut Vec<'a, Statement<'a>>) { diff --git a/crates/oxc_minifier/src/ast_passes/mod.rs b/crates/oxc_minifier/src/ast_passes/mod.rs index f93a038b30e6c..4859525fd9d4b 100644 --- a/crates/oxc_minifier/src/ast_passes/mod.rs +++ b/crates/oxc_minifier/src/ast_passes/mod.rs @@ -16,84 +16,25 @@ mod peephole_substitute_alternate_syntax; mod remove_unused_code; mod statement_fusion; -pub use collapse_variable_declarations::CollapseVariableDeclarations; -pub use convert_to_dotted_properties::ConvertToDottedProperties; -pub use exploit_assigns::ExploitAssigns; -pub use minimize_exit_points::MinimizeExitPoints; pub use normalize::{Normalize, NormalizeOptions}; -pub use peephole_fold_constants::PeepholeFoldConstants; -pub use peephole_minimize_conditions::PeepholeMinimizeConditions; -pub use peephole_remove_dead_code::PeepholeRemoveDeadCode; -pub use peephole_replace_known_methods::PeepholeReplaceKnownMethods; -pub use peephole_substitute_alternate_syntax::PeepholeSubstituteAlternateSyntax; #[expect(unused)] pub use remove_unused_code::RemoveUnusedCode; -pub use statement_fusion::StatementFusion; - -use crate::CompressOptions; pub trait CompressorPass<'a>: Traverse<'a> { fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>); } pub struct PeepholeOptimizations { - x0_statement_fusion: StatementFusion, - x1_minimize_exit_points: MinimizeExitPoints, - x2_exploit_assigns: ExploitAssigns, - x3_collapse_variable_declarations: CollapseVariableDeclarations, - x4_peephole_fold_constants: PeepholeFoldConstants, - x6_peephole_remove_dead_code: PeepholeRemoveDeadCode, - x5_peephole_minimize_conditions: PeepholeMinimizeConditions, - x7_convert_to_dotted_properties: ConvertToDottedProperties, - x8_peephole_replace_known_methods: PeepholeReplaceKnownMethods, - x9_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax, -} - -impl PeepholeOptimizations { + target: ESTarget, + changed: bool, /// `in_fixed_loop`: Do not compress syntaxes that are hard to analyze inside the fixed loop. /// Opposite of `late` in Closure Compiler. - pub fn new(target: ESTarget, in_fixed_loop: bool, options: CompressOptions) -> Self { - Self { - x0_statement_fusion: StatementFusion::new(), - x1_minimize_exit_points: MinimizeExitPoints::new(), - x2_exploit_assigns: ExploitAssigns::new(), - x3_collapse_variable_declarations: CollapseVariableDeclarations::new(), - x4_peephole_fold_constants: PeepholeFoldConstants::new(), - x5_peephole_minimize_conditions: PeepholeMinimizeConditions::new(target), - x6_peephole_remove_dead_code: PeepholeRemoveDeadCode::new(in_fixed_loop), - x7_convert_to_dotted_properties: ConvertToDottedProperties::new(in_fixed_loop), - x8_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(), - x9_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new( - options.target, - in_fixed_loop, - ), - } - } - - fn reset_changed(&mut self) { - self.x0_statement_fusion.changed = false; - self.x1_minimize_exit_points.changed = false; - self.x2_exploit_assigns.changed = false; - self.x3_collapse_variable_declarations.changed = false; - self.x4_peephole_fold_constants.changed = false; - self.x5_peephole_minimize_conditions.changed = false; - self.x6_peephole_remove_dead_code.changed = false; - self.x7_convert_to_dotted_properties.changed = false; - self.x8_peephole_replace_known_methods.changed = false; - self.x9_peephole_substitute_alternate_syntax.changed = false; - } + in_fixed_loop: bool, +} - fn changed(&self) -> bool { - self.x0_statement_fusion.changed - || self.x1_minimize_exit_points.changed - || self.x2_exploit_assigns.changed - || self.x3_collapse_variable_declarations.changed - || self.x4_peephole_fold_constants.changed - || self.x5_peephole_minimize_conditions.changed - || self.x6_peephole_remove_dead_code.changed - || self.x7_convert_to_dotted_properties.changed - || self.x8_peephole_replace_known_methods.changed - || self.x9_peephole_substitute_alternate_syntax.changed +impl PeepholeOptimizations { + pub fn new(target: ESTarget, in_fixed_loop: bool) -> Self { + Self { target, changed: false, in_fixed_loop } } pub fn run_in_loop<'a>( @@ -103,9 +44,9 @@ impl PeepholeOptimizations { ) { let mut i = 0; loop { - self.reset_changed(); + self.changed = false; self.build(program, ctx); - if !self.changed() { + if !self.changed { break; } if i > 10 { @@ -124,33 +65,29 @@ impl<'a> CompressorPass<'a> for PeepholeOptimizations { } impl<'a> Traverse<'a> for PeepholeOptimizations { - fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { - self.x6_peephole_remove_dead_code.exit_program(program, ctx); - } - fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { - self.x0_statement_fusion.exit_statements(stmts, ctx); - self.x2_exploit_assigns.exit_statements(stmts, ctx); - self.x3_collapse_variable_declarations.exit_statements(stmts, ctx); - self.x5_peephole_minimize_conditions.exit_statements(stmts, ctx); - self.x6_peephole_remove_dead_code.exit_statements(stmts, ctx); + self.statement_fusion_exit_statements(stmts, ctx); + self.exploit_assigns(stmts, ctx); + self.collapse_variable_declarations(stmts, ctx); + self.minimize_conditions_exit_statements(stmts, ctx); + self.remove_dead_code_exit_statements(stmts, ctx); } fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - self.x5_peephole_minimize_conditions.exit_statement(stmt, ctx); - self.x6_peephole_remove_dead_code.exit_statement(stmt, ctx); + self.minimize_conditions_exit_statement(stmt, ctx); + self.remove_dead_code_exit_statement(stmt, ctx); } fn exit_return_statement(&mut self, stmt: &mut ReturnStatement<'a>, ctx: &mut TraverseCtx<'a>) { - self.x9_peephole_substitute_alternate_syntax.exit_return_statement(stmt, ctx); + self.substitute_return_statement(stmt, ctx); } fn exit_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) { - self.x1_minimize_exit_points.exit_function_body(body, ctx); + self.minimize_exit_points(body, ctx); } fn exit_class_body(&mut self, body: &mut ClassBody<'a>, ctx: &mut TraverseCtx<'a>) { - self.x6_peephole_remove_dead_code.exit_class_body(body, ctx); + self.remove_dead_code_exit_class_body(body, ctx); } fn exit_variable_declaration( @@ -158,23 +95,19 @@ impl<'a> Traverse<'a> for PeepholeOptimizations { decl: &mut VariableDeclaration<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x9_peephole_substitute_alternate_syntax.exit_variable_declaration(decl, ctx); + self.substitute_variable_declaration(decl, ctx); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x4_peephole_fold_constants.exit_expression(expr, ctx); - self.x5_peephole_minimize_conditions.exit_expression(expr, ctx); - self.x6_peephole_remove_dead_code.exit_expression(expr, ctx); - self.x8_peephole_replace_known_methods.exit_expression(expr, ctx); - self.x9_peephole_substitute_alternate_syntax.exit_expression(expr, ctx); - } - - fn enter_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x9_peephole_substitute_alternate_syntax.enter_call_expression(expr, ctx); + self.fold_constants_exit_expression(expr, ctx); + self.minimize_conditions_exit_expression(expr, ctx); + self.remove_dead_code_exit_expression(expr, ctx); + self.replace_known_methods_exit_expression(expr, ctx); + self.substitute_exit_expression(expr, ctx); } fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x9_peephole_substitute_alternate_syntax.exit_call_expression(expr, ctx); + self.substitute_call_expression(expr, ctx); } fn exit_member_expression( @@ -182,11 +115,11 @@ impl<'a> Traverse<'a> for PeepholeOptimizations { expr: &mut MemberExpression<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x7_convert_to_dotted_properties.exit_member_expression(expr, ctx); + self.convert_to_dotted_properties(expr, ctx); } fn exit_object_property(&mut self, prop: &mut ObjectProperty<'a>, ctx: &mut TraverseCtx<'a>) { - self.x9_peephole_substitute_alternate_syntax.exit_object_property(prop, ctx); + self.substitute_object_property(prop, ctx); } fn exit_assignment_target_property_property( @@ -194,12 +127,11 @@ impl<'a> Traverse<'a> for PeepholeOptimizations { prop: &mut AssignmentTargetPropertyProperty<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x9_peephole_substitute_alternate_syntax - .exit_assignment_target_property_property(prop, ctx); + self.substitute_assignment_target_property_property(prop, ctx); } fn exit_binding_property(&mut self, prop: &mut BindingProperty<'a>, ctx: &mut TraverseCtx<'a>) { - self.x9_peephole_substitute_alternate_syntax.exit_binding_property(prop, ctx); + self.substitute_binding_property(prop, ctx); } fn exit_method_definition( @@ -207,7 +139,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations { prop: &mut MethodDefinition<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x9_peephole_substitute_alternate_syntax.exit_method_definition(prop, ctx); + self.substitute_method_definition(prop, ctx); } fn exit_property_definition( @@ -215,7 +147,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations { prop: &mut PropertyDefinition<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x9_peephole_substitute_alternate_syntax.exit_property_definition(prop, ctx); + self.substitute_property_definition(prop, ctx); } fn exit_accessor_property( @@ -223,25 +155,21 @@ impl<'a> Traverse<'a> for PeepholeOptimizations { prop: &mut AccessorProperty<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x9_peephole_substitute_alternate_syntax.exit_accessor_property(prop, ctx); + self.substitute_accessor_property(prop, ctx); } fn exit_catch_clause(&mut self, catch: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) { - self.x9_peephole_substitute_alternate_syntax.exit_catch_clause(catch, ctx); + self.substitute_catch_clause(catch, ctx); } } pub struct DeadCodeElimination { - x1_peephole_fold_constants: PeepholeFoldConstants, - x2_peephole_remove_dead_code: PeepholeRemoveDeadCode, + inner: PeepholeOptimizations, } impl DeadCodeElimination { pub fn new() -> Self { - Self { - x1_peephole_fold_constants: PeepholeFoldConstants::new(), - x2_peephole_remove_dead_code: PeepholeRemoveDeadCode::new(false), - } + Self { inner: PeepholeOptimizations::new(ESTarget::ESNext, false) } } } @@ -253,19 +181,15 @@ impl<'a> CompressorPass<'a> for DeadCodeElimination { impl<'a> Traverse<'a> for DeadCodeElimination { fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - self.x2_peephole_remove_dead_code.exit_statement(stmt, ctx); - } - - fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { - self.x2_peephole_remove_dead_code.exit_program(program, ctx); + self.inner.remove_dead_code_exit_statement(stmt, ctx); } fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { - self.x2_peephole_remove_dead_code.exit_statements(stmts, ctx); + self.inner.remove_dead_code_exit_statements(stmts, ctx); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x1_peephole_fold_constants.exit_expression(expr, ctx); - self.x2_peephole_remove_dead_code.exit_expression(expr, ctx); + self.inner.fold_constants_exit_expression(expr, ctx); + self.inner.remove_dead_code_exit_expression(expr, ctx); } } 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 e9218d0f6c80b..66ef561f47d40 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -8,26 +8,21 @@ use oxc_syntax::{ number::NumberBase, operator::{BinaryOperator, LogicalOperator}, }; -use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx}; +use oxc_traverse::{Ancestor, TraverseCtx}; -use crate::{ctx::Ctx, CompressorPass}; +use crate::ctx::Ctx; -/// Constant Folding -/// -/// -pub struct PeepholeFoldConstants { - pub(crate) changed: bool, -} +use super::PeepholeOptimizations; -impl<'a> CompressorPass<'a> for PeepholeFoldConstants { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl<'a> Traverse<'a> for PeepholeFoldConstants { - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { +impl<'a, 'b> PeepholeOptimizations { + /// Constant Folding + /// + /// + pub fn fold_constants_exit_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { let ctx = Ctx(ctx); if let Some(folded_expr) = match expr { Expression::BinaryExpression(e) => Self::try_fold_binary_expr(e, ctx) @@ -44,12 +39,6 @@ impl<'a> Traverse<'a> for PeepholeFoldConstants { self.changed = true; }; } -} - -impl<'a, 'b> PeepholeFoldConstants { - pub fn new() -> Self { - Self { changed: false } - } #[expect(clippy::float_cmp)] fn try_fold_unary_expr(e: &UnaryExpression<'a>, ctx: Ctx<'a, 'b>) -> Option> { 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 ae3a651422c45..ca951bdd741bc 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs @@ -2,10 +2,11 @@ use oxc_allocator::Vec; use oxc_ast::{ast::*, NONE}; use oxc_ecmascript::constant_evaluation::{ConstantEvaluation, ValueType}; use oxc_span::{cmp::ContentEq, GetSpan}; -use oxc_syntax::es_target::ESTarget; -use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx}; +use oxc_traverse::{Ancestor, TraverseCtx}; -use crate::{ctx::Ctx, CompressorPass}; +use crate::ctx::Ctx; + +use super::PeepholeOptimizations; /// Minimize Conditions /// @@ -14,21 +15,8 @@ use crate::{ctx::Ctx, CompressorPass}; /// with `? :` and short-circuit binary operators. /// /// -pub struct PeepholeMinimizeConditions { - #[allow(unused)] - target: ESTarget, - pub(crate) changed: bool, -} - -impl<'a> CompressorPass<'a> for PeepholeMinimizeConditions { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl<'a> Traverse<'a> for PeepholeMinimizeConditions { - fn exit_statements( +impl<'a> PeepholeOptimizations { + pub fn minimize_conditions_exit_statements( &mut self, stmts: &mut oxc_allocator::Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>, @@ -45,7 +33,11 @@ impl<'a> Traverse<'a> for PeepholeMinimizeConditions { self.changed = self.changed || changed; } - fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + pub fn minimize_conditions_exit_statement( + &mut self, + stmt: &mut Statement<'a>, + ctx: &mut TraverseCtx<'a>, + ) { let expr = match stmt { Statement::IfStatement(s) => Some(&mut s.test), Statement::WhileStatement(s) => Some(&mut s.test), @@ -76,7 +68,11 @@ impl<'a> Traverse<'a> for PeepholeMinimizeConditions { }; } - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + pub fn minimize_conditions_exit_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { loop { let mut changed = false; if let Expression::ConditionalExpression(logical_expr) = expr { @@ -106,12 +102,6 @@ impl<'a> Traverse<'a> for PeepholeMinimizeConditions { self.changed = true; }; } -} - -impl<'a> PeepholeMinimizeConditions { - pub fn new(target: ESTarget) -> Self { - Self { target, changed: false } - } fn try_minimize_not( expr: &mut UnaryExpression<'a>, 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 6c954c52a32f3..1c8b9f30a772b 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 @@ -5,9 +5,11 @@ use oxc_ecmascript::{ side_effects::MayHaveSideEffects, }; use oxc_span::GetSpan; -use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx}; +use oxc_traverse::{Ancestor, TraverseCtx}; -use crate::{ctx::Ctx, keep_var::KeepVar, CompressorPass}; +use crate::{ctx::Ctx, keep_var::KeepVar}; + +use super::PeepholeOptimizations; /// Remove Dead Code from the AST. /// @@ -15,28 +17,23 @@ use crate::{ctx::Ctx, keep_var::KeepVar, CompressorPass}; /// /// See `KeepVar` at the end of this file for `var` hoisting logic. /// -pub struct PeepholeRemoveDeadCode { - pub(crate) changed: bool, - - in_fixed_loop: bool, -} - -impl<'a> CompressorPass<'a> for PeepholeRemoveDeadCode { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl<'a> Traverse<'a> for PeepholeRemoveDeadCode { - fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { +impl<'a, 'b> PeepholeOptimizations { + pub fn remove_dead_code_exit_statements( + &mut self, + stmts: &mut Vec<'a, Statement<'a>>, + ctx: &mut TraverseCtx<'a>, + ) { if stmts.iter().any(|stmt| matches!(stmt, Statement::EmptyStatement(_))) { stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_))); } self.dead_code_elimination(stmts, Ctx(ctx)); } - fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + pub fn remove_dead_code_exit_statement( + &mut self, + stmt: &mut Statement<'a>, + ctx: &mut TraverseCtx<'a>, + ) { let ctx = Ctx(ctx); if let Some(new_stmt) = match stmt { Statement::BlockStatement(s) => Self::try_optimize_block(s, ctx), @@ -59,7 +56,11 @@ impl<'a> Traverse<'a> for PeepholeRemoveDeadCode { } } - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + pub fn remove_dead_code_exit_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { let ctx = Ctx(ctx); if let Some(folded_expr) = match expr { Expression::ConditionalExpression(e) => Self::try_fold_conditional_expression(e, ctx), @@ -73,17 +74,15 @@ impl<'a> Traverse<'a> for PeepholeRemoveDeadCode { } } - fn exit_class_body(&mut self, body: &mut ClassBody<'a>, _ctx: &mut TraverseCtx<'a>) { + pub fn remove_dead_code_exit_class_body( + &mut self, + body: &mut ClassBody<'a>, + _ctx: &mut TraverseCtx<'a>, + ) { if !self.in_fixed_loop { Self::remove_empty_class_static_block(body); } } -} - -impl<'a, 'b> PeepholeRemoveDeadCode { - pub fn new(in_fixed_loop: bool) -> Self { - Self { changed: false, in_fixed_loop } - } /// Removes dead code thats comes after `return` statements after inlining `if` statements fn dead_code_elimination(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: Ctx<'a, 'b>) { 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 c0f0ff34a84ce..3b0da8e5e8f62 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 @@ -7,34 +7,23 @@ use oxc_ecmascript::{ constant_evaluation::ConstantEvaluation, StringCharAt, StringCharCodeAt, StringIndexOf, StringLastIndexOf, StringSubstring, ToInt32, }; -use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx}; +use oxc_traverse::{Ancestor, TraverseCtx}; -use crate::{ctx::Ctx, CompressorPass}; +use crate::ctx::Ctx; -/// Minimize With Known Methods -/// -pub struct PeepholeReplaceKnownMethods { - pub(crate) changed: bool, -} - -impl<'a> CompressorPass<'a> for PeepholeReplaceKnownMethods { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} +use super::PeepholeOptimizations; -impl<'a> Traverse<'a> for PeepholeReplaceKnownMethods { - fn exit_expression(&mut self, node: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { +impl<'a> PeepholeOptimizations { + /// Minimize With Known Methods + /// + pub fn replace_known_methods_exit_expression( + &mut self, + node: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { self.try_fold_concat_chain(node, ctx); self.try_fold_known_string_methods(node, ctx); } -} - -impl<'a> PeepholeReplaceKnownMethods { - pub fn new() -> Self { - Self { changed: false } - } fn try_fold_known_string_methods( &mut self, 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 1b1ff7effb8e4..cc5dcb7669847 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 @@ -13,41 +13,34 @@ use oxc_syntax::{ number::NumberBase, operator::{BinaryOperator, UnaryOperator}, }; -use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx}; +use oxc_traverse::{Ancestor, TraverseCtx}; -use crate::{ctx::Ctx, CompressorPass}; +use crate::ctx::Ctx; + +use super::PeepholeOptimizations; /// A peephole optimization that minimizes code by simplifying conditional /// expressions, replacing IFs with HOOKs, replacing object constructors /// with literals, and simplifying returns. /// -pub struct PeepholeSubstituteAlternateSyntax { - target: ESTarget, - - in_fixed_loop: bool, - - in_define_export: bool, - - pub(crate) changed: bool, -} - -impl<'a> CompressorPass<'a> for PeepholeSubstituteAlternateSyntax { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { - fn exit_catch_clause(&mut self, catch: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) { +impl<'a, 'b> PeepholeOptimizations { + pub fn substitute_catch_clause( + &mut self, + catch: &mut CatchClause<'a>, + ctx: &mut TraverseCtx<'a>, + ) { self.compress_catch_clause(catch, ctx); } - fn exit_object_property(&mut self, prop: &mut ObjectProperty<'a>, ctx: &mut TraverseCtx<'a>) { + pub fn substitute_object_property( + &mut self, + prop: &mut ObjectProperty<'a>, + ctx: &mut TraverseCtx<'a>, + ) { self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } - fn exit_assignment_target_property_property( + pub fn substitute_assignment_target_property_property( &mut self, prop: &mut AssignmentTargetPropertyProperty<'a>, ctx: &mut TraverseCtx<'a>, @@ -55,11 +48,15 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { self.try_compress_property_key(&mut prop.name, &mut prop.computed, ctx); } - fn exit_binding_property(&mut self, prop: &mut BindingProperty<'a>, ctx: &mut TraverseCtx<'a>) { + pub fn substitute_binding_property( + &mut self, + prop: &mut BindingProperty<'a>, + ctx: &mut TraverseCtx<'a>, + ) { self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } - fn exit_method_definition( + pub fn substitute_method_definition( &mut self, prop: &mut MethodDefinition<'a>, ctx: &mut TraverseCtx<'a>, @@ -67,7 +64,7 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } - fn exit_property_definition( + pub fn substitute_property_definition( &mut self, prop: &mut PropertyDefinition<'a>, ctx: &mut TraverseCtx<'a>, @@ -75,7 +72,7 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } - fn exit_accessor_property( + pub fn substitute_accessor_property( &mut self, prop: &mut AccessorProperty<'a>, ctx: &mut TraverseCtx<'a>, @@ -83,11 +80,15 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { self.try_compress_property_key(&mut prop.key, &mut prop.computed, ctx); } - fn exit_return_statement(&mut self, stmt: &mut ReturnStatement<'a>, ctx: &mut TraverseCtx<'a>) { + pub fn substitute_return_statement( + &mut self, + stmt: &mut ReturnStatement<'a>, + ctx: &mut TraverseCtx<'a>, + ) { self.compress_return_statement(stmt, ctx); } - fn exit_variable_declaration( + pub fn substitute_variable_declaration( &mut self, decl: &mut VariableDeclaration<'a>, ctx: &mut TraverseCtx<'a>, @@ -97,36 +98,19 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { } } - /// Set `in_define_export` flag if this is a top-level statement of form: - /// ```js - /// Object.defineProperty(exports, 'Foo', { - /// enumerable: true, - /// get: function() { return Foo_1.Foo; } - /// }); - /// ``` - fn enter_call_expression( + pub fn substitute_call_expression( &mut self, - call_expr: &mut CallExpression<'a>, + expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>, ) { - if !self.in_fixed_loop { - let parent = ctx.parent(); - if (parent.is_expression_statement() || parent.is_sequence_expression()) - && Self::is_object_define_property_exports(call_expr) - { - self.in_define_export = true; - } - } - } - - fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { - if !self.in_fixed_loop { - self.in_define_export = false; - } self.try_compress_call_expression_arguments(expr, ctx); } - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + pub fn substitute_exit_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { let ctx = Ctx(ctx); // Change syntax @@ -188,12 +172,6 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { self.changed = true; } } -} - -impl<'a, 'b> PeepholeSubstituteAlternateSyntax { - pub fn new(target: ESTarget, in_fixed_loop: bool) -> Self { - Self { target, in_fixed_loop, in_define_export: false, changed: false } - } fn compress_catch_clause(&mut self, catch: &mut CatchClause<'_>, ctx: &mut TraverseCtx<'a>) { if !self.in_fixed_loop && self.target >= ESTarget::ES2019 { @@ -218,25 +196,6 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax { } } - /// Test `Object.defineProperty(exports, ...)` - fn is_object_define_property_exports(call_expr: &CallExpression<'a>) -> bool { - let Some(Argument::Identifier(ident)) = call_expr.arguments.first() else { return false }; - if ident.name != "exports" { - return false; - } - - // Use tighter check than `call_expr.callee.is_specific_member_access("Object", "defineProperty")` - // because we're looking for `Object.defineProperty` specifically, not e.g. `Object['defineProperty']` - if let Expression::StaticMemberExpression(callee) = &call_expr.callee { - if let Expression::Identifier(id) = &callee.object { - if id.name == "Object" && callee.property.name == "defineProperty" { - return true; - } - } - } - false - } - /// Transforms `undefined` => `void 0` fn try_compress_undefined( &self, @@ -265,9 +224,6 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax { ) -> Option> { debug_assert!(!self.in_fixed_loop); let Expression::BooleanLiteral(lit) = expr else { return None }; - if self.in_define_export { - return None; - } let parent = ctx.ancestry.parent(); let no_unary = { if let Ancestor::BinaryExpressionRight(u) = parent { diff --git a/crates/oxc_minifier/src/ast_passes/statement_fusion.rs b/crates/oxc_minifier/src/ast_passes/statement_fusion.rs index 1d52fd1bec2dd..fba0176c3bafa 100644 --- a/crates/oxc_minifier/src/ast_passes/statement_fusion.rs +++ b/crates/oxc_minifier/src/ast_passes/statement_fusion.rs @@ -2,36 +2,23 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; use oxc_ecmascript::side_effects::MayHaveSideEffects; use oxc_span::GetSpan; -use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx}; +use oxc_traverse::TraverseCtx; -use crate::CompressorPass; +use super::PeepholeOptimizations; /// Statement Fusion /// /// Tries to fuse all the statements in a block into a one statement by using COMMAs or statements. /// /// -pub struct StatementFusion { - pub(crate) changed: bool, -} - -impl<'a> CompressorPass<'a> for StatementFusion { - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; - traverse_mut_with_ctx(self, program, ctx); - } -} - -impl<'a> Traverse<'a> for StatementFusion { - fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { +impl<'a> PeepholeOptimizations { + pub fn statement_fusion_exit_statements( + &mut self, + stmts: &mut Vec<'a, Statement<'a>>, + ctx: &mut TraverseCtx<'a>, + ) { self.fuse_statements(stmts, ctx); } -} - -impl<'a> StatementFusion { - pub fn new() -> Self { - Self { changed: false } - } fn fuse_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { let len = stmts.len(); diff --git a/crates/oxc_minifier/src/compressor.rs b/crates/oxc_minifier/src/compressor.rs index dd7ec215e866b..b7b19cb6736b9 100644 --- a/crates/oxc_minifier/src/compressor.rs +++ b/crates/oxc_minifier/src/compressor.rs @@ -34,10 +34,8 @@ impl<'a> Compressor<'a> { // RemoveUnusedCode::new(self.options).build(program, &mut ctx); let normalize_options = NormalizeOptions { convert_while_to_fors: true }; Normalize::new(normalize_options, self.options).build(program, &mut ctx); - PeepholeOptimizations::new(self.options.target, true, self.options) - .run_in_loop(program, &mut ctx); - PeepholeOptimizations::new(self.options.target, false, self.options) - .build(program, &mut ctx); + PeepholeOptimizations::new(self.options.target, true).run_in_loop(program, &mut ctx); + PeepholeOptimizations::new(self.options.target, false).build(program, &mut ctx); } pub fn dead_code_elimination(self, program: &mut Program<'a>) { diff --git a/crates/oxc_minifier/tests/ast_passes/mod.rs b/crates/oxc_minifier/tests/ast_passes/mod.rs index 126b88881c92a..3ea6edaeb7782 100644 --- a/crates/oxc_minifier/tests/ast_passes/mod.rs +++ b/crates/oxc_minifier/tests/ast_passes/mod.rs @@ -98,75 +98,3 @@ fn tagged_template() { test("foo(true && o.f)", "foo(o.f)"); test("foo(true ? o.f : false)", "foo(o.f)"); } - -#[test] -fn cjs() { - // Bail `cjs-module-lexer`. - test_same("0 && (module.exports = { version });"); - - // Bail `cjs-module-lexer`. - // Export is undefined when `enumerable` is "!0". - // https://github.com/nodejs/cjs-module-lexer/issues/64 - test_same( - "Object.defineProperty(exports, 'ConnectableObservable', { - enumerable: true, - get: function() { - return ConnectableObservable_1.ConnectableObservable; - } - });", - ); - test_same( - "Object.defineProperty(exports, 'ConnectableObservable', { - enumerable: true, - get: function() { - return ConnectableObservable_1.ConnectableObservable; - } - });", - ); - // @babel/types/lib/index.js - test( - r#" - Object.defineProperty(exports, "__esModule", { - value: true - }); - Object.defineProperty(exports, "TargetNames", { - enumerable: true, - get: function () { - return _options.TargetNames; - } - });"#, - r#" - Object.defineProperty(exports, "__esModule", { value: true }), Object.defineProperty(exports, "TargetNames", { - enumerable: true, - get: function() { - return _options.TargetNames; - } - });"#, - ); - - test( - r#"Object.keys(_index6).forEach(function(key) { - if (key === "default" || key === "__esModule") return; - if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; - if (key in exports && exports[key] === _index6[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function() { - return _index6[key]; - } - }); - });"#, - " - Object.keys(_index6).forEach(function(key) { - if (key === 'default' || key === '__esModule') return; - if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; - if (key in exports && exports[key] === _index6[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function() { - return _index6[key]; - } - }); -});", - ); -} diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 6a8d41aaf90fb..adecb4e8bfe4c 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -19,7 +19,7 @@ Original | minified | minified | gzip | gzip | Fixture 2.14 MB | 724.06 kB | 724.14 kB | 179.94 kB | 181.07 kB | victory.js -3.20 MB | 1.01 MB | 1.01 MB | 332.01 kB | 331.56 kB | echarts.js +3.20 MB | 1.01 MB | 1.01 MB | 332.00 kB | 331.56 kB | echarts.js 6.69 MB | 2.31 MB | 2.31 MB | 492.53 kB | 488.28 kB | antd.js