mirror of
https://github.com/danbulant/oxc
synced 2026-05-23 06:08:47 +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 let Some(folded_stmt) = match stmt {
|
||||||
// If the condition is a literal, we'll let other optimizations try to remove useless code.
|
// 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,
|
_ => None,
|
||||||
} {
|
} {
|
||||||
*stmt = folded_stmt;
|
*stmt = folded_stmt;
|
||||||
|
|
@ -139,77 +139,110 @@ impl<'a> PeepholeMinimizeConditions {
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) -> Option<Statement<'a>> {
|
) -> Option<Statement<'a>> {
|
||||||
let Statement::IfStatement(if_stmt) = stmt else { unreachable!() };
|
let Statement::IfStatement(if_stmt) = stmt else { unreachable!() };
|
||||||
let then_branch = &if_stmt.consequent;
|
|
||||||
let else_branch = &if_stmt.alternate;
|
// `if (x) foo()` -> `x && foo()`
|
||||||
match else_branch {
|
if !if_stmt.test.is_literal() {
|
||||||
None => {
|
let then_branch = &if_stmt.consequent;
|
||||||
if Self::is_foldable_express_block(&if_stmt.consequent) {
|
let else_branch = &if_stmt.alternate;
|
||||||
let right = Self::get_block_expression(&mut if_stmt.consequent, ctx);
|
match else_branch {
|
||||||
let test = ctx.ast.move_expression(&mut if_stmt.test);
|
None => {
|
||||||
// `if(!x) foo()` -> `x || foo()`
|
if Self::is_foldable_express_block(&if_stmt.consequent) {
|
||||||
if let Expression::UnaryExpression(unary_expr) = test {
|
let right = Self::get_block_expression(&mut if_stmt.consequent, ctx);
|
||||||
if unary_expr.operator.is_not() {
|
let test = ctx.ast.move_expression(&mut if_stmt.test);
|
||||||
let left = unary_expr.unbox().argument;
|
// `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(
|
let logical_expr = ctx.ast.expression_logical(
|
||||||
if_stmt.span,
|
if_stmt.span,
|
||||||
left,
|
test,
|
||||||
LogicalOperator::Or,
|
LogicalOperator::And,
|
||||||
right,
|
right,
|
||||||
);
|
);
|
||||||
return Some(ctx.ast.statement_expression(if_stmt.span, logical_expr));
|
return Some(ctx.ast.statement_expression(if_stmt.span, logical_expr));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// `if(x) foo()` -> `x && foo()`
|
// `if (x) if (y) z` -> `if (x && y) z`
|
||||||
let logical_expr = ctx.ast.expression_logical(
|
if let Some(Statement::IfStatement(then_if_stmt)) =
|
||||||
if_stmt.span,
|
then_branch.get_one_child()
|
||||||
test,
|
{
|
||||||
LogicalOperator::And,
|
if then_if_stmt.alternate.is_none() {
|
||||||
right,
|
let and_left = ctx.ast.move_expression(&mut if_stmt.test);
|
||||||
);
|
let Some(then_if_stmt) = if_stmt.consequent.get_one_child_mut()
|
||||||
return Some(ctx.ast.statement_expression(if_stmt.span, logical_expr));
|
else {
|
||||||
}
|
unreachable!()
|
||||||
} else {
|
};
|
||||||
// `if (x) if (y) z` -> `if (x && y) z`
|
let Statement::IfStatement(mut then_if_stmt) =
|
||||||
if let Some(Statement::IfStatement(then_if_stmt)) = then_branch.get_one_child()
|
ctx.ast.move_statement(then_if_stmt)
|
||||||
{
|
else {
|
||||||
if then_if_stmt.alternate.is_none() {
|
unreachable!()
|
||||||
let and_left = ctx.ast.move_expression(&mut if_stmt.test);
|
};
|
||||||
let Some(then_if_stmt) = if_stmt.consequent.get_one_child_mut() else {
|
let and_right = ctx.ast.move_expression(&mut then_if_stmt.test);
|
||||||
unreachable!()
|
then_if_stmt.test = ctx.ast.expression_logical(
|
||||||
};
|
and_left.span(),
|
||||||
let Statement::IfStatement(mut then_if_stmt) =
|
and_left,
|
||||||
ctx.ast.move_statement(then_if_stmt)
|
LogicalOperator::And,
|
||||||
else {
|
and_right,
|
||||||
unreachable!()
|
);
|
||||||
};
|
return Some(Statement::IfStatement(then_if_stmt));
|
||||||
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) => {
|
||||||
Some(else_branch) => {
|
let then_branch_is_expression_block =
|
||||||
let then_branch_is_expression_block = Self::is_foldable_express_block(then_branch);
|
Self::is_foldable_express_block(then_branch);
|
||||||
let else_branch_is_expression_block = Self::is_foldable_express_block(else_branch);
|
let else_branch_is_expression_block =
|
||||||
// `if(foo) bar else baz` -> `foo ? bar : baz`
|
Self::is_foldable_express_block(else_branch);
|
||||||
if then_branch_is_expression_block && else_branch_is_expression_block {
|
// `if(foo) bar else baz` -> `foo ? bar : baz`
|
||||||
let test = ctx.ast.move_expression(&mut if_stmt.test);
|
if then_branch_is_expression_block && else_branch_is_expression_block {
|
||||||
let consequent = Self::get_block_expression(&mut if_stmt.consequent, ctx);
|
let test = ctx.ast.move_expression(&mut if_stmt.test);
|
||||||
let else_branch = if_stmt.alternate.as_mut().unwrap();
|
let consequent = Self::get_block_expression(&mut if_stmt.consequent, ctx);
|
||||||
let alternate = Self::get_block_expression(else_branch, ctx);
|
let else_branch = if_stmt.alternate.as_mut().unwrap();
|
||||||
let expr =
|
let alternate = Self::get_block_expression(else_branch, ctx);
|
||||||
ctx.ast.expression_conditional(if_stmt.span, test, consequent, alternate);
|
let expr = ctx.ast.expression_conditional(
|
||||||
return Some(ctx.ast.statement_expression(if_stmt.span, expr));
|
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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2047,4 +2080,14 @@ mod test {
|
||||||
test("typeof foo !== `number`", "typeof foo != `number`");
|
test("typeof foo !== `number`", "typeof foo != `number`");
|
||||||
test("`number` !== typeof foo", "`number` != typeof foo");
|
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);
|
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);
|
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
|
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
|
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
|
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
|
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