Skip to content

Commit 1ecabde

Browse files
committed
refactor(minfier): single match expression
1 parent 2bf9347 commit 1ecabde

File tree

8 files changed

+166
-184
lines changed

8 files changed

+166
-184
lines changed

crates/oxc_minifier/src/peephole/fold_constants.rs

Lines changed: 13 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,12 @@ use crate::ctx::Ctx;
1212

1313
use super::PeepholeOptimizations;
1414

15+
/// Constant Folding
16+
///
17+
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
1518
impl<'a> PeepholeOptimizations {
16-
/// Constant Folding
17-
///
18-
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
19-
pub fn fold_constants_exit_expression(&self, e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
20-
match e {
21-
Expression::TemplateLiteral(t) => {
22-
self.try_inline_values_in_template_literal(t, ctx);
23-
}
24-
Expression::ObjectExpression(e) => self.fold_object_spread(e, ctx),
25-
Expression::BinaryExpression(_) => {
26-
Self::try_fold_binary_expr(e, ctx);
27-
Self::try_fold_binary_typeof_comparison(e, ctx);
28-
}
29-
Expression::UnaryExpression(_) => Self::try_fold_unary_expr(e, ctx),
30-
Expression::StaticMemberExpression(_) => Self::try_fold_static_member_expr(e, ctx),
31-
Expression::ComputedMemberExpression(_) => Self::try_fold_computed_member_expr(e, ctx),
32-
Expression::LogicalExpression(_) => Self::try_fold_logical_expr(e, ctx),
33-
Expression::ChainExpression(_) => Self::try_fold_optional_chain(e, ctx),
34-
Expression::CallExpression(_) => Self::try_fold_number_constructor(e, ctx),
35-
_ => {}
36-
}
37-
}
38-
3919
#[expect(clippy::float_cmp)]
40-
fn try_fold_unary_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
20+
pub fn fold_unary_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
4121
let Expression::UnaryExpression(e) = expr else { return };
4222
match e.operator {
4323
// Do not fold `void 0` back to `undefined`.
@@ -57,7 +37,7 @@ impl<'a> PeepholeOptimizations {
5737
}
5838
}
5939

60-
fn try_fold_static_member_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
40+
pub fn fold_static_member_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
6141
let Expression::StaticMemberExpression(e) = expr else { return };
6242
// TODO: tryFoldObjectPropAccess(n, left, name)
6343
if e.object.may_have_side_effects(ctx) {
@@ -69,7 +49,7 @@ impl<'a> PeepholeOptimizations {
6949
}
7050
}
7151

72-
fn try_fold_computed_member_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
52+
pub fn fold_computed_member_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
7353
let Expression::ComputedMemberExpression(e) = expr else { return };
7454
// TODO: tryFoldObjectPropAccess(n, left, name)
7555
if e.object.may_have_side_effects(ctx) || e.expression.may_have_side_effects(ctx) {
@@ -81,7 +61,7 @@ impl<'a> PeepholeOptimizations {
8161
}
8262
}
8363

84-
fn try_fold_logical_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
64+
pub fn fold_logical_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
8565
let Expression::LogicalExpression(e) = expr else { return };
8666
if let Some(changed) = match e.operator {
8767
LogicalOperator::And | LogicalOperator::Or => Self::try_fold_and_or(e, ctx),
@@ -92,7 +72,7 @@ impl<'a> PeepholeOptimizations {
9272
}
9373
}
9474

95-
fn try_fold_optional_chain(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
75+
pub fn fold_chain_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
9676
let Expression::ChainExpression(e) = expr else { return };
9777
let left_expr = match &e.expression {
9878
match_member_expression!(ChainElement) => {
@@ -273,7 +253,7 @@ impl<'a> PeepholeOptimizations {
273253
}
274254

275255
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
276-
fn try_fold_binary_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
256+
pub fn fold_binary_expr(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
277257
let Expression::BinaryExpression(e) = expr else { return };
278258
// TODO: tryReduceOperandsForOp
279259

@@ -537,7 +517,7 @@ impl<'a> PeepholeOptimizations {
537517
))
538518
}
539519

