perf(minifier): reduce code branches when evaluating roots (#27906)

- split `sqrt` and `cbrt` to separate functions
- add missing regression tests for `cbrt`
This commit is contained in:
Armano authored and GitHub committed 2026-10-17 17:40:11 -07:00
1 parent 63e70cf804
commit 88b5c3580b
2 files changed
+27 -14

No files matched your search

@@ -89,7 +89,8 @@ pub fn try_fold_known_global_methods<'a>(
"isFinite" | "isNaN" | "isInteger" | "isSafeInteger" => {
try_fold_number_methods(arguments, object, name, ctx)
}
"sqrt" | "cbrt" => try_fold_roots(arguments, name, object, ctx),
"sqrt" => try_fold_sqrt(arguments, object, ctx),
"cbrt" => try_fold_cbrt(arguments, object, ctx),
"abs" | "ceil" | "floor" | "round" | "fround" | "trunc" | "sign" | "clz32" => {
try_fold_math_unary(arguments, name, object, ctx)
}
@@ -431,9 +432,9 @@ fn try_fold_number_methods<'a>(
result.map(ConstantValue::Boolean)
}
fn try_fold_roots<'a>(
/// <https://tc39.es/ecma262/#sec-math.sqrt>
fn try_fold_sqrt<'a>(
args: &ArenaVec<'a, Argument<'a>>,
name: &str,
object: &Expression<'a>,
ctx: &impl ConstantEvaluationCtx<'a>,
) -> Option<ConstantValue<'a>> {
@@ -444,20 +445,27 @@ fn try_fold_roots<'a>(
if arg_val == f64::INFINITY || arg_val.is_nan() || arg_val == 0.0 {
return Some(ConstantValue::Number(arg_val));
}
// `Math.sqrt` returns NaN for negative numbers, but `Math.cbrt` does not:
// <https://tc39.es/ecma262/#sec-math.sqrt>, <https://tc39.es/ecma262/#sec-math.cbrt>
if arg_val < 0.0 && name == "sqrt" {
if arg_val < 0.0 {
return Some(ConstantValue::Number(f64::NAN));
}
if arg_val == f64::NEG_INFINITY {
// `Math.cbrt(-Infinity)` is `-Infinity`
let calculated_val = arg_val.sqrt();
(calculated_val.fract() == 0.0).then_some(ConstantValue::Number(calculated_val))
}
/// <https://tc39.es/ecma262/#sec-math.cbrt>
fn try_fold_cbrt<'a>(
args: &ArenaVec<'a, Argument<'a>>,
object: &Expression<'a>,
ctx: &impl ConstantEvaluationCtx<'a>,
) -> Option<ConstantValue<'a>> {
if !ctx.is_global_expr("Math", object) || !validate_arguments(args, 1) {
return None;
}
let arg_val = args[0].to_expression().get_side_free_number_value(ctx)?;
if arg_val.is_infinite() || arg_val.is_nan() || arg_val == 0.0 {
return Some(ConstantValue::Number(arg_val));
}
let calculated_val = match name {
"sqrt" => arg_val.sqrt(),
"cbrt" => arg_val.cbrt(),
_ => unreachable!(),
};
let calculated_val = arg_val.cbrt();
(calculated_val.fract() == 0.0).then_some(ConstantValue::Number(calculated_val))
}
@@ -640,7 +640,7 @@ fn test_fold_math_functions_pow() {
#[test]
fn test_fold_math_functions_sqrt() {
test_same_value("Math.sqrt()");
test_same_value("Math.sqrt()"); // NaN
test_same_value("Math.sqrt(1, 2)");
test_same_value("Math.sqrt(...a)");
test_same_value("Math.sqrt(a)"); // a maybe -0
@@ -659,6 +659,11 @@ fn test_fold_math_functions_sqrt() {
#[test]
fn test_fold_math_functions_cbrt() {
test_same_value("Math.cbrt()"); // NaN
test_same_value("Math.cbrt(1, 2)");
test_same_value("Math.cbrt(...a)");
test_same_value("Math.cbrt(a)");
test_value("Math.cbrt(0)", "0");
test_value("Math.cbrt(1)", "1");
test_value("Math.cbrt(8)", "2");
test_same_value("Math.cbrt(2)");