From ca649e059c5d0f3cac4d1e0ef9ca72abc5794d15 Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 14 Sep 2026 03:15:54 +0200 Subject: [PATCH] feat(ecma): define math constants as known globals and resolve their types (#26585) `Number.*` constants will now be treated as side effect free `typeof Math.*` and `typeof Number.*` will now resolve to `number` ref: https://github.com/rolldown/rolldown/issues/10852 --- .../src/side_effects/known_globals.rs | 3 +++ crates/oxc_ecmascript/src/value_type.rs | 22 ++++++++++++++----- .../tests/ecmascript/may_have_side_effects.rs | 17 ++++++++++++++ .../tests/peephole/fold_constants.rs | 22 +++++++++++++++++++ .../tests/peephole/obscure_edge_cases.rs | 4 ++-- 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/crates/oxc_ecmascript/src/side_effects/known_globals.rs b/crates/oxc_ecmascript/src/side_effects/known_globals.rs index a41e7a3b42..82999dbd01 100644 --- a/crates/oxc_ecmascript/src/side_effects/known_globals.rs +++ b/crates/oxc_ecmascript/src/side_effects/known_globals.rs @@ -455,6 +455,9 @@ pub(super) fn is_known_global_property(global: &str, property: &str) -> bool { match global { "Math" => matches!(property, "E" | "LN10" | "LN2" | "LOG10E" | "LOG2E" | "PI" | "SQRT1_2" | "SQRT2") || is_pure_math_method(property), + "Number" => matches!(property, + "POSITIVE_INFINITY" | "NEGATIVE_INFINITY" | "EPSILON" | "NaN" + | "MAX_VALUE" | "MIN_VALUE" | "MAX_SAFE_INTEGER" | "MIN_SAFE_INTEGER"), "console" => matches!(property, "assert" | "clear" | "count" | "countReset" | "debug" | "dir" | "dirxml" | "error" | "group" | "groupCollapsed" | "groupEnd" | "info" | "log" diff --git a/crates/oxc_ecmascript/src/value_type.rs b/crates/oxc_ecmascript/src/value_type.rs index aaa469b48f..25c0afc776 100644 --- a/crates/oxc_ecmascript/src/value_type.rs +++ b/crates/oxc_ecmascript/src/value_type.rs @@ -272,12 +272,24 @@ impl<'a> DetermineValueType<'a> for LogicalExpression<'a> { impl<'a> DetermineValueType<'a> for StaticMemberExpression<'a> { fn value_type(&self, ctx: &impl GlobalContext<'a>) -> ValueType { - if matches!(self.property.name.as_str(), "POSITIVE_INFINITY" | "NEGATIVE_INFINITY") - && ctx.is_global_expr("Number", &self.object) - { - return ValueType::Number; + match self.property.name.as_str() { + "POSITIVE_INFINITY" | "NEGATIVE_INFINITY" | "EPSILON" | "NaN" | "MAX_VALUE" + | "MIN_VALUE" => { + if ctx.is_global_expr("Number", &self.object) { + ValueType::Number + } else { + ValueType::Undetermined + } + } + "E" | "LN10" | "LN2" | "LOG10E" | "LOG2E" | "PI" | "SQRT1_2" | "SQRT2" => { + if ctx.is_global_expr("Math", &self.object) { + ValueType::Number + } else { + ValueType::Undetermined + } + } + _ => ValueType::Undetermined, } - ValueType::Undetermined } } diff --git a/crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs b/crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs index 365ee69507..33a3e038ab 100644 --- a/crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs +++ b/crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs @@ -915,11 +915,28 @@ fn test_known_global_property_reads() { // Math properties test("Math.PI", false); test("Math.E", false); + test("Math.LN10", false); + test("Math.LN2", false); + test("Math.LOG10E", false); + test("Math.LOG2E", false); + test("Math.SQRT1_2", false); + test("Math.SQRT2", false); test("Math.abs", false); test("Math.floor", false); test("Math.random", false); test("Math.unknownProp", true); + // Number properties + test("Number.POSITIVE_INFINITY", false); + test("Number.NEGATIVE_INFINITY", false); + test("Number.EPSILON", false); + test("Number.NaN", false); + test("Number.MAX_VALUE", false); + test("Number.MIN_VALUE", false); + test("Number.MAX_SAFE_INTEGER", false); + test("Number.MIN_SAFE_INTEGER", false); + test("Number.UNKNOWN", true); + // Object properties test("Object.keys", false); test("Object.create", false); diff --git a/crates/oxc_minifier/tests/peephole/fold_constants.rs b/crates/oxc_minifier/tests/peephole/fold_constants.rs index 9f9c13a9de..bc5685e488 100644 --- a/crates/oxc_minifier/tests/peephole/fold_constants.rs +++ b/crates/oxc_minifier/tests/peephole/fold_constants.rs @@ -448,6 +448,28 @@ fn js_typeof() { fold_same("x = typeof[1,[foo()]]"); fold_same("x = typeof{bathwater:baby()}"); fold_same("x = typeof class { static { foo() } }"); + + fold("typeof NaN", "'number'"); + fold("typeof Infinity", "'number'"); + fold("typeof Math.E", "'number'"); + fold("typeof Math.LN10", "'number'"); + fold("typeof Math.LN2", "'number'"); + fold("typeof Math.LOG10E", "'number'"); + fold("typeof Math.LOG2E", "'number'"); + fold("typeof Math.PI", "'number'"); + fold("typeof Math.SQRT1_2", "'number'"); + fold("typeof Math.SQRT2", "'number'"); + fold_same("typeof Math.missing"); + + fold("typeof Number.POSITIVE_INFINITY", "'number'"); + fold("typeof Number.NEGATIVE_INFINITY", "'number'"); + fold("typeof Number.EPSILON", "'number'"); + fold("typeof Number.NaN", "'number'"); + fold("typeof Number.MAX_VALUE", "'number'"); + fold("typeof Number.MIN_VALUE", "'number'"); + fold("typeof Number.MAX_SAFE_INTEGER", "'number'"); + fold("typeof Number.MIN_SAFE_INTEGER", "'number'"); + fold_same("typeof Number.UNKNOWN"); } #[test] diff --git a/crates/oxc_minifier/tests/peephole/obscure_edge_cases.rs b/crates/oxc_minifier/tests/peephole/obscure_edge_cases.rs index 14688780df..6515f70adc 100644 --- a/crates/oxc_minifier/tests/peephole/obscure_edge_cases.rs +++ b/crates/oxc_minifier/tests/peephole/obscure_edge_cases.rs @@ -130,8 +130,8 @@ fn test_mathematical_expression_edge_cases() { test("Infinity + 1", ""); // eliminated as unused expression test("Infinity - Infinity", ""); // eliminated as unused expression test("Infinity / Infinity", ""); // eliminated as unused expression - test_same("Math.PI * 2"); // runtime value - test_same("Math.E + 1"); // runtime value + test("Math.PI * 2", ""); // eliminated as unused expression + test("Math.E + 1", ""); // eliminated as unused expression test("-0 + 0", ""); // eliminated as unused expression test("-0 * 1", ""); // eliminated as unused expression test("1 / -0", ""); // eliminated as unused expression