mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 06:39:17 +00:00
perf(minifier): unify merging of last expression into target sequence (#26264)
adds new helper `merge_last_expression_into_sequence` and uses it everywhere when we want to merge last expression to target expression `a; b;` -> `a, b;` this change avoids creation of 2 dummy nodes and unnecessary `drop_statement` on dummy stmt per merge
This commit is contained in:
1 parent
2ef8e266e1
commit
5303f5c884
2 files changed
+65
-81
No files matched your search
@@ -169,13 +169,27 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_last_expression_into_sequence(
|
||||
target: &mut Expression<'a>,
|
||||
result: &mut ArenaVec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if !ctx.options().sequences
|
||||
|| !matches!(result.last(), Some(Statement::ExpressionStatement(_)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
let last_epr = result.pop().unwrap();
|
||||
let Statement::ExpressionStatement(prev_expr_stmt) = last_epr else { unreachable!() };
|
||||
let a = prev_expr_stmt.unbox().expression;
|
||||
ctx.replace_expression_with(target, |b, ctx| Self::join_sequence(a, b, ctx));
|
||||
}
|
||||
|
||||
fn join_sequence(
|
||||
a: &mut Expression<'a>,
|
||||
b: &mut Expression<'a>,
|
||||
a: Expression<'a>,
|
||||
b: Expression<'a>,
|
||||
ctx: &TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let a = a.take_in(ctx);
|
||||
let b = b.take_in(ctx);
|
||||
if let Expression::SequenceExpression(mut sequence_expr) = a {
|
||||
// `(a, b); c`
|
||||
sequence_expr.expressions.push(b);
|
||||
@@ -300,16 +314,10 @@ impl<'a> PeepholeOptimizations {
|
||||
return;
|
||||
}
|
||||
|
||||
if ctx.options().sequences
|
||||
&& let Some(Statement::ExpressionStatement(prev_expr_stmt)) = result.last_mut()
|
||||
{
|
||||
let a = &mut prev_expr_stmt.expression;
|
||||
let b = &mut expr_stmt.expression;
|
||||
expr_stmt.expression = Self::join_sequence(a, b, ctx);
|
||||
let dropped = result.pop().unwrap();
|
||||
ctx.drop_statement(&dropped);
|
||||
}
|
||||
// "var a; a = b();" => "var a = b();"
|
||||
// `a; b;` => `a, b;`
|
||||
Self::merge_last_expression_into_sequence(&mut expr_stmt.expression, result, ctx);
|
||||
|
||||
// `var a; a = b();` => `var a = b();`
|
||||
if Self::merge_leading_assignments_to_declaration(
|
||||
&mut expr_stmt.expression,
|
||||
true,
|
||||
@@ -457,17 +465,10 @@ impl<'a> PeepholeOptimizations {
|
||||
false,
|
||||
);
|
||||
|
||||
if ctx.options().sequences
|
||||
&& let Some(Statement::ExpressionStatement(prev_expr_stmt)) = result.last_mut()
|
||||
{
|
||||
let a = &mut prev_expr_stmt.expression;
|
||||
let b = &mut switch_stmt.discriminant;
|
||||
switch_stmt.discriminant = Self::join_sequence(a, b, ctx);
|
||||
let dropped = result.pop().unwrap();
|
||||
ctx.drop_statement(&dropped);
|
||||
}
|
||||
// `a; switch(b){}` => `switch(a, b){}`
|
||||
Self::merge_last_expression_into_sequence(&mut switch_stmt.discriminant, result, ctx);
|
||||
|
||||
// "var a; switch (a = b(), c) {}" => "var a = b(); switch (c) {}"
|
||||
// `var a; switch (a = b(), c) {}` => `var a = b(); switch (c) {}`
|
||||
Self::merge_leading_assignments_to_declaration(
|
||||
&mut switch_stmt.discriminant,
|
||||
false,
|
||||
@@ -538,19 +539,14 @@ impl<'a> PeepholeOptimizations {
|
||||
) {
|
||||
Self::substitute_single_use_symbol_in_statement(&mut if_stmt.test, result, ctx, false);
|
||||
|
||||
// "var a; if (a = b(), c) d;" => "var a = b(); if (c) d;"
|
||||
// `var a; if (a = b(), c) d;` => `var a = b(); if (c) d;`
|
||||
Self::merge_leading_assignments_to_declaration(&mut if_stmt.test, false, result, ctx);
|
||||
|
||||
// `a; if (b) c;` => `if (a, b) c;`
|
||||
Self::merge_last_expression_into_sequence(&mut if_stmt.test, result, ctx);
|
||||
|
||||
// Absorb a previous expression statement
|
||||
if ctx.options().sequences {
|
||||
if let Some(Statement::ExpressionStatement(prev_expr_stmt)) = result.last_mut() {
|
||||
let a = &mut prev_expr_stmt.expression;
|
||||
let b = &mut if_stmt.test;
|
||||
if_stmt.test = Self::join_sequence(a, b, ctx);
|
||||
let dropped = result.pop().unwrap();
|
||||
ctx.drop_statement(&dropped);
|
||||
}
|
||||
|
||||
if if_stmt.consequent.is_jump_statement() {
|
||||
// Absorb a previous if statement
|
||||
if let Some(Statement::IfStatement(prev_if_stmt)) = result.last_mut()
|
||||
@@ -657,35 +653,33 @@ impl<'a> PeepholeOptimizations {
|
||||
Self::merge_leading_assignments_to_declaration(ret_argument_expr, false, result, ctx);
|
||||
}
|
||||
|
||||
if let Some(argument) = &mut ret_stmt.argument
|
||||
if let Some(argument) = &ret_stmt.argument
|
||||
&& argument.value_type(ctx) == ValueType::Undefined
|
||||
// `return undefined` has a different semantic in async generator function.
|
||||
&& !ctx.is_closest_function_scope_an_async_generator()
|
||||
{
|
||||
let argument = ret_stmt.argument.take().unwrap();
|
||||
if argument.may_have_side_effects(ctx) {
|
||||
if ctx.options().sequences
|
||||
&& let Some(Statement::ExpressionStatement(prev_expr_stmt)) = result.last_mut()
|
||||
{
|
||||
let a = &mut prev_expr_stmt.expression;
|
||||
prev_expr_stmt.expression = Self::join_sequence(a, argument, ctx);
|
||||
// `x; return a,void 0;` -> `x,a,void 0; return;`
|
||||
ctx.replace_expression_with(&mut prev_expr_stmt.expression, |a, ctx| {
|
||||
Self::join_sequence(a, argument, ctx)
|
||||
});
|
||||
} else {
|
||||
// `return a,void 0;` -> `a,void 0; return;`
|
||||
let span = argument.span();
|
||||
let argument = ret_stmt.argument.take().unwrap();
|
||||
result.push(Statement::new_expression_statement(span, argument, ctx));
|
||||
ctx.notice_change();
|
||||
}
|
||||
} else {
|
||||
// `return void 0;` -> `return;`
|
||||
ctx.drop_expression(&argument);
|
||||
}
|
||||
if let Some(old) = ret_stmt.argument.take() {
|
||||
ctx.drop_expression(&old);
|
||||
}
|
||||
} else if ctx.options().sequences
|
||||
&& let Some(argument) = &mut ret_stmt.argument
|
||||
&& let Some(Statement::ExpressionStatement(prev_expr_stmt)) = result.last_mut()
|
||||
{
|
||||
let a = &mut prev_expr_stmt.expression;
|
||||
let new_arg = Self::join_sequence(a, argument, ctx);
|
||||
ctx.replace_expression(argument, new_arg);
|
||||
result.pop();
|
||||
} else if let Some(argument) = &mut ret_stmt.argument {
|
||||
// `a; return b;` => `return b, c;`
|
||||
Self::merge_last_expression_into_sequence(argument, result, ctx);
|
||||
}
|
||||
|
||||
// `if (a) return b; return c;` => `return a ? b : c;`
|
||||
@@ -781,16 +775,8 @@ impl<'a> PeepholeOptimizations {
|
||||
false,
|
||||
);
|
||||
|
||||
// `a; throw x` => `throw a, x`
|
||||
if ctx.options().sequences
|
||||
&& let Some(Statement::ExpressionStatement(prev_expr_stmt)) = result.last_mut()
|
||||
{
|
||||
let a = &mut prev_expr_stmt.expression;
|
||||
let b = &mut throw_stmt.argument;
|
||||
throw_stmt.argument = Self::join_sequence(a, b, ctx);
|
||||
let dropped = result.pop().unwrap();
|
||||
ctx.drop_statement(&dropped);
|
||||
}
|
||||
// `a; throw b;` => `throw a, b;`
|
||||
Self::merge_last_expression_into_sequence(&mut throw_stmt.argument, result, ctx);
|
||||
|
||||
// `var a; throw a = b(), c;` => `var a = b(); throw c;`
|
||||
Self::merge_leading_assignments_to_declaration(
|
||||
@@ -902,14 +888,11 @@ impl<'a> PeepholeOptimizations {
|
||||
|
||||
if ctx.options().sequences {
|
||||
match result.last_mut() {
|
||||
Some(Statement::ExpressionStatement(prev_expr_stmt)) => {
|
||||
Some(Statement::ExpressionStatement(_)) => {
|
||||
if let Some(init) = &mut for_stmt.init {
|
||||
if let Some(init) = init.as_expression_mut() {
|
||||
let a = &mut prev_expr_stmt.expression;
|
||||
let new_init = Self::join_sequence(a, init, ctx);
|
||||
ctx.replace_expression(init, new_init);
|
||||
let dropped = result.pop().unwrap();
|
||||
ctx.drop_statement(&dropped);
|
||||
// `a; for (b;;) c;` => `for (a, b;;) c;`
|
||||
Self::merge_last_expression_into_sequence(init, result, ctx);
|
||||
}
|
||||
} else {
|
||||
let previous = result.pop().unwrap();
|
||||
@@ -979,7 +962,7 @@ impl<'a> PeepholeOptimizations {
|
||||
if ctx.options().sequences {
|
||||
match result.last_mut() {
|
||||
// "a; for (var b in c) d" => "for (var b in a, c) d"
|
||||
Some(Statement::ExpressionStatement(prev_expr_stmt)) => {
|
||||
Some(Statement::ExpressionStatement(_)) => {
|
||||
// Annex B.3.5 allows initializers in non-strict mode
|
||||
// <https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-initializers-in-forin-statement-heads>
|
||||
// Only allow inlining when the for-in variable is declared with `var` and
|
||||
@@ -1006,10 +989,11 @@ impl<'a> PeepholeOptimizations {
|
||||
true
|
||||
};
|
||||
if can_inline {
|
||||
let a = &mut prev_expr_stmt.expression;
|
||||
for_in_stmt.right = Self::join_sequence(a, &mut for_in_stmt.right, ctx);
|
||||
let dropped = result.pop().unwrap();
|
||||
ctx.drop_statement(&dropped);
|
||||
Self::merge_last_expression_into_sequence(
|
||||
&mut for_in_stmt.right,
|
||||
result,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
}
|
||||
// "var a; for (a in b) c" => "for (var a in b) c"
|
||||
|
||||
@@ -5,9 +5,9 @@ checker.ts:
|
||||
sys deallocs: 150
|
||||
sys alloc bytes: 14996632 # 15.00 MB
|
||||
sys peak growth: 10169560 # 10.17 MB
|
||||
arena allocs: 77041
|
||||
arena allocs: 72029
|
||||
arena reallocs: 28269
|
||||
arena size: 17492768 # 17.49 MB
|
||||
arena size: 17401952 # 17.40 MB
|
||||
|
||||
App.tsx:
|
||||
file size: 415342 # 415.34 kB
|
||||
@@ -16,9 +16,9 @@ App.tsx:
|
||||
sys deallocs: 61
|
||||
sys alloc bytes: 2128563 # 2.13 MB
|
||||
sys peak growth: 1612427 # 1.61 MB
|
||||
arena allocs: 9590
|
||||
arena allocs: 8372
|
||||
arena reallocs: 2709
|
||||
arena size: 2741920 # 2.74 MB
|
||||
arena size: 2719488 # 2.72 MB
|
||||
|
||||
RadixUIAdoptionSection.jsx:
|
||||
file size: 2520 # 2.52 kB
|
||||
@@ -38,9 +38,9 @@ pdf.mjs:
|
||||
sys deallocs: 2427
|
||||
sys alloc bytes: 5316219 # 5.32 MB
|
||||
sys peak growth: 3693363 # 3.69 MB
|
||||
arena allocs: 26475
|
||||
arena allocs: 21027
|
||||
arena reallocs: 7699
|
||||
arena size: 5889456 # 5.89 MB
|
||||
arena size: 5783984 # 5.78 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
@@ -49,9 +49,9 @@ antd.js:
|
||||
sys deallocs: 1038
|
||||
sys alloc bytes: 29976375 # 29.98 MB
|
||||
sys peak growth: 19934749 # 19.93 MB
|
||||
arena allocs: 221812
|
||||
arena allocs: 207576
|
||||
arena reallocs: 75941
|
||||
arena size: 45729992 # 45.73 MB
|
||||
arena size: 45456840 # 45.46 MB
|
||||
|
||||
binder.ts:
|
||||
file size: 193077 # 193.08 kB
|
||||
@@ -60,9 +60,9 @@ binder.ts:
|
||||
sys deallocs: 33
|
||||
sys alloc bytes: 963032 # 963.03 kB
|
||||
sys peak growth: 657404 # 657.40 kB
|
||||
arena allocs: 4099
|
||||
arena allocs: 3311
|
||||
arena reallocs: 834
|
||||
arena size: 1098864 # 1.10 MB
|
||||
arena size: 1083312 # 1.08 MB
|
||||
|
||||
kitchen-sink.tsx:
|
||||
file size: 732898 # 732.90 kB
|
||||
@@ -71,6 +71,6 @@ kitchen-sink.tsx:
|
||||
sys deallocs: 1854
|
||||
sys alloc bytes: 5304692 # 5.30 MB
|
||||
sys peak growth: 3707457 # 3.71 MB
|
||||
arena allocs: 34448
|
||||
arena allocs: 32092
|
||||
arena reallocs: 12187
|
||||
arena size: 8732520 # 8.73 MB
|
||||
arena size: 8691304 # 8.69 MB
|
||||
Reference in new issue
Block a user