mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-17 12:56:09 +00:00
fix(minifier): preserve delete semantic when folding non-finite numbers (#27466)
after folding constants to numbers we have to preserve behavior of `delete`. `delete (0/0)`, `delete (1/0)`, `delete +"a"` should evaluate to `true, but if we convert them to `NaN` or `Infinity` this expression was returning `false` with this change we are going to convert them to `delete 0` to preserve result ref https://github.com/oxc-project/oxc/issues/24331#delete-of-a-constant-folded-nan-changes-true-to-false
This commit is contained in:
1 parent
989862fedf
commit
02577b142d
2 files changed
+30
-3
No files matched your search
@@ -9,7 +9,7 @@ use oxc_ecmascript::{
|
||||
use oxc_span::{GetSpan, SPAN};
|
||||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator};
|
||||
|
||||
use crate::TraverseCtx;
|
||||
use crate::{TraverseCtx, generated::ancestor::Ancestor};
|
||||
|
||||
use super::PeepholeOptimizations;
|
||||
|
||||
@@ -30,7 +30,10 @@ impl<'a> PeepholeOptimizations {
|
||||
UnaryOperator::UnaryNegation if e.argument.is_big_int_literal() => {}
|
||||
_ if e.may_have_side_effects(ctx) => {}
|
||||
_ => {
|
||||
if let Some(changed) = e.evaluate_value(ctx).map(|v| ctx.value_to_expr(e.span, v)) {
|
||||
if let Some(mut changed) =
|
||||
e.evaluate_value(ctx).map(|v| ctx.value_to_expr(e.span, v))
|
||||
{
|
||||
Self::normalize_non_finite_number_for_delete(&mut changed, ctx);
|
||||
ctx.replace_expression(expr, changed);
|
||||
}
|
||||
}
|
||||
@@ -386,7 +389,8 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
BinaryOperator::In => None,
|
||||
};
|
||||
if let Some(changed) = changed {
|
||||
if let Some(mut changed) = changed {
|
||||
Self::normalize_non_finite_number_for_delete(&mut changed, ctx);
|
||||
ctx.replace_expression(expr, changed);
|
||||
}
|
||||
}
|
||||
@@ -1019,6 +1023,15 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_non_finite_number_for_delete(expr: &mut Expression<'a>, ctx: &TraverseCtx<'a>) {
|
||||
if let Expression::NumericLiteral(num_expr) = expr
|
||||
&& (num_expr.value.is_nan() || num_expr.value.is_infinite())
|
||||
&& matches!(&ctx.parent(), Ancestor::UnaryExpressionArgument(unary) if unary.operator().is_delete())
|
||||
{
|
||||
num_expr.value = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Outcome of folding an optional-chain at the deepest optional position.
|
||||
|
||||
@@ -1473,6 +1473,20 @@ fn test_issue_8782() {
|
||||
fold("+(void unknown())", "+void unknown()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_delete_number() {
|
||||
fold("delete (+'x')", "delete 0");
|
||||
fold("delete (5 / 0)", "delete 0");
|
||||
fold_same("delete (1 / 0)");
|
||||
fold("delete (0 / -0)", "delete 0");
|
||||
fold("delete (0 / 0)", "delete 0");
|
||||
fold("delete (Infinity / Infinity)", "delete 0");
|
||||
fold("delete (NaN / 0)", "delete 0");
|
||||
fold("delete (1 / NaN)", "delete 0");
|
||||
fold_same("delete (0, NaN)");
|
||||
fold_same("delete (0, Infinity)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inline_values_in_template_literal() {
|
||||
fold("`foo${1}`", "'foo1'");
|
||||
|
||||
Reference in new issue
Block a user