From fd5af73a222f254e7cf4b6aabe20b932356116b4 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Sat, 4 Jan 2025 09:56:03 +0000 Subject: [PATCH] feat(minifier): minimize `Number` constructor (#8245) --- .../src/ast_passes/peephole_fold_constants.rs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index eb65b28c273ea..56feb06d586e7 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -35,6 +35,7 @@ impl<'a> Traverse<'a> for PeepholeFoldConstants { Expression::StaticMemberExpression(e) => Self::try_fold_static_member_expr(e, ctx), Expression::LogicalExpression(e) => Self::try_fold_logical_expr(e, ctx), Expression::ChainExpression(e) => Self::try_fold_optional_chain(e, ctx), + Expression::CallExpression(e) => Self::try_fold_number_constructor(e, ctx), _ => None, } { *expr = folded_expr; @@ -555,6 +556,51 @@ impl<'a, 'b> PeepholeFoldConstants { } None } + + fn try_fold_number_constructor( + e: &CallExpression<'a>, + ctx: Ctx<'a, 'b>, + ) -> Option> { + let Expression::Identifier(ident) = &e.callee else { return None }; + if ident.name != "Number" { + return None; + } + if !ctx.is_global_reference(ident) { + return None; + } + if e.arguments.len() != 1 { + return None; + } + Some(ctx.value_to_expr( + e.span, + ConstantValue::Number(match &e.arguments[0] { + // `Number(undefined)` -> `NaN` + Argument::Identifier(ident) if ctx.is_identifier_undefined(ident) => f64::NAN, + // `Number(null)` -> `0` + Argument::NullLiteral(_) => 0.0, + // `Number(true)` -> `1` `Number(false)` -> `0` + Argument::BooleanLiteral(b) => f64::from(b.value), + // `Number(100)` -> `100` + Argument::NumericLiteral(n) => n.value, + // `Number("a")` -> `+"a"` -> `NaN` + // `Number("1")` -> `+"1"` -> `1` + Argument::StringLiteral(n) => { + let argument = + ctx.ast.expression_string_literal(n.span, n.value.clone(), n.raw.clone()); + if let Some(n) = ctx.eval_to_number(&argument) { + n + } else { + return Some(ctx.ast.expression_unary( + e.span, + UnaryOperator::UnaryPlus, + argument, + )); + } + } + _ => return None, + }), + )) + } } /// @@ -1676,4 +1722,15 @@ mod test { } } } + + #[test] + fn test_number_constructor() { + test("Number(undefined)", "NaN"); + test("Number(null)", "0"); + test("Number(true)", "1"); + test("Number(false)", "0"); + test("Number('a')", "NaN"); + test("Number('1')", "1"); + test_same("var Number; Number(1)"); + } }