use oxide_types::value::JsValue; use oxide_runtime_api::{NativeResult, VmHost}; fn num(vm: &mut H, reg: u8) -> f64 { vm.coerce_number_bounded(vm.reg(reg)).unwrap_or(f64::NAN) } fn arg1(vm: &mut H, args: &[u8]) -> f64 { if args.len() < 2 { f64::NAN } else { num(vm, args[1]) } } fn arg2(vm: &mut H, args: &[u8]) -> (f64, f64) { // 顺序求值(非元组字面量):两次 coercion 各自单独借用 &mut H。 let a = if args.len() > 1 { num(vm, args[1]) } else { f64::NAN }; let b = if args.len() > 2 { num(vm, args[2]) } else { f64::NAN }; (a, b) } /// `Math.abs`:返回参数的绝对值。int 参数走 int 运算,其余按 double 返回。 pub fn math_abs(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); } let x = vm.reg(args[1]); if x.is_int() { NativeResult::Ok(JsValue::int(x.as_int().abs())) } else { NativeResult::Ok(JsValue::float(num(vm, args[1]).abs())) } } macro_rules! math_unary { ($name:ident, $op:ident) => { /// 一元数学函数(`Math.acos` 等):对首个参数做 Rust 同名浮点运算, /// 返回 double 结果;缺参或不可转数字时按 NaN 处理。 pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(arg1(vm, args).$op())) } }; } math_unary!(math_acos, acos); math_unary!(math_acosh, acosh); math_unary!(math_asin, asin); math_unary!(math_asinh, asinh); math_unary!(math_atan, atan); math_unary!(math_atanh, atanh); math_unary!(math_cbrt, cbrt); math_unary!(math_ceil, ceil); math_unary!(math_cos, cos); math_unary!(math_cosh, cosh); math_unary!(math_exp, exp); math_unary!(math_expm1, exp_m1); math_unary!(math_floor, floor); math_unary!(math_log, ln); math_unary!(math_log10, log10); math_unary!(math_log1p, ln_1p); math_unary!(math_log2, log2); math_unary!(math_sin, sin); math_unary!(math_sinh, sinh); math_unary!(math_sqrt, sqrt); math_unary!(math_tan, tan); math_unary!(math_tanh, tanh); math_unary!(math_trunc, trunc); /// `Math.atan2(y, x)`:返回 y/x 的反正切角(弧度)。 pub fn math_atan2(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.atan2(b))) } /// `Math.round`:四舍五入到最接近的整数,.5 时向正无穷取整(JS 语义)。 pub fn math_round(vm: &mut H, args: &[u8]) -> NativeResult { let x = arg1(vm, args); let r = if x < 0.0 { (x - 0.5).ceil() } else { (x + 0.5).floor() }; NativeResult::Ok(JsValue::float(r)) } /// `Math.sign`:返回 1 / -1 / 0 / -0 / NaN。 pub fn math_sign(vm: &mut H, args: &[u8]) -> NativeResult { let x = arg1(vm, args); if x.is_nan() { NativeResult::Ok(JsValue::float(f64::NAN)) } else if x > 0.0 { NativeResult::Ok(JsValue::int(1)) } else if x < 0.0 { NativeResult::Ok(JsValue::int(-1)) } else { NativeResult::Ok(JsValue::float(x)) } } /// `Math.clz32`:返回 32 位无符号整数表示的前导零个数。 pub fn math_clz32(vm: &mut H, args: &[u8]) -> NativeResult { let n = arg1(vm, args) as u32; NativeResult::Ok(JsValue::int(n.leading_zeros() as i32)) } /// `Math.fround`:把 double 舍入到 float32 精度再还原为 double。 pub fn math_fround(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(arg1(vm, args) as f32 as f64)) } /// `Math.hypot`:返回 sqrt(a²+b²)(当前仅支持两个参数)。 pub fn math_hypot(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.hypot(b))) } /// `Math.imul`:按 32 位整数做 wrap-around 乘法。 pub fn math_imul(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::int((a as i32).wrapping_mul(b as i32))) } /// `Math.pow(base, exp)`:返回幂运算结果。 pub fn math_pow(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.powf(b))) } /// `Math.max`:返回参数中的最大值;任一无参时返回 -Infinity,任一 NaN 时返回 NaN。 pub fn math_max(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NEG_INFINITY)); } let mut m = f64::NEG_INFINITY; for &r in args.iter().skip(1) { let x = num(vm, r); if x.is_nan() { return NativeResult::Ok(JsValue::float(f64::NAN)); } if x > m { m = x; } } NativeResult::Ok(JsValue::float(m)) } /// `Math.min`:返回参数中的最小值;无参时返回 Infinity,任一 NaN 时返回 NaN。 pub fn math_min(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::INFINITY)); } let mut m = f64::INFINITY; for &r in args.iter().skip(1) { let x = num(vm, r); if x.is_nan() { return NativeResult::Ok(JsValue::float(f64::NAN)); } if x < m { m = x; } } NativeResult::Ok(JsValue::float(m)) } /// `Math.random`:推进 RNG 并返回 [0, 1) 的伪随机浮点数。 pub fn math_random(vm: &mut H, _args: &[u8]) -> NativeResult { vm.step_rng(); NativeResult::Ok(JsValue::float(vm.math_rng_value())) }