use oxc_allocator::TakeIn;
use oxc_ast::ast::*;
use oxc_ecmascript::constant_evaluation::{ConstantEvaluation, IsInt32OrUint32};
use oxc_span::GetSpan;
use crate::TraverseCtx;
use super::PeepholeOptimizations;
impl<'a> PeepholeOptimizations {
pub fn minimize_expression_in_boolean_context(
expr: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
match expr {
Expression::UnaryExpression(u1) if u1.operator.is_not() => {
if let Expression::UnaryExpression(u2) = &mut u1.argument
&& u2.operator.is_not()
{
let mut e = u2.argument.take_in(ctx.ast);
Self::minimize_expression_in_boolean_context(&mut e, ctx);
*expr = e;
ctx.state.changed = true;
}
}
Expression::BinaryExpression(e)
if e.operator.is_equality()
&& matches!(&e.right, Expression::NumericLiteral(lit) if lit.value == 0.0)
&& e.left.is_int32_or_uint32(ctx) =>
{
let argument = e.left.take_in(ctx.ast);
*expr = if matches!(
e.operator,
BinaryOperator::StrictInequality | BinaryOperator::Inequality
) {
argument
} else {
ctx.ast.expression_unary(e.span, UnaryOperator::LogicalNot, argument)
};
ctx.state.changed = true;
}
Expression::LogicalExpression(e) if e.operator.is_and() => {
Self::minimize_expression_in_boolean_context(&mut e.left, ctx);
Self::minimize_expression_in_boolean_context(&mut e.right, ctx);
if e.right.get_side_free_boolean_value(ctx) == Some(true) {
*expr = e.left.take_in(ctx.ast);
ctx.state.changed = true;
}
}
Expression::LogicalExpression(e) if e.operator == LogicalOperator::Or => {
Self::minimize_expression_in_boolean_context(&mut e.left, ctx);
Self::minimize_expression_in_boolean_context(&mut e.right, ctx);
if e.right.get_side_free_boolean_value(ctx) == Some(false) {
*expr = e.left.take_in(ctx.ast);
ctx.state.changed = true;
}
}
Expression::ConditionalExpression(e) => {
Self::minimize_expression_in_boolean_context(&mut e.consequent, ctx);
Self::minimize_expression_in_boolean_context(&mut e.alternate, ctx);
if let Some(boolean) = e.consequent.get_side_free_boolean_value(ctx) {
let right = e.alternate.take_in(ctx.ast);
let left = e.test.take_in(ctx.ast);
let span = e.span;
let (op, left) = if boolean {
(LogicalOperator::Or, left)
} else {
(LogicalOperator::And, Self::minimize_not(left.span(), left, ctx))
};
*expr = Self::join_with_left_associative_op(span, op, left, right, ctx);
ctx.state.changed = true;
return;
}
if let Some(boolean) = e.alternate.get_side_free_boolean_value(ctx) {
let left = e.test.take_in(ctx.ast);
let right = e.consequent.take_in(ctx.ast);
let span = e.span;
let (op, left) = if boolean {
(LogicalOperator::Or, Self::minimize_not(left.span(), left, ctx))
} else {
(LogicalOperator::And, left)
};
*expr = Self::join_with_left_associative_op(span, op, left, right, ctx);
ctx.state.changed = true;
}
}
Expression::SequenceExpression(seq_expr) => {
if let Some(last) = seq_expr.expressions.last_mut() {
Self::minimize_expression_in_boolean_context(last, ctx);
}
}
_ => {}
}
}
}