feat: 支持 Number 对象及成员方法并修复属性溢出崩溃问题

This commit is contained in:
爱特每羊队
2026-06-07 10:46:12 +08:00
parent ee6fcad370
commit 7667d9cfb0
5 changed files with 405 additions and 9 deletions
+1
View File
@@ -1,5 +1,6 @@
pub mod array;
pub mod error;
pub mod global;
pub mod number;
pub mod object;
pub mod string;
+169
View File
@@ -0,0 +1,169 @@
use oxide_types::value::JsValue;
use crate::coercion;
use crate::native::NativeResult;
use crate::vm::Vm;
pub fn number_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref())
} else {
0.0
};
if n.fract() == 0.0 && n.is_finite() && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
Ok(JsValue::int(n as i32))
} else {
Ok(JsValue::float(n))
}
}
pub fn number_is_nan(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return Ok(JsValue::bool(false));
}
let val = vm.reg(args[1]);
if val.is_double() {
Ok(JsValue::bool(val.as_double().is_nan()))
} else {
Ok(JsValue::bool(false))
}
}
pub fn number_is_finite(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return Ok(JsValue::bool(false));
}
let val = vm.reg(args[1]);
if val.is_int() {
return Ok(JsValue::bool(true));
}
if val.is_double() {
return Ok(JsValue::bool(val.as_double().is_finite()));
}
let n = coercion::to_number(val, vm.kernel().string_forge().as_ref());
Ok(JsValue::bool(n.is_finite()))
}
pub fn number_parse_int(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return Ok(JsValue::float(f64::NAN));
}
let sf = vm.kernel().string_forge().as_ref();
let s = coercion::to_string(sf, vm.reg(args[1]));
let s = s.trim();
if s.is_empty() {
return Ok(JsValue::float(f64::NAN));
}
let radix = if args.len() > 2 {
let r = coercion::to_number(vm.reg(args[2]), sf) as i32;
if r == 0 {
10
} else {
r.clamp(2, 36)
}
} else {
10
};
let (rest, hex) = if s.starts_with("0x") || s.starts_with("0X") {
if radix == 16 || radix == 0 || (args.len() <= 2) {
(s[2..].to_string(), true)
} else {
(s.to_string(), false)
}
} else {
(s.to_string(), false)
};
let actual_radix = if hex { 16u32 } else { radix as u32 };
if let Ok(n) = i32::from_str_radix(&rest, actual_radix) {
return Ok(JsValue::int(n));
}
Ok(JsValue::float(f64::NAN))
}
pub fn number_parse_float(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return Ok(JsValue::float(f64::NAN));
}
let sf = vm.kernel().string_forge().as_ref();
let s = coercion::to_string(sf, vm.reg(args[1]));
let s = s.trim();
if s.is_empty() {
return Ok(JsValue::float(f64::NAN));
}
match fast_float::parse::<f64, _>(&s) {
Ok(v) => Ok(JsValue::float(v)),
Err(_) => Ok(JsValue::float(f64::NAN)),
}
}
pub fn number_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]), vm.kernel().string_forge().as_ref());
let radix = if args.len() > 1 {
let r = coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as u32;
r.clamp(2, 36)
} else {
10u32
};
if radix == 10 {
if n.is_nan() {
return Ok(vm.intern("NaN"));
}
if n.is_infinite() {
if n.is_sign_positive() {
return Ok(vm.intern("Infinity"));
}
return Ok(vm.intern("-Infinity"));
}
if n.fract() == 0.0 && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
return Ok(vm.intern(&(n as i64).to_string()));
}
let mut buf = ryu::Buffer::new();
Ok(vm.intern(buf.format(n)))
} else {
if n.is_nan() {
return Ok(vm.intern("NaN"));
}
let nn = n as i64;
let mut result = String::new();
let mut value = nn.abs();
if value == 0 {
result.push('0');
} else {
let chars = "0123456789abcdefghijklmnopqrstuvwxyz";
let mut digits = Vec::new();
while value > 0 {
digits.push(chars.as_bytes()[(value % radix as i64) as usize] as char);
value /= radix as i64;
}
for ch in digits.iter().rev() {
result.push(*ch);
}
}
if nn < 0 {
result.insert(0, '-');
}
Ok(vm.intern(&result))
}
}
pub fn number_to_fixed(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]), vm.kernel().string_forge().as_ref());
let digits = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize
} else {
0usize
}
.min(20);
let formatted = format!("{:.digits$}", n);
Ok(vm.intern(&formatted))
}
+71 -9
View File
@@ -8,7 +8,9 @@ use oxide_compiler::opcode::{self, OpCode};
use crate::coercion;
use crate::native::NativeFn;
use oxide_kernel::builtin::{ArrayMethods, ErrorMethods, ObjectMethods, StringMethods};
use oxide_kernel::builtin::{
ArrayMethods, ErrorMethods, NumberMethods, ObjectMethods, StringMethods,
};
use oxide_kernel::kernel::{KernelConfig, OxideKernel};
use oxide_kernel::prop_forge::PropTemplate;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
@@ -65,8 +67,7 @@ pub fn init_kernel_builtins(kernel: &Arc<OxideKernel>) {
let cur_count = global.prop_count();
global.set_shape_id(obj_shape);
global.set_prop_count(cur_count + 1);
let bump = bumpalo::Bump::new();
global.set_prop_expand(cur_count, obj_val, &bump);
global.set_prop_expand_heap(cur_count, obj_val);
let _array_methods = ArrayMethods {
push: crate::builtins::array::array_push as *const (),
@@ -102,8 +103,7 @@ pub fn init_kernel_builtins(kernel: &Arc<OxideKernel>) {
let cur_count = global.prop_count();
global.set_shape_id(arr_shape);
global.set_prop_count(cur_count + 1);
let bump2 = bumpalo::Bump::new();
global.set_prop_expand(cur_count, arr_val, &bump2);
global.set_prop_expand_heap(cur_count, arr_val);
global.bump_generation();
let error_methods = ErrorMethods {
@@ -128,8 +128,7 @@ pub fn init_kernel_builtins(kernel: &Arc<OxideKernel>) {
let cur_count = global.prop_count();
global.set_shape_id(err_shape);
global.set_prop_count(cur_count + 1);
let bump3 = bumpalo::Bump::new();
global.set_prop_expand(cur_count, err_val, &bump3);
global.set_prop_expand_heap(cur_count, err_val);
global.bump_generation();
let string_methods = StringMethods {
@@ -167,9 +166,61 @@ pub fn init_kernel_builtins(kernel: &Arc<OxideKernel>) {
let cur_count = global.prop_count();
global.set_shape_id(str_shape);
global.set_prop_count(cur_count + 1);
let bump4 = bumpalo::Bump::new();
global.set_prop_expand(cur_count, str_val, &bump4);
global.set_prop_expand_heap(cur_count, str_val);
global.bump_generation();
let number_methods = NumberMethods {
is_nan: crate::builtins::number::number_is_nan as *const (),
is_finite: crate::builtins::number::number_is_finite as *const (),
parse_int: crate::builtins::number::number_parse_int as *const (),
parse_float: crate::builtins::number::number_parse_float as *const (),
to_string: crate::builtins::number::number_to_string as *const (),
to_fixed: crate::builtins::number::number_to_fixed as *const (),
};
kernel.builtin_world().bind_number_methods(
&number_methods,
kernel.string_forge().as_ref(),
kernel.shape_forge().as_ref(),
);
let si_num = kernel.string_forge().intern("Number").0;
let num_shape = kernel.shape_forge().make_shape(global.shape_id(), si_num);
let num_val = JsValue::from_js_object(
kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject
);
let cur_count = global.prop_count();
global.set_shape_id(num_shape);
global.set_prop_count(cur_count + 1);
global.set_prop_expand_heap(cur_count, num_val);
global.bump_generation();
{
let num_ctor_ptr = kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject;
let num_ctor = unsafe { &mut *num_ctor_ptr };
num_ctor.set_native_fn(Some(
crate::builtins::number::number_constructor as *const (),
));
num_ctor.set_native_arg_count(1);
}
let pi_fn = crate::builtins::number::number_parse_int as *const ();
let pf_fn = crate::builtins::number::number_parse_float as *const ();
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
global,
kernel.shape_forge().as_ref(),
kernel.string_forge().as_ref(),
"parseInt",
pi_fn,
1,
);
let _ = oxide_kernel::builtin::BuiltinWorld::bind_method(
global,
kernel.shape_forge().as_ref(),
kernel.string_forge().as_ref(),
"parseFloat",
pf_fn,
1,
);
}
impl Vm {
@@ -605,6 +656,17 @@ impl Vm {
continue;
}
}
if val.is_int() || val.is_double() {
let proto_ptr =
self.kernel.builtin_world().number_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &*proto_ptr };
let prop_name_si = self.regs[b].as_string_index();
if let Some(resolved) = self.resolve_property(proto, prop_name_si) {
self.regs[a] = resolved;
self.pc += 1;
continue;
}
}
return Err("IC_GET_PROP on non-object".into());
}
let obj = unsafe { &*obj_ptr };