mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(minifier): collapse if stmt with empty consequent (#8577)
This commit is contained in:
parent
41f2070895
commit
4d4e805691
3 changed files with 106 additions and 63 deletions
|
|
@ -68,7 +68,7 @@ impl<'a> Traverse<'a> for PeepholeMinimizeConditions {
|
|||
|
||||
if let Some(folded_stmt) = match stmt {
|
||||
// If the condition is a literal, we'll let other optimizations try to remove useless code.
|
||||
Statement::IfStatement(s) if !s.test.is_literal() => Self::try_minimize_if(stmt, ctx),
|
||||
Statement::IfStatement(_) => Self::try_minimize_if(stmt, ctx),
|
||||
_ => None,
|
||||
} {
|
||||
*stmt = folded_stmt;
|
||||
|
|
@ -139,77 +139,110 @@ impl<'a> PeepholeMinimizeConditions {
|
|||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Statement<'a>> {
|
||||
let Statement::IfStatement(if_stmt) = stmt else { unreachable!() };
|
||||
let then_branch = &if_stmt.consequent;
|
||||
let else_branch = &if_stmt.alternate;
|
||||
match else_branch {
|
||||
None => {
|
||||
if Self::is_foldable_express_block(&if_stmt.consequent) {
|
||||
let right = Self::get_block_expression(&mut if_stmt.consequent, ctx);
|
||||
let test = ctx.ast.move_expression(&mut if_stmt.test);
|
||||
// `if(!x) foo()` -> `x || foo()`
|
||||
if let Expression::UnaryExpression(unary_expr) = test {
|
||||
if unary_expr.operator.is_not() {
|
||||
let left = unary_expr.unbox().argument;
|
||||
|
||||
// `if (x) foo()` -> `x && foo()`
|
||||
if !if_stmt.test.is_literal() {
|
||||
let then_branch = &if_stmt.consequent;
|
||||
let else_branch = &if_stmt.alternate;
|
||||
match else_branch {
|
||||
None => {
|
||||
if Self::is_foldable_express_block(&if_stmt.consequent) {
|
||||
let right = Self::get_block_expression(&mut if_stmt.consequent, ctx);
|
||||
let test = ctx.ast.move_expression(&mut if_stmt.test);
|
||||
// `if(!x) foo()` -> `x || foo()`
|
||||
if let Expression::UnaryExpression(unary_expr) = test {
|
||||
if unary_expr.operator.is_not() {
|
||||
let left = unary_expr.unbox().argument;
|
||||
let logical_expr = ctx.ast.expression_logical(
|
||||
if_stmt.span,
|
||||
left,
|
||||
LogicalOperator::Or,
|
||||
right,
|
||||
);
|
||||
return Some(
|
||||
ctx.ast.statement_expression(if_stmt.span, logical_expr),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// `if(x) foo()` -> `x && foo()`
|
||||
let logical_expr = ctx.ast.expression_logical(
|
||||
if_stmt.span,
|
||||
left,
|
||||
LogicalOperator::Or,
|
||||
test,
|
||||
LogicalOperator::And,
|
||||
right,
|
||||
);
|
||||
return Some(ctx.ast.statement_expression(if_stmt.span, logical_expr));
|
||||
}
|
||||
} else {
|
||||
// `if(x) foo()` -> `x && foo()`
|
||||
let logical_expr = ctx.ast.expression_logical(
|
||||
if_stmt.span,
|
||||
test,
|
||||
LogicalOperator::And,
|
||||
right,
|
||||
);
|
||||
return Some(ctx.ast.statement_expression(if_stmt.span, logical_expr));
|
||||
}
|
||||
} else {
|
||||
// `if (x) if (y) z` -> `if (x && y) z`
|
||||
if let Some(Statement::IfStatement(then_if_stmt)) = then_branch.get_one_child()
|
||||
{
|
||||
if then_if_stmt.alternate.is_none() {
|
||||
let and_left = ctx.ast.move_expression(&mut if_stmt.test);
|
||||
let Some(then_if_stmt) = if_stmt.consequent.get_one_child_mut() else {
|
||||
unreachable!()
|
||||
};
|
||||
let Statement::IfStatement(mut then_if_stmt) =
|
||||
ctx.ast.move_statement(then_if_stmt)
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let and_right = ctx.ast.move_expression(&mut then_if_stmt.test);
|
||||
then_if_stmt.test = ctx.ast.expression_logical(
|
||||
and_left.span(),
|
||||
and_left,
|
||||
LogicalOperator::And,
|
||||
and_right,
|
||||
);
|
||||
return Some(Statement::IfStatement(then_if_stmt));
|
||||
// `if (x) if (y) z` -> `if (x && y) z`
|
||||
if let Some(Statement::IfStatement(then_if_stmt)) =
|
||||
then_branch.get_one_child()
|
||||
{
|
||||
if then_if_stmt.alternate.is_none() {
|
||||
let and_left = ctx.ast.move_expression(&mut if_stmt.test);
|
||||
let Some(then_if_stmt) = if_stmt.consequent.get_one_child_mut()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let Statement::IfStatement(mut then_if_stmt) =
|
||||
ctx.ast.move_statement(then_if_stmt)
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let and_right = ctx.ast.move_expression(&mut then_if_stmt.test);
|
||||
then_if_stmt.test = ctx.ast.expression_logical(
|
||||
and_left.span(),
|
||||
and_left,
|
||||
LogicalOperator::And,
|
||||
and_right,
|
||||
);
|
||||
return Some(Statement::IfStatement(then_if_stmt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(else_branch) => {
|
||||
let then_branch_is_expression_block = Self::is_foldable_express_block(then_branch);
|
||||
let else_branch_is_expression_block = Self::is_foldable_express_block(else_branch);
|
||||
// `if(foo) bar else baz` -> `foo ? bar : baz`
|
||||
if then_branch_is_expression_block && else_branch_is_expression_block {
|
||||
let test = ctx.ast.move_expression(&mut if_stmt.test);
|
||||
let consequent = Self::get_block_expression(&mut if_stmt.consequent, ctx);
|
||||
let else_branch = if_stmt.alternate.as_mut().unwrap();
|
||||
let alternate = Self::get_block_expression(else_branch, ctx);
|
||||
let expr =
|
||||
ctx.ast.expression_conditional(if_stmt.span, test, consequent, alternate);
|
||||
return Some(ctx.ast.statement_expression(if_stmt.span, expr));
|
||||
Some(else_branch) => {
|
||||
let then_branch_is_expression_block =
|
||||
Self::is_foldable_express_block(then_branch);
|
||||
let else_branch_is_expression_block =
|
||||
Self::is_foldable_express_block(else_branch);
|
||||
// `if(foo) bar else baz` -> `foo ? bar : baz`
|
||||
if then_branch_is_expression_block && else_branch_is_expression_block {
|
||||
let test = ctx.ast.move_expression(&mut if_stmt.test);
|
||||
let consequent = Self::get_block_expression(&mut if_stmt.consequent, ctx);
|
||||
let else_branch = if_stmt.alternate.as_mut().unwrap();
|
||||
let alternate = Self::get_block_expression(else_branch, ctx);
|
||||
let expr = ctx.ast.expression_conditional(
|
||||
if_stmt.span,
|
||||
test,
|
||||
consequent,
|
||||
alternate,
|
||||
);
|
||||
return Some(ctx.ast.statement_expression(if_stmt.span, expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `if (x) {} else foo` -> `if (!x) foo`
|
||||
if match &if_stmt.consequent {
|
||||
Statement::EmptyStatement(_) => true,
|
||||
Statement::BlockStatement(block_stmt) => block_stmt.body.is_empty(),
|
||||
_ => false,
|
||||
} && if_stmt.alternate.is_some()
|
||||
{
|
||||
return Some(ctx.ast.statement_if(
|
||||
if_stmt.span,
|
||||
ctx.ast.expression_unary(
|
||||
if_stmt.test.span(),
|
||||
UnaryOperator::LogicalNot,
|
||||
ctx.ast.move_expression(&mut if_stmt.test),
|
||||
),
|
||||
ctx.ast.move_statement(if_stmt.alternate.as_mut().unwrap()),
|
||||
None,
|
||||
));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -2047,4 +2080,14 @@ mod test {
|
|||
test("typeof foo !== `number`", "typeof foo != `number`");
|
||||
test("`number` !== typeof foo", "`number` != typeof foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negate_empty_if_stmt_consequent() {
|
||||
test("if (x) {} else { foo }", "if (!x) { foo }");
|
||||
test("if (x) ;else { foo }", "if (!x) { foo }");
|
||||
test("if (x) {;} else { foo }", "if (!x) { foo }");
|
||||
|
||||
test_same("if (x) { var foo } else { bar }");
|
||||
test_same("if (x) foo; else { var bar }");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ fn integration() {
|
|||
}
|
||||
console.log(c, d);
|
||||
",
|
||||
"if ((() => console.log('effect'))(), !0) {} else for (var c = 1, c; unknownGlobal; unknownGlobal && !0) var d;
|
||||
"if (!((() => console.log('effect'))(), !0)) for (var c = 1, c; unknownGlobal; unknownGlobal && !0) var d;
|
||||
console.log(c, d);
|
||||
",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
|
||||
544.10 kB | 71.76 kB | 72.48 kB | 26.15 kB | 26.20 kB | lodash.js
|
||||
|
||||
555.77 kB | 272.91 kB | 270.13 kB | 90.90 kB | 90.80 kB | d3.js
|
||||
555.77 kB | 272.90 kB | 270.13 kB | 90.90 kB | 90.80 kB | d3.js
|
||||
|
||||
1.01 MB | 460.17 kB | 458.89 kB | 126.76 kB | 126.71 kB | bundle.min.js
|
||||
1.01 MB | 460.15 kB | 458.89 kB | 126.77 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 652.88 kB | 646.76 kB | 163.54 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 724.06 kB | 724.14 kB | 179.94 kB | 181.07 kB | victory.js
|
||||
2.14 MB | 724.05 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
|
||||
|
||||
6.69 MB | 2.32 MB | 2.31 MB | 492.44 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.49 MB | 3.49 MB | 907.09 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.49 MB | 3.49 MB | 907.07 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue