mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
refactor(minifier): clean up try_minimize_conditional (#8660)
This commit is contained in:
parent
ae8db53d65
commit
3802d28233
3 changed files with 113 additions and 120 deletions
|
|
@ -353,130 +353,108 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
// https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L2745
|
||||
// <https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L2745>
|
||||
fn try_minimize_conditional(
|
||||
expr: &mut ConditionalExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
// `(a, b) ? c : d` -> `a, b ? c : d`
|
||||
if let Expression::SequenceExpression(sequence_expr) = &mut expr.test {
|
||||
if sequence_expr.expressions.len() > 1 {
|
||||
let span = expr.span();
|
||||
let mut sequence = ctx.ast.move_expression(&mut expr.test);
|
||||
let Expression::SequenceExpression(sequence_expr) = &mut sequence else {
|
||||
unreachable!()
|
||||
};
|
||||
let test = sequence_expr.expressions.pop().unwrap();
|
||||
sequence_expr.expressions.push(ctx.ast.expression_conditional(
|
||||
span,
|
||||
test,
|
||||
ctx.ast.move_expression(&mut expr.consequent),
|
||||
ctx.ast.move_expression(&mut expr.alternate),
|
||||
));
|
||||
return Some(sequence);
|
||||
match &mut expr.test {
|
||||
// `x != y ? b : c` -> `x == y ? c : b`
|
||||
Expression::BinaryExpression(test_expr) => {
|
||||
if matches!(
|
||||
test_expr.operator,
|
||||
BinaryOperator::Inequality | BinaryOperator::StrictInequality
|
||||
) {
|
||||
test_expr.operator = test_expr.operator.equality_inverse_operator().unwrap();
|
||||
let test = ctx.ast.move_expression(&mut expr.test);
|
||||
let consequent = ctx.ast.move_expression(&mut expr.consequent);
|
||||
let alternate = ctx.ast.move_expression(&mut expr.alternate);
|
||||
return Some(
|
||||
ctx.ast.expression_conditional(expr.span, test, alternate, consequent),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `!a ? b : c` -> `a ? c : b`
|
||||
if let Expression::UnaryExpression(test_expr) = &mut expr.test {
|
||||
if test_expr.operator.is_not()
|
||||
// Skip `!!!a`
|
||||
&& !matches!(test_expr.argument, Expression::UnaryExpression(_))
|
||||
{
|
||||
let test = ctx.ast.move_expression(&mut test_expr.argument);
|
||||
let consequent = ctx.ast.move_expression(&mut expr.consequent);
|
||||
let alternate = ctx.ast.move_expression(&mut expr.alternate);
|
||||
return Some(
|
||||
ctx.ast.expression_conditional(expr.span, test, alternate, consequent),
|
||||
);
|
||||
// "(a, b) ? c : d" => "a, b ? c : d"
|
||||
Expression::SequenceExpression(sequence_expr) => {
|
||||
if sequence_expr.expressions.len() > 1 {
|
||||
let span = expr.span();
|
||||
let mut sequence = ctx.ast.move_expression(&mut expr.test);
|
||||
let Expression::SequenceExpression(sequence_expr) = &mut sequence else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut cond_expr = ctx.ast.conditional_expression(
|
||||
span,
|
||||
sequence_expr.expressions.pop().unwrap(),
|
||||
ctx.ast.move_expression(&mut expr.consequent),
|
||||
ctx.ast.move_expression(&mut expr.alternate),
|
||||
);
|
||||
let expr =
|
||||
Self::try_minimize_conditional(&mut cond_expr, ctx).unwrap_or_else(|| {
|
||||
Expression::ConditionalExpression(ctx.ast.alloc(cond_expr))
|
||||
});
|
||||
sequence_expr.expressions.push(expr);
|
||||
return Some(sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `x != y ? b : c` -> `x == y ? c : b`
|
||||
if let Expression::BinaryExpression(test_expr) = &mut expr.test {
|
||||
if matches!(
|
||||
test_expr.operator,
|
||||
BinaryOperator::Inequality | BinaryOperator::StrictInequality
|
||||
) {
|
||||
test_expr.operator = test_expr.operator.equality_inverse_operator().unwrap();
|
||||
let test = ctx.ast.move_expression(&mut expr.test);
|
||||
let consequent = ctx.ast.move_expression(&mut expr.consequent);
|
||||
let alternate = ctx.ast.move_expression(&mut expr.alternate);
|
||||
return Some(
|
||||
ctx.ast.expression_conditional(expr.span, test, alternate, consequent),
|
||||
);
|
||||
// "!a ? b : c" => "a ? c : b"
|
||||
Expression::UnaryExpression(test_expr) => {
|
||||
if test_expr.operator.is_not() {
|
||||
let test = ctx.ast.move_expression(&mut test_expr.argument);
|
||||
let consequent = ctx.ast.move_expression(&mut expr.alternate);
|
||||
let alternate = ctx.ast.move_expression(&mut expr.consequent);
|
||||
return Some(
|
||||
ctx.ast.expression_conditional(expr.span, test, consequent, alternate),
|
||||
);
|
||||
}
|
||||
}
|
||||
// "a ? a : b" => "a || b"
|
||||
Expression::Identifier(id) => {
|
||||
if let Expression::Identifier(id2) = &expr.consequent {
|
||||
if id.name == id2.name {
|
||||
return Some(ctx.ast.expression_logical(
|
||||
expr.span,
|
||||
ctx.ast.move_expression(&mut expr.test),
|
||||
LogicalOperator::Or,
|
||||
ctx.ast.move_expression(&mut expr.alternate),
|
||||
));
|
||||
}
|
||||
}
|
||||
// "a ? b : a" => "a && b"
|
||||
if let Expression::Identifier(id2) = &expr.alternate {
|
||||
if id.name == id2.name {
|
||||
return Some(ctx.ast.expression_logical(
|
||||
expr.span,
|
||||
ctx.ast.move_expression(&mut expr.test),
|
||||
LogicalOperator::And,
|
||||
ctx.ast.move_expression(&mut expr.consequent),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// TODO: `/* @__PURE__ */ a() ? b : b` -> `b`
|
||||
|
||||
// `a ? b : b` -> `a, b`
|
||||
if expr.alternate.content_eq(&expr.consequent) {
|
||||
let expressions = ctx.ast.vec_from_array([
|
||||
ctx.ast.move_expression(&mut expr.test),
|
||||
ctx.ast.move_expression(&mut expr.consequent),
|
||||
]);
|
||||
return Some(ctx.ast.expression_sequence(expr.span, expressions));
|
||||
}
|
||||
|
||||
// `a ? true : false` -> `!!a`
|
||||
// `a ? false : true` -> `!a`
|
||||
if let (
|
||||
Expression::Identifier(_),
|
||||
Expression::BooleanLiteral(consequent_lit),
|
||||
Expression::BooleanLiteral(alternate_lit),
|
||||
) = (&expr.test, &expr.consequent, &expr.alternate)
|
||||
// "a ? true : false" => "!!a"
|
||||
// "a ? false : true" => "!a"
|
||||
if let (Expression::BooleanLiteral(left), Expression::BooleanLiteral(right)) =
|
||||
(&expr.consequent, &expr.alternate)
|
||||
{
|
||||
match (consequent_lit.value, alternate_lit.value) {
|
||||
let op = UnaryOperator::LogicalNot;
|
||||
match (left.value, right.value) {
|
||||
(false, true) => {
|
||||
let ident = ctx.ast.move_expression(&mut expr.test);
|
||||
return Some(ctx.ast.expression_unary(
|
||||
expr.span,
|
||||
UnaryOperator::LogicalNot,
|
||||
ident,
|
||||
));
|
||||
return Some(ctx.ast.expression_unary(expr.span, op, ident));
|
||||
}
|
||||
(true, false) => {
|
||||
let ident = ctx.ast.move_expression(&mut expr.test);
|
||||
return Some(ctx.ast.expression_unary(
|
||||
expr.span,
|
||||
UnaryOperator::LogicalNot,
|
||||
ctx.ast.expression_unary(expr.span, UnaryOperator::LogicalNot, ident),
|
||||
));
|
||||
let unary = ctx.ast.expression_unary(expr.span, op, ident);
|
||||
return Some(ctx.ast.expression_unary(expr.span, op, unary));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// `a ? a : b` -> `a || b`
|
||||
if let (Expression::Identifier(test_ident), Expression::Identifier(consequent_ident)) =
|
||||
(&expr.test, &expr.consequent)
|
||||
{
|
||||
if test_ident.name == consequent_ident.name {
|
||||
return Some(ctx.ast.expression_logical(
|
||||
expr.span,
|
||||
ctx.ast.move_expression(&mut expr.test),
|
||||
LogicalOperator::Or,
|
||||
ctx.ast.move_expression(&mut expr.alternate),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// `a ? b : a` -> `a && b`
|
||||
if let (Expression::Identifier(test_ident), Expression::Identifier(alternate_ident)) =
|
||||
(&expr.test, &expr.alternate)
|
||||
{
|
||||
if test_ident.name == alternate_ident.name {
|
||||
return Some(ctx.ast.expression_logical(
|
||||
expr.span,
|
||||
ctx.ast.move_expression(&mut expr.test),
|
||||
LogicalOperator::And,
|
||||
ctx.ast.move_expression(&mut expr.consequent),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// `a ? b ? c : d : d` -> `a && b ? c : d`
|
||||
// "a ? b ? c : d : d" => "a && b ? c : d"
|
||||
if let Expression::ConditionalExpression(consequent) = &mut expr.consequent {
|
||||
if consequent.alternate.content_eq(&expr.alternate) {
|
||||
return Some(ctx.ast.expression_conditional(
|
||||
|
|
@ -493,7 +471,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
// `a ? b : c ? b : d` -> `a || c ? b : d`
|
||||
// "a ? b : c ? b : d" => "a || c ? b : d"
|
||||
if let Expression::ConditionalExpression(alternate) = &mut expr.alternate {
|
||||
if alternate.consequent.content_eq(&expr.consequent) {
|
||||
return Some(ctx.ast.expression_conditional(
|
||||
|
|
@ -510,7 +488,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
// `a ? c : (b, c)` -> `(a || b), c`
|
||||
// "a ? c : (b, c)" => "(a || b), c"
|
||||
if let Expression::SequenceExpression(alternate) = &mut expr.alternate {
|
||||
if alternate.expressions.len() == 2
|
||||
&& alternate.expressions[1].content_eq(&expr.consequent)
|
||||
|
|
@ -530,7 +508,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
// `a ? (b, c) : c` -> `(a && b), c`
|
||||
// "a ? (b, c) : c" => "(a && b), c"
|
||||
if let Expression::SequenceExpression(consequent) = &mut expr.consequent {
|
||||
if consequent.expressions.len() == 2
|
||||
&& consequent.expressions[1].content_eq(&expr.alternate)
|
||||
|
|
@ -550,7 +528,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
// `a ? b || c : c` => "(a && b) || c"
|
||||
// "a ? b || c : c" => "(a && b) || c"
|
||||
if let Expression::LogicalExpression(logical_expr) = &mut expr.consequent {
|
||||
if logical_expr.operator == LogicalOperator::Or
|
||||
&& logical_expr.right.content_eq(&expr.alternate)
|
||||
|
|
@ -569,7 +547,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
// `a ? c : b && c` -> `(a || b) && c`
|
||||
// "a ? c : b && c" => "(a || b) && c"
|
||||
if let Expression::LogicalExpression(logical_expr) = &mut expr.alternate {
|
||||
if logical_expr.operator == LogicalOperator::And
|
||||
&& logical_expr.right.content_eq(&expr.consequent)
|
||||
|
|
@ -715,6 +693,21 @@ impl<'a> PeepholeOptimizations {
|
|||
));
|
||||
}
|
||||
|
||||
if expr.alternate.content_eq(&expr.consequent) {
|
||||
// TODO:
|
||||
// "/* @__PURE__ */ a() ? b : b" => "b"
|
||||
// if ctx.ExprCanBeRemovedIfUnused(test) {
|
||||
// return yes
|
||||
// }
|
||||
|
||||
// "a ? b : b" => "a, b"
|
||||
let expressions = ctx.ast.vec_from_array([
|
||||
ctx.ast.move_expression(&mut expr.test),
|
||||
ctx.ast.move_expression(&mut expr.consequent),
|
||||
]);
|
||||
return Some(ctx.ast.expression_sequence(expr.span, expressions));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -333,16 +333,16 @@ impl<'a> PeepholeOptimizations {
|
|||
/// `[].concat(a).concat(b)` -> `[].concat(a, b)`
|
||||
/// `"".concat(a).concat(b)` -> `"".concat(a, b)`
|
||||
fn try_fold_concat_chain(&mut self, node: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if matches!(ctx.parent(), Ancestor::StaticMemberExpressionObject(_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let original_span = if let Expression::CallExpression(root_call_expr) = node {
|
||||
root_call_expr.span
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if matches!(ctx.parent(), Ancestor::StaticMemberExpressionObject(_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut current_node: &mut Expression = node;
|
||||
let mut collected_arguments = ctx.ast.vec();
|
||||
let new_root_callee: &mut Expression<'a>;
|
||||
|
|
|
|||
|
|
@ -9,19 +9,19 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
|
||||
342.15 kB | 118.19 kB | 118.14 kB | 44.45 kB | 44.37 kB | vue.js
|
||||
|
||||
544.10 kB | 71.74 kB | 72.48 kB | 26.14 kB | 26.20 kB | lodash.js
|
||||
544.10 kB | 71.73 kB | 72.48 kB | 26.15 kB | 26.20 kB | lodash.js
|
||||
|
||||
555.77 kB | 272.89 kB | 270.13 kB | 90.90 kB | 90.80 kB | d3.js
|
||||
|
||||
1.01 MB | 460.18 kB | 458.89 kB | 126.78 kB | 126.71 kB | bundle.min.js
|
||||
1.01 MB | 460.17 kB | 458.89 kB | 126.78 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 652.90 kB | 646.76 kB | 163.54 kB | 163.73 kB | three.js
|
||||
1.25 MB | 652.85 kB | 646.76 kB | 163.53 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 724.01 kB | 724.14 kB | 179.93 kB | 181.07 kB | victory.js
|
||||
2.14 MB | 724.00 kB | 724.14 kB | 179.93 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.00 kB | 331.56 kB | echarts.js
|
||||
|
||||
6.69 MB | 2.31 MB | 2.31 MB | 491.97 kB | 488.28 kB | antd.js
|
||||
6.69 MB | 2.31 MB | 2.31 MB | 491.95 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.48 MB | 3.49 MB | 905.34 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.48 MB | 3.49 MB | 905.35 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue