diff --git a/crates/oxc_minifier/src/peephole/fold_constants.rs b/crates/oxc_minifier/src/peephole/fold_constants.rs index b16fd2cd78ce5..10af04ba55adc 100644 --- a/crates/oxc_minifier/src/peephole/fold_constants.rs +++ b/crates/oxc_minifier/src/peephole/fold_constants.rs @@ -12,32 +12,12 @@ use crate::ctx::Ctx; use super::PeepholeOptimizations; +/// Constant Folding +/// +/// impl<'a> PeepholeOptimizations { - /// Constant Folding - /// - /// - pub fn fold_constants_exit_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { - match e { - Expression::TemplateLiteral(t) => { - self.try_inline_values_in_template_literal(t, ctx); - } - Expression::ObjectExpression(e) => self.fold_object_spread(e, ctx), - Expression::BinaryExpression(_) => { - Self::try_fold_binary_expr(e, ctx); - Self::try_fold_binary_typeof_comparison(e, ctx); - } - Expression::UnaryExpression(_) => Self::try_fold_unary_expr(e, ctx), - Expression::StaticMemberExpression(_) => Self::try_fold_static_member_expr(e, ctx), - Expression::ComputedMemberExpression(_) => Self::try_fold_computed_member_expr(e, ctx), - Expression::LogicalExpression(_) => Self::try_fold_logical_expr(e, ctx), - Expression::ChainExpression(_) => Self::try_fold_optional_chain(e, ctx), - Expression::CallExpression(_) => Self::try_fold_number_constructor(e, ctx), - _ => {} - } - } - #[expect(clippy::float_cmp)] - fn try_fold_unary_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_unary_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::UnaryExpression(e) = expr else { return }; match e.operator { // Do not fold `void 0` back to `undefined`. @@ -57,7 +37,7 @@ impl<'a> PeepholeOptimizations { } } - fn try_fold_static_member_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_static_member_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::StaticMemberExpression(e) = expr else { return }; // TODO: tryFoldObjectPropAccess(n, left, name) if e.object.may_have_side_effects(ctx) { @@ -69,7 +49,7 @@ impl<'a> PeepholeOptimizations { } } - fn try_fold_computed_member_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_computed_member_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::ComputedMemberExpression(e) = expr else { return }; // TODO: tryFoldObjectPropAccess(n, left, name) if e.object.may_have_side_effects(ctx) || e.expression.may_have_side_effects(ctx) { @@ -81,7 +61,7 @@ impl<'a> PeepholeOptimizations { } } - fn try_fold_logical_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_logical_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::LogicalExpression(e) = expr else { return }; if let Some(changed) = match e.operator { LogicalOperator::And | LogicalOperator::Or => Self::try_fold_and_or(e, ctx), @@ -92,7 +72,7 @@ impl<'a> PeepholeOptimizations { } } - fn try_fold_optional_chain(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_chain_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::ChainExpression(e) = expr else { return }; let left_expr = match &e.expression { match_member_expression!(ChainElement) => { @@ -273,7 +253,7 @@ impl<'a> PeepholeOptimizations { } #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - fn try_fold_binary_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_binary_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::BinaryExpression(e) = expr else { return }; // TODO: tryReduceOperandsForOp @@ -537,7 +517,7 @@ impl<'a> PeepholeOptimizations { )) } - fn try_fold_number_constructor(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_call_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::CallExpression(e) = expr else { return }; if !ctx.is_global_expr("Number", &e.callee) { return; @@ -577,7 +557,7 @@ impl<'a> PeepholeOptimizations { ctx.state.changed = true; } - fn try_fold_binary_typeof_comparison(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_binary_typeof_comparison(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::BinaryExpression(e) = expr else { return }; // `typeof a == typeof a` -> `true`, `typeof a != typeof a` -> `false` if e.operator.is_equality() { @@ -644,7 +624,7 @@ impl<'a> PeepholeOptimizations { } } - fn fold_object_spread(&self, e: &mut ObjectExpression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn fold_object_exp(&self, e: &mut ObjectExpression<'a>, ctx: &mut Ctx<'a, '_>) { fn should_fold_spread_element<'a>(e: &Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { match e { Expression::ArrayExpression(o) if o.elements.is_empty() => true, @@ -739,12 +719,7 @@ impl<'a> PeepholeOptimizations { /// Inline constant values in template literals /// /// - `foo${1}bar${i}` => `foo1bar${i}` - fn try_inline_values_in_template_literal( - &self, - t: &mut TemplateLiteral<'a>, - - ctx: &mut Ctx<'a, '_>, - ) { + pub fn inline_template_literal(&self, t: &mut TemplateLiteral<'a>, ctx: &mut Ctx<'a, '_>) { let has_expr_to_inline = t .expressions .iter() diff --git a/crates/oxc_minifier/src/peephole/minimize_conditions.rs b/crates/oxc_minifier/src/peephole/minimize_conditions.rs index fab25c7cf8584..eecb272f3f426 100644 --- a/crates/oxc_minifier/src/peephole/minimize_conditions.rs +++ b/crates/oxc_minifier/src/peephole/minimize_conditions.rs @@ -15,34 +15,6 @@ use super::PeepholeOptimizations; /// /// impl<'a> PeepholeOptimizations { - pub fn minimize_conditions_exit_expression( - &self, - expr: &mut Expression<'a>, - ctx: &mut Ctx<'a, '_>, - ) { - match expr { - Expression::UnaryExpression(_) => self.try_minimize_not(expr, ctx), - Expression::BinaryExpression(e) => { - Self::try_compress_is_loose_boolean(e, ctx); - Self::try_minimize_binary(expr, ctx); - } - Expression::LogicalExpression(_) => self.minimize_logical_expression(expr, ctx), - Expression::ConditionalExpression(logical_expr) => { - self.try_fold_expr_in_boolean_context(&mut logical_expr.test, ctx); - if let Some(changed) = self.try_minimize_conditional(logical_expr, ctx) { - *expr = changed; - ctx.state.changed = true; - } - } - Expression::AssignmentExpression(e) => { - self.try_compress_normal_assignment_to_combined_logical_assignment(e, ctx); - Self::try_compress_normal_assignment_to_combined_assignment(e, ctx); - Self::try_compress_assignment_to_update_expression(expr, ctx); - } - _ => {} - } - } - // The goal of this function is to "rotate" the AST if it's possible to use the // left-associative property of the operator to avoid unnecessary parentheses. // @@ -95,7 +67,7 @@ impl<'a> PeepholeOptimizations { // ^^^^^^^^^^^^^^ `ctx.expression_value_type(&e.left).is_boolean()` is `true`. // `x >> +y !== 0` -> `x >> +y` // ^^^^^^^ ctx.expression_value_type(&e.left).is_number()` is `true`. - fn try_minimize_binary(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_minimize_binary(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::BinaryExpression(e) = expr else { return }; if !e.operator.is_equality() { return; @@ -152,7 +124,8 @@ impl<'a> PeepholeOptimizations { /// /// In `IsLooselyEqual`, `true` and `false` are converted to `1` and `0` first. /// - fn try_compress_is_loose_boolean(e: &mut BinaryExpression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_compress_is_loose_boolean(e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + let Expression::BinaryExpression(e) = e else { return }; if !matches!(e.operator, BinaryOperator::Equality | BinaryOperator::Inequality) { return; } @@ -198,7 +171,7 @@ impl<'a> PeepholeOptimizations { /// Compress `a = a || b` to `a ||= b` /// /// This can only be done for resolved identifiers as this would avoid setting `a` when `a` is truthy. - fn try_compress_normal_assignment_to_combined_logical_assignment( + pub fn try_compress_normal_assignment_to_combined_logical_assignment( &self, expr: &mut AssignmentExpression<'a>, ctx: &mut Ctx<'a, '_>, @@ -236,7 +209,7 @@ impl<'a> PeepholeOptimizations { } /// Compress `a = a + b` to `a += b` - fn try_compress_normal_assignment_to_combined_assignment( + pub fn try_compress_normal_assignment_to_combined_assignment( expr: &mut AssignmentExpression<'a>, ctx: &mut Ctx<'a, '_>, ) { @@ -256,7 +229,7 @@ impl<'a> PeepholeOptimizations { /// Compress `a -= 1` to `--a` and `a -= -1` to `++a` #[expect(clippy::float_cmp)] - fn try_compress_assignment_to_update_expression( + pub fn try_compress_assignment_to_update_expression( expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>, ) { diff --git a/crates/oxc_minifier/src/peephole/minimize_statements.rs b/crates/oxc_minifier/src/peephole/minimize_statements.rs index 008e695a53b7b..b1531454eb597 100644 --- a/crates/oxc_minifier/src/peephole/minimize_statements.rs +++ b/crates/oxc_minifier/src/peephole/minimize_statements.rs @@ -171,15 +171,13 @@ impl<'a> PeepholeOptimizations { Self::join_sequence(&mut prev_if.test, &mut b, ctx) } else { // "if (a) return b; return c;" => "return a ? b : c;" - let mut expr = self.minimize_conditional( + self.minimize_conditional( prev_if.span, prev_if.test.take_in(ctx.ast), left, right, ctx, - ); - self.minimize_conditions_exit_expression(&mut expr, ctx); - expr + ) }; let last_return_stmt = ctx.ast.statement_return(right_span, Some(argument)); @@ -258,15 +256,13 @@ impl<'a> PeepholeOptimizations { Self::join_sequence(&mut prev_if.test, &mut b, ctx) } else { // "if (a) throw b; throw c;" => "throw a ? b : c;" - let mut expr = self.minimize_conditional( + self.minimize_conditional( prev_if.span, prev_if.test.take_in(ctx.ast), left, right, ctx, - ); - self.minimize_conditions_exit_expression(&mut expr, ctx); - expr + ) }; let last_throw_stmt = ctx.ast.statement_throw(right_span, argument); result.push(last_throw_stmt); diff --git a/crates/oxc_minifier/src/peephole/mod.rs b/crates/oxc_minifier/src/peephole/mod.rs index 5704af4f54e0c..556b7a407a60e 100644 --- a/crates/oxc_minifier/src/peephole/mod.rs +++ b/crates/oxc_minifier/src/peephole/mod.rs @@ -172,13 +172,81 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations { } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - let mut ctx = Ctx::new(ctx); - 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); - self.inline_identifier_reference(expr, &mut ctx); + let ctx = &mut Ctx::new(ctx); + match expr { + Expression::TemplateLiteral(t) => { + self.inline_template_literal(t, ctx); + Self::try_fold_template_literal(expr, ctx); + } + Expression::ObjectExpression(e) => self.fold_object_exp(e, ctx), + Expression::BinaryExpression(e) => { + Self::swap_binary_expressions(e); + Self::fold_binary_expr(expr, ctx); + Self::fold_binary_typeof_comparison(expr, ctx); + Self::try_compress_is_loose_boolean(expr, ctx); + Self::try_minimize_binary(expr, ctx); + Self::try_fold_loose_equals_undefined(expr, ctx); + Self::try_compress_typeof_undefined(expr, ctx); + } + Expression::UnaryExpression(_) => { + Self::fold_unary_expr(expr, ctx); + self.try_minimize_not(expr, ctx); + Self::try_remove_unary_plus(expr, ctx); + } + Expression::StaticMemberExpression(_) => { + Self::fold_static_member_expr(expr, ctx); + self.try_fold_known_property_access(expr, ctx); + } + Expression::ComputedMemberExpression(_) => { + Self::fold_computed_member_expr(expr, ctx); + self.try_fold_known_property_access(expr, ctx); + } + Expression::LogicalExpression(_) => { + Self::fold_logical_expr(expr, ctx); + self.minimize_logical_expression(expr, ctx); + Self::try_compress_is_object_and_not_null(expr, ctx); + Self::try_rotate_logical_expression(expr, ctx); + } + Expression::ChainExpression(_) => { + Self::fold_chain_expr(expr, ctx); + Self::try_compress_chain_call_expression(expr, ctx); + } + Expression::CallExpression(_) => { + Self::fold_call_expression(expr, ctx); + self.remove_dead_code_call_expression(expr, ctx); + self.try_fold_concat_chain(expr, ctx); + self.try_fold_known_global_methods(expr, ctx); + self.try_fold_simple_function_call(expr, ctx); + Self::try_fold_object_or_array_constructor(expr, ctx); + } + Expression::ConditionalExpression(logical_expr) => { + self.try_fold_expr_in_boolean_context(&mut logical_expr.test, ctx); + if let Some(changed) = self.try_minimize_conditional(logical_expr, ctx) { + *expr = changed; + ctx.state.changed = true; + } + self.try_fold_conditional_expression(expr, ctx); + } + Expression::AssignmentExpression(e) => { + self.try_compress_normal_assignment_to_combined_logical_assignment(e, ctx); + Self::try_compress_normal_assignment_to_combined_assignment(e, ctx); + Self::try_compress_assignment_to_update_expression(expr, ctx); + self.remove_unused_assignment_expression(expr, ctx); + } + Expression::SequenceExpression(_) => self.try_fold_sequence_expression(expr, ctx), + Expression::ArrowFunctionExpression(e) => self.try_compress_arrow_expression(e, ctx), + Expression::FunctionExpression(e) => Self::try_remove_name_from_functions(e, ctx), + Expression::ClassExpression(e) => Self::try_remove_name_from_classes(e, ctx), + Expression::NewExpression(e) => { + Self::try_compress_typed_array_constructor(e, ctx); + Self::try_fold_new_expression(expr, ctx); + Self::try_fold_object_or_array_constructor(expr, ctx); + } + Expression::BooleanLiteral(_) => Self::try_compress_boolean(expr, ctx), + Expression::ArrayExpression(_) => Self::try_compress_array_expression(expr, ctx), + Expression::Identifier(_) => self.inline_identifier_reference(expr, ctx), + _ => {} + } } fn exit_unary_expression(&mut self, expr: &mut UnaryExpression<'a>, ctx: &mut TraverseCtx<'a>) { @@ -328,10 +396,37 @@ impl<'a> Traverse<'a, MinifierState<'a>> for DeadCodeElimination { stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_))); } - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - let mut ctx = Ctx::new(ctx); - self.inner.fold_constants_exit_expression(expr, &mut ctx); - self.inner.remove_dead_code_exit_expression(expr, &mut ctx); + fn exit_expression(&mut self, e: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + let ctx = &mut Ctx::new(ctx); + match e { + Expression::TemplateLiteral(t) => self.inner.inline_template_literal(t, ctx), + Expression::ObjectExpression(e) => self.inner.fold_object_exp(e, ctx), + Expression::BinaryExpression(_) => { + PeepholeOptimizations::fold_binary_expr(e, ctx); + PeepholeOptimizations::fold_binary_typeof_comparison(e, ctx); + } + Expression::UnaryExpression(_) => PeepholeOptimizations::fold_unary_expr(e, ctx), + Expression::StaticMemberExpression(_) => { + PeepholeOptimizations::fold_static_member_expr(e, ctx); + } + Expression::ComputedMemberExpression(_) => { + PeepholeOptimizations::fold_computed_member_expr(e, ctx); + } + Expression::LogicalExpression(_) => PeepholeOptimizations::fold_logical_expr(e, ctx), + Expression::ChainExpression(_) => PeepholeOptimizations::fold_chain_expr(e, ctx), + Expression::CallExpression(_) => { + PeepholeOptimizations::fold_call_expression(e, ctx); + self.inner.remove_dead_code_call_expression(e, ctx); + } + Expression::ConditionalExpression(_) => { + self.inner.try_fold_conditional_expression(e, ctx); + } + Expression::SequenceExpression(_) => self.inner.try_fold_sequence_expression(e, ctx), + Expression::AssignmentExpression(_) => { + self.inner.remove_unused_assignment_expression(e, ctx); + } + _ => {} + } } } diff --git a/crates/oxc_minifier/src/peephole/remove_dead_code.rs b/crates/oxc_minifier/src/peephole/remove_dead_code.rs index 89853f686bef8..0e9afc3a2f1b9 100644 --- a/crates/oxc_minifier/src/peephole/remove_dead_code.rs +++ b/crates/oxc_minifier/src/peephole/remove_dead_code.rs @@ -34,18 +34,6 @@ impl<'a> PeepholeOptimizations { self.try_fold_expression_stmt(stmt, ctx); } - pub fn remove_dead_code_exit_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { - match e { - Expression::ConditionalExpression(_) => self.try_fold_conditional_expression(e, ctx), - Expression::SequenceExpression(_) => self.try_fold_sequence_expression(e, ctx), - Expression::CallExpression(_) => self.remove_call_expression(e, ctx), - Expression::AssignmentExpression(_) => { - self.remove_unused_assignment_expression(e, ctx); - } - _ => {} - } - } - /// Remove block from single line blocks /// `{ block } -> block` fn try_optimize_block( @@ -306,7 +294,11 @@ impl<'a> PeepholeOptimizations { } /// Try folding conditional expression (?:) if the condition results of the condition is known. - fn try_fold_conditional_expression(&self, expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_fold_conditional_expression( + &self, + expr: &mut Expression<'a>, + ctx: &mut Ctx<'a, '_>, + ) { let Expression::ConditionalExpression(e) = expr else { return }; let Some(v) = e.test.evaluate_value_to_boolean(ctx) else { return }; ctx.state.changed = true; @@ -340,7 +332,7 @@ impl<'a> PeepholeOptimizations { }; } - fn try_fold_sequence_expression(&self, expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_fold_sequence_expression(&self, expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::SequenceExpression(e) = expr else { return }; let should_keep_as_sequence_expr = e .expressions @@ -452,7 +444,11 @@ impl<'a> PeepholeOptimizations { } } - fn remove_call_expression(&self, expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn remove_dead_code_call_expression( + &self, + expr: &mut Expression<'a>, + ctx: &mut Ctx<'a, '_>, + ) { let Expression::CallExpression(e) = expr else { return }; if let Expression::Identifier(ident) = &e.callee { if let Some(reference_id) = ident.reference_id.get() { diff --git a/crates/oxc_minifier/src/peephole/remove_unused_expression.rs b/crates/oxc_minifier/src/peephole/remove_unused_expression.rs index bd936e409e73d..9431c683308cd 100644 --- a/crates/oxc_minifier/src/peephole/remove_unused_expression.rs +++ b/crates/oxc_minifier/src/peephole/remove_unused_expression.rs @@ -26,7 +26,7 @@ impl<'a> PeepholeOptimizations { 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::CallExpression(_) => self.remove_unused_call_expression(e, ctx), Expression::AssignmentExpression(_) => self.remove_unused_assignment_expression(e, ctx), Expression::ClassExpression(_) => self.remove_unused_class_expression(e, ctx), _ => !e.may_have_side_effects(ctx), @@ -520,7 +520,7 @@ impl<'a> PeepholeOptimizations { false } - fn fold_call_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool { + fn remove_unused_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() { diff --git a/crates/oxc_minifier/src/peephole/replace_known_methods.rs b/crates/oxc_minifier/src/peephole/replace_known_methods.rs index 3162027aa100e..df74fd58c57c7 100644 --- a/crates/oxc_minifier/src/peephole/replace_known_methods.rs +++ b/crates/oxc_minifier/src/peephole/replace_known_methods.rs @@ -19,27 +19,10 @@ use super::PeepholeOptimizations; type Arguments<'a> = oxc_allocator::Vec<'a, Argument<'a>>; +/// Minimize With Known Methods +/// impl<'a> PeepholeOptimizations { - /// Minimize With Known Methods - /// - pub fn replace_known_methods_exit_expression( - &self, - e: &mut Expression<'a>, - ctx: &mut Ctx<'a, '_>, - ) { - match e { - Expression::CallExpression(_) => { - self.try_fold_concat_chain(e, ctx); - self.try_fold_known_global_methods(e, ctx); - } - Expression::StaticMemberExpression(_) | Expression::ComputedMemberExpression(_) => { - self.try_fold_known_property_access(e, ctx); - } - _ => {} - } - } - - fn try_fold_known_global_methods(&self, node: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_fold_known_global_methods(&self, node: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::CallExpression(ce) = node else { return }; // Use constant evaluation for known method calls @@ -135,7 +118,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>, ctx: &mut Ctx<'a, '_>) { + pub 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 { @@ -375,7 +358,7 @@ impl<'a> PeepholeOptimizations { } } - fn try_fold_known_property_access(&self, node: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub 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) diff --git a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs index 358c7c855ade8..8ef288f2fb716 100644 --- a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs @@ -152,40 +152,7 @@ impl<'a> PeepholeOptimizations { self.try_flatten_arguments(&mut expr.arguments, ctx); } - 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, ctx), - Expression::ChainExpression(e) => self.try_compress_chain_call_expression(e, ctx), - Expression::BinaryExpression(e) => { - Self::swap_binary_expressions(e); - Self::try_fold_loose_equals_undefined(expr, ctx); - Self::try_compress_typeof_undefined(expr, ctx); - } - Expression::FunctionExpression(e) => self.try_remove_name_from_functions(e, ctx), - Expression::ClassExpression(e) => self.try_remove_name_from_classes(e, ctx), - Expression::NewExpression(e) => { - Self::try_compress_typed_array_constructor(e, ctx); - Self::try_fold_new_expression(expr, ctx); - Self::try_fold_object_or_array_constructor(expr, ctx); - } - Expression::CallExpression(_) => { - self.try_fold_simple_function_call(expr, ctx); - Self::try_fold_object_or_array_constructor(expr, ctx); - } - Expression::LogicalExpression(_) => { - Self::try_compress_is_object_and_not_null(expr, ctx); - Self::try_rotate_logical_expression(expr, ctx); - } - Expression::TemplateLiteral(_) => Self::try_fold_template_literal(expr, ctx), - Expression::UnaryExpression(_) => Self::try_remove_unary_plus(expr, ctx), - Expression::BooleanLiteral(_) => Self::try_compress_boolean(expr, ctx), - Expression::ArrayExpression(_) => Self::try_compress_array_expression(expr, ctx), - _ => {} - } - } - - fn swap_binary_expressions(e: &mut BinaryExpression<'a>) { + pub fn swap_binary_expressions(e: &mut BinaryExpression<'a>) { if e.operator.is_equality() && (e.left.is_literal() || e.left.is_no_substitution_template() || e.left.is_void_0()) && !e.right.is_literal() @@ -195,7 +162,7 @@ impl<'a> PeepholeOptimizations { } /// `() => { return foo })` -> `() => foo` - fn try_compress_arrow_expression( + pub fn try_compress_arrow_expression( &self, arrow_expr: &mut ArrowFunctionExpression<'a>, ctx: &mut Ctx<'a, '_>, @@ -228,7 +195,7 @@ impl<'a> PeepholeOptimizations { /// - `typeof foo.bar != "undefined"` -> `foo.bar !== undefined` (for any expression e.g.`typeof (foo + "")`) /// /// Enabled by `compress.typeofs` - fn try_compress_typeof_undefined(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_compress_typeof_undefined(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::BinaryExpression(e) = expr else { return }; let Expression::UnaryExpression(unary_expr) = &e.left else { return }; if !unary_expr.operator.is_typeof() { @@ -269,7 +236,7 @@ impl<'a> PeepholeOptimizations { /// /// - `1 - +b` => `1 - b` (for other operators as well) /// - `+a - 1` => `a - 1` (for other operators as well) - fn try_remove_unary_plus(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_remove_unary_plus(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::UnaryExpression(e) = expr else { return }; if e.operator != UnaryOperator::UnaryPlus { return; @@ -335,7 +302,7 @@ impl<'a> PeepholeOptimizations { } /// `a || (b || c);` -> `(a || b) || c;` - fn try_rotate_logical_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_rotate_logical_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::LogicalExpression(e) = expr else { return }; let Expression::LogicalExpression(right) = &e.right else { return }; if right.operator != e.operator { @@ -367,7 +334,7 @@ impl<'a> PeepholeOptimizations { /// - If `foo` is an object, then `!!foo` is `true`. If `foo` is null, then `!!foo` is `false`. /// /// This compression is safe for `document.all` because `typeof document.all` is not `'object'`. - fn try_compress_is_object_and_not_null(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_compress_is_object_and_not_null(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::LogicalExpression(e) = expr else { return }; let inversed = match e.operator { LogicalOperator::And => false, @@ -506,7 +473,7 @@ impl<'a> PeepholeOptimizations { )) } - fn try_fold_loose_equals_undefined(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_fold_loose_equals_undefined(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::BinaryExpression(e) = expr else { return }; // `foo == void 0` -> `foo == null`, `foo == undefined` -> `foo == null` // `foo != void 0` -> `foo == null`, `foo == undefined` -> `foo == null` @@ -578,7 +545,7 @@ impl<'a> PeepholeOptimizations { /// `Number(0)` -> `0` /// `String()` -> `''` /// `BigInt(1)` -> `1` - fn try_fold_simple_function_call(&self, expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_fold_simple_function_call(&self, expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::CallExpression(e) = expr else { return }; if e.optional || e.arguments.len() >= 2 { return; @@ -679,7 +646,7 @@ impl<'a> PeepholeOptimizations { /// `window.Object()`, `new Object()`, `Object()` -> `{}` /// `window.Array()`, `new Array()`, `Array()` -> `[]` - fn try_fold_object_or_array_constructor(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_fold_object_or_array_constructor(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let callee = match expr { Expression::NewExpression(e) => &e.callee, Expression::CallExpression(e) => &e.callee, @@ -765,7 +732,7 @@ impl<'a> PeepholeOptimizations { /// `new AggregateError()` -> `AggregateError()` /// `new Function()` -> `Function()` /// `new RegExp()` -> `RegExp()` - fn try_fold_new_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_fold_new_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::NewExpression(e) = expr else { return }; let Expression::Identifier(ident) = &e.callee else { return }; let name = ident.name.as_str(); @@ -818,12 +785,9 @@ impl<'a> PeepholeOptimizations { ) } - fn try_compress_chain_call_expression( - &self, - chain_expr: &mut ChainExpression<'a>, - ctx: &mut Ctx<'a, '_>, - ) { - if let ChainElement::CallExpression(call_expr) = &mut chain_expr.expression { + pub fn try_compress_chain_call_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + let Expression::ChainExpression(e) = expr else { return }; + if let ChainElement::CallExpression(call_expr) = &mut e.expression { // `window.Object?.()` -> `Object?.()` if call_expr.arguments.is_empty() && call_expr @@ -837,7 +801,7 @@ impl<'a> PeepholeOptimizations { } } - fn try_fold_template_literal(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_fold_template_literal(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::TemplateLiteral(t) = expr else { return }; let Some(val) = t.to_js_string(ctx) else { return }; *expr = ctx.ast.expression_string_literal(t.span(), ctx.ast.atom_from_cow(&val), None); @@ -950,7 +914,7 @@ 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>, ctx: &mut Ctx<'a, '_>) { + pub fn try_remove_name_from_functions(func: &mut Function<'a>, ctx: &mut Ctx<'a, '_>) { if ctx.options().keep_names.function { return; } @@ -966,7 +930,7 @@ 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>, ctx: &mut Ctx<'a, '_>) { + pub fn try_remove_name_from_classes(class: &mut Class<'a>, ctx: &mut Ctx<'a, '_>) { if ctx.options().keep_names.class { return; } @@ -978,7 +942,7 @@ impl<'a> PeepholeOptimizations { } /// `new Int8Array(0)` -> `new Int8Array()` (also for other TypedArrays) - fn try_compress_typed_array_constructor(e: &mut NewExpression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_compress_typed_array_constructor(e: &mut NewExpression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::Identifier(ident) = &e.callee else { return }; let name = ident.name.as_str(); if !Self::is_typed_array_name(name) || !ctx.is_global_reference(ident) { @@ -992,7 +956,7 @@ impl<'a> PeepholeOptimizations { } /// Transforms boolean expression `true` => `!0` `false` => `!1`. - fn try_compress_boolean(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_compress_boolean(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { let Expression::BooleanLiteral(lit) = expr else { return }; let num = ctx.ast.expression_numeric_literal( lit.span, @@ -1005,7 +969,7 @@ impl<'a> PeepholeOptimizations { } /// Transforms long array expression with string literals to `"str1,str2".split(',')` - fn try_compress_array_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { + pub fn try_compress_array_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) { // this threshold is chosen by hand by checking the minsize output const THRESHOLD: usize = 40;