diff --git a/crates/oxc_ast/src/ast_builder_impl.rs b/crates/oxc_ast/src/ast_builder_impl.rs index 770fddc18f811..71a3614f1b030 100644 --- a/crates/oxc_ast/src/ast_builder_impl.rs +++ b/crates/oxc_ast/src/ast_builder_impl.rs @@ -140,6 +140,11 @@ impl<'a> AstBuilder<'a> { num, ))) } + /// `NaN` + #[inline] + pub fn nan(self, span: Span) -> Expression<'a> { + self.expression_numeric_literal(span, f64::NAN, None, NumberBase::Decimal) + } /// `"use strict"` directive #[inline] diff --git a/crates/oxc_minifier/src/peephole/normalize.rs b/crates/oxc_minifier/src/peephole/normalize.rs index e7f1899bb11e3..ead774833e4ea 100644 --- a/crates/oxc_minifier/src/peephole/normalize.rs +++ b/crates/oxc_minifier/src/peephole/normalize.rs @@ -26,7 +26,7 @@ pub struct NormalizeOptions { /// * convert whiles to fors /// * convert `const` to `let` for non-exported variables /// * convert `Infinity` to `f64::INFINITY` -/// * convert `NaN` to `f64::NaN` +/// * convert `NaN` and `Number.NaN` to `f64::NaN` /// * convert `var x; void x` to `void 0` /// * convert `undefined` to `void 0` /// * apply `pure` to side-effect free global constructors (e.g. `new WeakMap()`) @@ -82,7 +82,7 @@ impl<'a> Traverse<'a> for Normalize { if let Some(e) = match expr { Expression::Identifier(ident) => Self::try_compress_identifier(ident, ctx), Expression::UnaryExpression(e) if e.operator.is_void() => { - Self::convert_void_ident(e, ctx); + Self::fold_void_ident(e, ctx); None } Expression::ArrowFunctionExpression(e) => { @@ -92,6 +92,7 @@ impl<'a> Traverse<'a> for Normalize { Expression::CallExpression(_) if self.compress_options.drop_console => { self.compress_console(expr, ctx) } + Expression::StaticMemberExpression(e) => Self::fold_number_nan_to_nan(e, ctx), _ => None, } { *expr = e; @@ -214,12 +215,7 @@ impl<'a> Normalize { if Self::is_unary_delete_ancestor(ctx.ancestors()) { return None; } - Some(ctx.ast.expression_numeric_literal( - ident.span, - f64::NAN, - None, - NumberBase::Decimal, - )) + Some(ctx.ast.nan(ident.span)) } _ => None, } @@ -239,7 +235,7 @@ impl<'a> Normalize { false } - fn convert_void_ident(e: &mut UnaryExpression<'a>, ctx: &TraverseCtx<'a>) { + fn fold_void_ident(e: &mut UnaryExpression<'a>, ctx: &TraverseCtx<'a>) { debug_assert!(e.operator.is_void()); let Expression::Identifier(ident) = &e.argument else { return }; if Ctx(ctx).is_global_reference(ident) { @@ -248,6 +244,23 @@ impl<'a> Normalize { e.argument = ctx.ast.expression_numeric_literal(ident.span, 0.0, None, NumberBase::Decimal); } + fn fold_number_nan_to_nan( + e: &StaticMemberExpression<'a>, + ctx: &TraverseCtx<'a>, + ) -> Option> { + let Expression::Identifier(ident) = &e.object else { return None }; + if ident.name != "Number" { + return None; + } + if e.property.name != "NaN" { + return None; + } + if !Ctx(ctx).is_global_reference(ident) { + return None; + } + Some(ctx.ast.nan(ident.span)) + } + fn set_no_side_effects_to_call_expr(call_expr: &mut CallExpression<'a>, ctx: &TraverseCtx<'a>) { if call_expr.pure { return; @@ -379,4 +392,10 @@ mod test { fn drop_debugger() { test("debugger", ""); } + + #[test] + fn fold_number_nan() { + test("foo(Number.NaN)", "foo(NaN)"); + test_same("let Number; foo(Number.NaN)"); + } } diff --git a/crates/oxc_minifier/tests/peephole/dead_code_elimination.rs b/crates/oxc_minifier/tests/peephole/dead_code_elimination.rs index 7c580c44a03e2..d85dd75dd695f 100644 --- a/crates/oxc_minifier/tests/peephole/dead_code_elimination.rs +++ b/crates/oxc_minifier/tests/peephole/dead_code_elimination.rs @@ -183,6 +183,12 @@ fn pure_comment_for_pure_global_constructors() { test("var x = new WeakSet([])", "var x = /* @__PURE__ */ new WeakSet([]);\n"); } +#[test] +fn fold_number_nan() { + test("foo(Number.NaN)", "foo(NaN)"); + test_same("let Number; foo(Number.NaN)"); +} + // https://github.com/terser/terser/blob/v5.9.0/test/compress/dead-code.js #[test] fn dce_from_terser() { diff --git a/tasks/coverage/snapshots/minifier_test262.snap b/tasks/coverage/snapshots/minifier_test262.snap index b64cb508e7b96..b236892570408 100644 --- a/tasks/coverage/snapshots/minifier_test262.snap +++ b/tasks/coverage/snapshots/minifier_test262.snap @@ -2,6 +2,4 @@ commit: 4b5d36ab minifier_test262 Summary: AST Parsed : 42013/42013 (100.00%) -Positive Passed: 42012/42013 (100.00%) -Compress: tasks/coverage/test262/test/built-ins/Date/prototype/valueOf/S9.4_A3_T2.js - +Positive Passed: 42013/42013 (100.00%)