540-
fn try_fold_number_constructor(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
520+
pub fn fold_call_expression(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
541521
let Expression::CallExpression(e) = expr else { return };
542522
if !ctx.is_global_expr("Number", &e.callee) {
543523
return;
@@ -577,7 +557,7 @@ impl<'a> PeepholeOptimizations {
577557
ctx.state.changed = true;
578558
}
579559

580-
fn try_fold_binary_typeof_comparison(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
560+
pub fn fold_binary_typeof_comparison(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
581561
let Expression::BinaryExpression(e) = expr else { return };
582562
// `typeof a == typeof a` -> `true`, `typeof a != typeof a` -> `false`
583563
if e.operator.is_equality() {
@@ -644,7 +624,7 @@ impl<'a> PeepholeOptimizations {
644624
}
645625
}
646626

647-
fn fold_object_spread(&self, e: &mut ObjectExpression<'a>, ctx: &mut Ctx<'a, '_>) {
627+
pub fn fold_object_exp(&self, e: &mut ObjectExpression<'a>, ctx: &mut Ctx<'a, '_>) {
648628
fn should_fold_spread_element<'a>(e: &Expression<'a>, ctx: &mut Ctx<'a, '_>) -> bool {
649629
match e {
650630
Expression::ArrayExpression(o) if o.elements.is_empty() => true,
@@ -739,12 +719,7 @@ impl<'a> PeepholeOptimizations {
739719
/// Inline constant values in template literals
740720
///
741721
/// - `foo${1}bar${i}` => `foo1bar${i}`
742-
fn try_inline_values_in_template_literal(
743-
&self,
744-
t: &mut TemplateLiteral<'a>,
745-
746-
ctx: &mut Ctx<'a, '_>,
747-
) {
722+
pub fn inline_template_literal(&self, t: &mut TemplateLiteral<'a>, ctx: &mut Ctx<'a, '_>) {
748723
let has_expr_to_inline = t
749724
.expressions
750725
.iter()

crates/oxc_minifier/src/peephole/minimize_conditions.rs

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,6 @@ use super::PeepholeOptimizations;
1515
///
1616
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java>
1717
impl<'a> PeepholeOptimizations {
18-
pub fn minimize_conditions_exit_expression(
19-
&self,
20-
expr: &mut Expression<'a>,
21-
ctx: &mut Ctx<'a, '_>,
22-
) {
23-
match expr {
24-
Expression::UnaryExpression(_) => self.try_minimize_not(expr, ctx),
25-
Expression::BinaryExpression(e) => {
26-
Self::try_compress_is_loose_boolean(e, ctx);
27-
Self::try_minimize_binary(expr, ctx);
28-
}
29-
Expression::LogicalExpression(_) => self.minimize_logical_expression(expr, ctx),
30-
Expression::ConditionalExpression(logical_expr) => {
31-
self.try_fold_expr_in_boolean_context(&mut logical_expr.test, ctx);
32-
if let Some(changed) = self.try_minimize_conditional(logical_expr, ctx) {
33-
*expr = changed;
34-
ctx.state.changed = true;
35-
}
36-
}
37-
Expression::AssignmentExpression(e) => {
38-
self.try_compress_normal_assignment_to_combined_logical_assignment(e, ctx);
39-
Self::try_compress_normal_assignment_to_combined_assignment(e, ctx);
40-
Self::try_compress_assignment_to_update_expression(expr, ctx);
41-
}
42-
_ => {}
43-
}
44-
}
45-
4618
// The goal of this function is to "rotate" the AST if it's possible to use the
4719
// left-associative property of the operator to avoid unnecessary parentheses.
4820
//
@@ -95,7 +67,7 @@ impl<'a> PeepholeOptimizations {
9567
// ^^^^^^^^^^^^^^ `ctx.expression_value_type(&e.left).is_boolean()` is `true`.
9668
// `x >> +y !== 0` -> `x >> +y`
9769
// ^^^^^^^ ctx.expression_value_type(&e.left).is_number()` is `true`.
98-
fn try_minimize_binary(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
70+
pub fn try_minimize_binary(expr: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
9971
let Expression::BinaryExpression(e) = expr else { return };
10072
if !e.operator.is_equality() {
10173
return;
@@ -152,7 +124,8 @@ impl<'a> PeepholeOptimizations {
152124
///
153125
/// In `IsLooselyEqual`, `true` and `false` are converted to `1` and `0` first.
154126
/// <https://tc39.es/ecma262/multipage/abstract-operations.html#sec-islooselyequal>
155-
fn try_compress_is_loose_boolean(e: &mut BinaryExpression<'a>, ctx: &mut Ctx<'a, '_>) {
127+
pub fn try_compress_is_loose_boolean(e: &mut Expression<'a>, ctx: &mut Ctx<'a, '_>) {
128+
let Expression::BinaryExpression(e) = e else { return };
156129
if !matches!(e.operator, BinaryOperator::Equality | BinaryOperator::Inequality) {
157130
return;
158131
}
@@ -198,7 +171,7 @@ impl<'a> PeepholeOptimizations {
198171
/// Compress `a = a || b` to `a ||= b`
199172
///
200173
/// This can only be done for resolved identifiers as this would avoid setting `a` when `a` is truthy.
201-
fn try_compress_normal_assignment_to_combined_logical_assignment(
174+
pub fn try_compress_normal_assignment_to_combined_logical_assignment(
202175
&self,
203176
expr: &mut AssignmentExpression<'a>,
204177
ctx: &mut Ctx<'a, '_>,
@@ -236,7 +209,7 @@ impl<'a> PeepholeOptimizations {
236209
}
237210

238211
/// Compress `a = a + b` to `a += b`
239-
fn try_compress_normal_assignment_to_combined_assignment(
212+
pub fn try_compress_normal_assignment_to_combined_assignment(
240213
expr: &mut AssignmentExpression<'a>,
241214
ctx: &mut Ctx<'a, '_>,
242215
) {
@@ -256,7 +229,7 @@ impl<'a> PeepholeOptimizations {
256229

257230
/// Compress `a -= 1` to `--a` and `a -= -1` to `++a`
258231
#[expect(clippy::float_cmp)]
259-
fn try_compress_assignment_to_update_expression(
232+
pub fn try_compress_assignment_to_update_expression(
260233
expr: &mut Expression<'a>,
261234
ctx: &mut Ctx<'a, '_>,
262235
) {

crates/oxc_minifier/src/peephole/minimize_statements.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,13 @@ impl<'a> PeepholeOptimizations {
171171
Self::join_sequence(&mut prev_if.test, &mut b, ctx)
172172
} else {
173173
// "if (a) return b; return c;" => "return a ? b : c;"
174-
let mut expr = self.minimize_conditional(
174+
self.minimize_conditional(
175175
prev_if.span,
176176
prev_if.test.take_in(ctx.ast),
177177
left,
178178
right,
179179
ctx,
180-
);
181-
self.minimize_conditions_exit_expression(&mut expr, ctx);
182-
expr
180+
)
183181
};
184182
let last_return_stmt =
185183
ctx.ast.statement_return(right_span, Some(argument));
@@ -258,15 +256,13 @@ impl<'a> PeepholeOptimizations {
258256
Self::join_sequence(&mut prev_if.test, &mut b, ctx)
259257
} else {
260258
// "if (a) throw b; throw c;" => "throw a ? b : c;"
261-
let mut expr = self.minimize_conditional(
259+
self.minimize_conditional(
262260
prev_if.span,
263261
prev_if.test.take_in(ctx.ast),
264262
left,
265263
right,
266264
ctx,
267-
);
268-
self.minimize_conditions_exit_expression(&mut expr, ctx);
269-
expr
265+
)
270266
};
271267
let last_throw_stmt = ctx.ast.statement_throw(right_span, argument);
272268
result.push(last_throw_stmt);

crates/oxc_minifier/src/peephole/mod.rs

Lines changed: 106 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,81 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations {
172172
}
173173

174174
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
175-
let mut ctx = Ctx::new(ctx);
176-
self.fold_constants_exit_expression(expr, &mut ctx);
177-
self.minimize_conditions_exit_expression(expr, &mut ctx);
178-
self.remove_dead_code_exit_expression(expr, &mut ctx);
179-
self.replace_known_methods_exit_expression(expr, &mut ctx);
180-
self.substitute_exit_expression(expr, &mut ctx);
181-
self.inline_identifier_reference(expr, &mut ctx);
175+
let ctx = &mut Ctx::new(ctx);
176+
match expr {
177+
Expression::TemplateLiteral(t) => {
178+
self.inline_template_literal(t, ctx);
179+
Self::try_fold_template_literal(expr, ctx);
180+
}
181+
Expression::ObjectExpression(e) => self.fold_object_exp(e, ctx),
182+
Expression::BinaryExpression(e) => {
183+
Self::swap_binary_expressions(e);
184+
Self::fold_binary_expr(expr, ctx);
185+
Self::fold_binary_typeof_comparison(expr, ctx);
186+
Self::try_compress_is_loose_boolean(expr, ctx);
187+
Self::try_minimize_binary(expr, ctx);
188+
Self::try_fold_loose_equals_undefined(expr, ctx);
189+
Self::try_compress_typeof_undefined(expr, ctx);
190+
}
191+
Expression::UnaryExpression(_) => {
192+
Self::fold_unary_expr(expr, ctx);
193+
self.try_minimize_not(expr, ctx);
194+
Self::try_remove_unary_plus(expr, ctx);
195+
}
196+
Expression::StaticMemberExpression(_) => {
197+
Self::fold_static_member_expr(expr, ctx);
198+
self.try_fold_known_property_access(expr, ctx);
199+
}
200+
Expression::ComputedMemberExpression(_) => {
201+
Self::fold_computed_member_expr(expr, ctx);
202+
self.try_fold_known_property_access(expr, ctx);
203+
}
204+
Expression::LogicalExpression(_) => {
205+
Self::fold_logical_expr(expr, ctx);
206+
self.minimize_logical_expression(expr, ctx);
207+
Self::try_compress_is_object_and_not_null(expr, ctx);
208+
Self::try_rotate_logical_expression(expr, ctx);
209+
}
210+
Expression::ChainExpression(_) => {
211+
Self::fold_chain_expr(expr, ctx);
212+
Self::try_compress_chain_call_expression(expr, ctx);
213+
}
214+
Expression::CallExpression(_) => {
215+
Self::fold_call_expression(expr, ctx);
216+
self.remove_dead_code_call_expression(expr, ctx);
217+
self.try_fold_concat_chain(expr, ctx);
218+
self.try_fold_known_global_methods(expr, ctx);
219+
self.try_fold_simple_function_call(expr, ctx);
220+
Self::try_fold_object_or_array_constructor(expr, ctx);
221+
}
222+
Expression::ConditionalExpression(logical_expr) => {
223+
self.try_fold_expr_in_boolean_context(&mut logical_expr.test, ctx);
224+
if let Some(changed) = self.try_minimize_conditional(logical_expr, ctx) {
225+
*expr = changed;
226+
ctx.state.changed = true;
227+
}
228+
self.try_fold_conditional_expression(expr, ctx);
229+
}
230+
Expression::AssignmentExpression(e) => {
231+
self.try_compress_normal_assignment_to_combined_logical_assignment(e, ctx);
232+
Self::try_compress_normal_assignment_to_combined_assignment(e, ctx);
233+
Self::try_compress_assignment_to_update_expression(expr, ctx);
234+
self.remove_unused_assignment_expression(expr, ctx);
235+
}
236+
Expression::SequenceExpression(_) => self.try_fold_sequence_expression(expr, ctx),
237+
Expression::ArrowFunctionExpression(e) => self.try_compress_arrow_expression(e, ctx),
238+
Expression::FunctionExpression(e) => Self::try_remove_name_from_functions(e, ctx),
239+
Expression::ClassExpression(e) => Self::try_remove_name_from_classes(e, ctx),
240+
Expression::NewExpression(e) => {
241+
Self::try_compress_typed_array_constructor(e, ctx);
242+
Self::try_fold_new_expression(expr, ctx);
243+
Self::try_fold_object_or_array_constructor(expr, ctx);
244+
}
245+
Expression::BooleanLiteral(_) => Self::try_compress_boolean(expr, ctx),
246+
Expression::ArrayExpression(_) => Self::try_compress_array_expression(expr, ctx),
247+
Expression::Identifier(_) => self.inline_identifier_reference(expr, ctx),
248+
_ => {}
249+
}
182250
}
183251

184252
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 {
328396
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
329397
}
330398

331-
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
332-
let mut ctx = Ctx::new(ctx);
333-
self.inner.fold_constants_exit_expression(expr, &mut ctx);
334-
self.inner.remove_dead_code_exit_expression(expr, &mut ctx);
399+
fn exit_expression(&mut self, e: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
400+
let ctx = &mut Ctx::new(ctx);
401+
match e {
402+
Expression::TemplateLiteral(t) => self.inner.inline_template_literal(t, ctx),
403+
Expression::ObjectExpression(e) => self.inner.fold_object_exp(e, ctx),
404+
Expression::BinaryExpression(_) => {
405+
PeepholeOptimizations::fold_binary_expr(e, ctx);
406+
PeepholeOptimizations::fold_binary_typeof_comparison(e, ctx);
407+
}
408+
Expression::UnaryExpression(_) => PeepholeOptimizations::fold_unary_expr(e, ctx),
409+
Expression::StaticMemberExpression(_) => {
410+
PeepholeOptimizations::fold_static_member_expr(e, ctx);
411+
}
412+
Expression::ComputedMemberExpression(_) => {
413+
PeepholeOptimizations::fold_computed_member_expr(e, ctx);
414+
}
415+
Expression::LogicalExpression(_) => PeepholeOptimizations::fold_logical_expr(e, ctx),
416+
Expression::ChainExpression(_) => PeepholeOptimizations::fold_chain_expr(e, ctx),
417+
Expression::CallExpression(_) => {
418+
PeepholeOptimizations::fold_call_expression(e, ctx);
419+
self.inner.remove_dead_code_call_expression(e, ctx);
420+
}
421+
Expression::ConditionalExpression(_) => {
422+
self.inner.try_fold_conditional_expression(e, ctx);
423+
}
424+
Expression::SequenceExpression(_) => self.inner.try_fold_sequence_expression(e, ctx),
425+
Expression::AssignmentExpression(_) => {
426+
self.inner.remove_unused_assignment_expression(e, ctx);
427+
}
428+
_ => {}
429+
}
335430
}
336431
}
337432

0 commit comments

Comments
 (0)