diff --git a/crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs b/crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs index dbb5976e6cdd4..7a9f94d89d03d 100644 --- a/crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs +++ b/crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs @@ -16,7 +16,7 @@ use cow_utils::CowUtils; use crate::{ StringCharAt, StringCharAtResult, StringCharCodeAt, StringIndexOf, StringLastIndexOf, - StringSubstring, ToInt32, ToJsString as ToJsStringTrait, + StringSubstring, ToInt32, ToJsString as ToJsStringTrait, ToUint32, constant_evaluation::url_encoding::{ decode_uri_chars, encode_uri_chars, is_uri_always_unescaped, }, @@ -89,10 +89,10 @@ pub fn try_fold_known_global_methods<'a>( try_fold_number_methods(arguments, object, name, ctx) } "sqrt" | "cbrt" => try_fold_roots(arguments, name, object, ctx), - "abs" | "ceil" | "floor" | "round" | "fround" | "trunc" | "sign" => { + "abs" | "ceil" | "floor" | "round" | "fround" | "trunc" | "sign" | "clz32" => { try_fold_math_unary(arguments, name, object, ctx) } - "min" | "max" => try_fold_math_variadic(arguments, name, object, ctx), + "imul" | "min" | "max" => try_fold_math_variadic(arguments, name, object, ctx), _ => None, } } @@ -478,6 +478,7 @@ fn try_fold_math_unary<'a>( "sign" if arg_val.to_bits() == 0f64.to_bits() => 0f64, "sign" if arg_val.to_bits() == (-0f64).to_bits() => -0f64, "sign" => arg_val.signum(), + "clz32" => f64::from(arg_val.to_uint_32().leading_zeros()), _ => unreachable!(), }; // These results are always shorter to return as a number, so we can just return them as NumericLiteral. @@ -499,28 +500,38 @@ fn try_fold_math_variadic<'a>( let value = expr.get_side_free_number_value(ctx)?; numbers.push(value); } - let result = if numbers.iter().any(|n: &f64| n.is_nan()) { - f64::NAN - } else { - match name { - // TODO - // see , we can't use `min` and `max` here due to inconsistency - "min" => numbers.iter().copied().fold(f64::INFINITY, |a, b| { - if a < b || ((a == 0f64) && (b == 0f64) && (a.to_bits() > b.to_bits())) { - a - } else { - b - } - }), - "max" => numbers.iter().copied().fold(f64::NEG_INFINITY, |a, b| { - if a > b || ((a == 0f64) && (b == 0f64) && (a.to_bits() < b.to_bits())) { - a - } else { - b + let result = match name { + "min" | "max" => { + if numbers.iter().any(|n: &f64| n.is_nan()) { + f64::NAN + } else { + match name { + // TODO + // see , we can't use `min` and `max` here due to inconsistency + "min" => numbers.iter().copied().fold(f64::INFINITY, |a, b| { + if a < b || ((a == 0f64) && (b == 0f64) && (a.to_bits() > b.to_bits())) { + a + } else { + b + } + }), + "max" => numbers.iter().copied().fold(f64::NEG_INFINITY, |a, b| { + if a > b || ((a == 0f64) && (b == 0f64) && (a.to_bits() < b.to_bits())) { + a + } else { + b + } + }), + _ => return None, } - }), - _ => return None, + } + } + "imul" => { + let a = numbers.first().copied().unwrap_or(f64::NAN).to_uint_32(); + let b = numbers.get(1).copied().unwrap_or(f64::NAN).to_uint_32(); + f64::from(a.wrapping_mul(b).cast_signed()) } + _ => return None, }; Some(ConstantValue::Number(result)) } diff --git a/crates/oxc_minifier/src/peephole/replace_known_methods.rs b/crates/oxc_minifier/src/peephole/replace_known_methods.rs index c6e7d74d19272..827cc07969f30 100644 --- a/crates/oxc_minifier/src/peephole/replace_known_methods.rs +++ b/crates/oxc_minifier/src/peephole/replace_known_methods.rs @@ -1012,9 +1012,9 @@ mod test { } #[test] - #[ignore] fn test_fold_math_functions_imul() { test_same_value("Math.imul(Math.random(),2)"); + test_value("Math.imul()", "0"); test_value("Math.imul(-1,1)", "-1"); test_value("Math.imul(2,2)", "4"); test_value("Math.imul(2)", "0"); @@ -1096,27 +1096,33 @@ mod test { } #[test] - #[ignore] fn test_fold_math_functions_clz32() { - test("Math.clz32(0)", "32"); - let mut x = 1; + test_value("Math.clz32(0)", "32"); + test_value("Math.clz32(0.0)", "32"); + test_value("Math.clz32(-0.0)", "32"); + let mut x = 1_i64; for i in (0..=31).rev() { - test(&format!("{x}.leading_zeros()"), &i.to_string()); - test(&format!("{}.leading_zeros()", 2 * x - 1), &i.to_string()); + test_value(&format!("Math.clz32({x})"), &i.to_string()); + test_value(&format!("Math.clz32({})", 2 * x - 1), &i.to_string()); x *= 2; } - test("Math.clz32('52')", "26"); - test("Math.clz32([52])", "26"); - test("Math.clz32([52, 53])", "32"); + test_value("Math.clz32('52')", "26"); + test_value("Math.clz32([52])", "26"); + test_value("Math.clz32([52, 53])", "32"); // Overflow cases - test("Math.clz32(0x100000000)", "32"); - test("Math.clz32(0x100000001)", "31"); + test_value("Math.clz32(0x100000000)", "32"); + test_value("Math.clz32(0x100000001)", "31"); + + // Negative cases + test_value("Math.clz32(-1)", "0"); + test_value("Math.clz32(-2147483647)", "0"); + test_value("Math.clz32(-2147483649)", "1"); // NaN -> 0 - test("Math.clz32(NaN)", "32"); - test("Math.clz32('foo')", "32"); - test("Math.clz32(Infinity)", "32"); + test_value("Math.clz32(NaN)", "32"); + test_value("Math.clz32('foo')", "32"); + test_value("Math.clz32(Infinity)", "32"); } #[test]