feat: 支持 Math 对象及核心成员方法

This commit is contained in:
爱特每羊队
2026-06-07 11:07:02 +08:00
parent 7667d9cfb0
commit e2d5893205
5 changed files with 559 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
use oxide_types::value::JsValue;
use crate::coercion;
use crate::native::NativeResult;
use crate::vm::Vm;
fn num(vm: &Vm, reg: u8) -> f64 {
coercion::to_number(vm.reg(reg), vm.kernel().string_forge().as_ref())
}
fn arg1(vm: &Vm, args: &[u8]) -> f64 {
if args.len() < 2 {
f64::NAN
} else {
num(vm, args[1])
}
}
fn arg2(vm: &Vm, args: &[u8]) -> (f64, f64) {
(
if args.len() > 1 {
num(vm, args[1])
} else {
f64::NAN
},
if args.len() > 2 {
num(vm, args[2])
} else {
f64::NAN
},
)
}
pub fn math_abs(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return Ok(JsValue::float(f64::NAN));
}
let x = vm.reg(args[1]);
if x.is_int() {
Ok(JsValue::int(x.as_int().abs()))
} else {
Ok(JsValue::float(num(vm, args[1]).abs()))
}
}
macro_rules! math_unary {
($name:ident, $op:ident) => {
pub fn $name(vm: &mut Vm, args: &[u8]) -> 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);
pub fn math_atan2(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (a, b) = arg2(vm, args);
Ok(JsValue::float(a.atan2(b)))
}
pub fn math_round(vm: &mut Vm, args: &[u8]) -> NativeResult {
let x = arg1(vm, args);
let r = if x < 0.0 {
(x - 0.5).ceil()
} else {
(x + 0.5).floor()
};
Ok(JsValue::float(r))
}
pub fn math_sign(vm: &mut Vm, args: &[u8]) -> NativeResult {
let x = arg1(vm, args);
if x.is_nan() {
Ok(JsValue::float(f64::NAN))
} else if x > 0.0 {
Ok(JsValue::int(1))
} else if x < 0.0 {
Ok(JsValue::int(-1))
} else {
Ok(JsValue::float(x))
}
}
pub fn math_clz32(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = arg1(vm, args) as u32;
Ok(JsValue::int(n.leading_zeros() as i32))
}
pub fn math_fround(vm: &mut Vm, args: &[u8]) -> NativeResult {
Ok(JsValue::float(arg1(vm, args) as f32 as f64))
}
pub fn math_hypot(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (a, b) = arg2(vm, args);
Ok(JsValue::float(a.hypot(b)))
}
pub fn math_imul(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (a, b) = arg2(vm, args);
Ok(JsValue::int((a as i32).wrapping_mul(b as i32)))
}
pub fn math_pow(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (a, b) = arg2(vm, args);
Ok(JsValue::float(a.powf(b)))
}
pub fn math_max(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return 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 Ok(JsValue::float(f64::NAN));
}
if x > m {
m = x;
}
}
Ok(JsValue::float(m))
}
pub fn math_min(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return 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 Ok(JsValue::float(f64::NAN));
}
if x < m {
m = x;
}
}
Ok(JsValue::float(m))
}
pub fn math_random(vm: &mut Vm, _args: &[u8]) -> NativeResult {
vm.step_rng();
Ok(JsValue::float(vm.math_rng_value()))
}
+1
View File
@@ -1,6 +1,7 @@
pub mod array;
pub mod error;
pub mod global;
pub mod math;
pub mod number;
pub mod object;
pub mod string;
+270
View File
@@ -40,6 +40,7 @@ pub struct Vm {
interned_strings: Vec<u32>,
pub epoch: Epoch,
pub object_prototype: P<JsObject>,
pub math_rng_state: u64,
}
pub fn init_kernel_builtins(kernel: &Arc<OxideKernel>) {
@@ -221,6 +222,256 @@ pub fn init_kernel_builtins(kernel: &Arc<OxideKernel>) {
pf_fn,
1,
);
let math_ptr = kernel.builtin_world().math_object.as_ptr() as *mut JsObject;
let math = unsafe { &mut *math_ptr };
let sf = kernel.string_forge().as_ref();
let sh = kernel.shape_forge().as_ref();
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"abs",
crate::builtins::math::math_abs as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"acos",
crate::builtins::math::math_acos as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"asin",
crate::builtins::math::math_asin as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"atan",
crate::builtins::math::math_atan as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"atan2",
crate::builtins::math::math_atan2 as *const (),
2,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"cbrt",
crate::builtins::math::math_cbrt as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"ceil",
crate::builtins::math::math_ceil as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"cos",
crate::builtins::math::math_cos as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"cosh",
crate::builtins::math::math_cosh as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"exp",
crate::builtins::math::math_exp as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"floor",
crate::builtins::math::math_floor as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"hypot",
crate::builtins::math::math_hypot as *const (),
2,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"imul",
crate::builtins::math::math_imul as *const (),
2,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"log",
crate::builtins::math::math_log as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"log10",
crate::builtins::math::math_log10 as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"log2",
crate::builtins::math::math_log2 as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"max",
crate::builtins::math::math_max as *const (),
2,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"min",
crate::builtins::math::math_min as *const (),
2,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"pow",
crate::builtins::math::math_pow as *const (),
2,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"random",
crate::builtins::math::math_random as *const (),
0,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"round",
crate::builtins::math::math_round as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"sign",
crate::builtins::math::math_sign as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"sin",
crate::builtins::math::math_sin as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"sinh",
crate::builtins::math::math_sinh as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"sqrt",
crate::builtins::math::math_sqrt as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"tan",
crate::builtins::math::math_tan as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"tanh",
crate::builtins::math::math_tanh as *const (),
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
math,
sh,
sf,
"trunc",
crate::builtins::math::math_trunc as *const (),
1,
);
let pi_val = JsValue::float(std::f64::consts::PI);
{
let si_pi = sf.intern("PI").0;
let sh_pi = sh.make_shape(math.shape_id(), si_pi);
let cur = math.prop_count();
math.set_shape_id(sh_pi);
math.set_prop_count(cur + 1);
math.set_prop_expand_heap(cur, pi_val);
}
let si_m = kernel.string_forge().intern("Math").0;
let m_shape = kernel.shape_forge().make_shape(global.shape_id(), si_m);
let m_val =
JsValue::from_js_object(kernel.builtin_world().math_object.as_ptr() as *mut JsObject);
let cur_count = global.prop_count();
global.set_shape_id(m_shape);
global.set_prop_count(cur_count + 1);
global.set_prop_expand_heap(cur_count, m_val);
global.bump_generation();
}
impl Vm {
@@ -238,6 +489,7 @@ impl Vm {
interned_strings: Vec::new(),
epoch: Epoch::new(),
object_prototype: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
math_rng_state: 0,
}
}
@@ -253,9 +505,27 @@ impl Vm {
interned_strings: Vec::new(),
epoch: Epoch::new(),
object_prototype: P::clone(&kernel.builtin_world().object_proto),
math_rng_state: 0,
}
}
pub fn step_rng(&mut self) {
if self.math_rng_state == 0 {
self.math_rng_state = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
}
self.math_rng_state = self
.math_rng_state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
}
pub fn math_rng_value(&self) -> f64 {
(self.math_rng_state >> 33) as f64 / (1u64 << 31) as f64
}
pub fn kernel(&self) -> &Arc<OxideKernel> {
&self.kernel
}