feat: 实现最小 BigInt 子集(字面量/算术/比较/构造器,Temporal 前置)
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::bindings::{apply_binding_table, bind_global_value, configure_native_constructor};
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
/// 把 BigInt 构造器与原型方法绑定到 global。
|
||||
///
|
||||
/// 构造器/原型对象由 `BuiltinWorld` 创建(含 name/prototype/constructor 互指),
|
||||
/// 这里只配置 native 实现并挂方法。`BigInt(value)` 当函数调用时返回 BigInt 值;
|
||||
/// `new BigInt()` 抛 TypeError(BigInt 不可 new)。
|
||||
pub fn bind_bigint(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
let world = session.builtin_world();
|
||||
let ctor_ptr = world.bigint_constructor.as_ptr() as *mut JsObject;
|
||||
let ctor = unsafe { &mut *ctor_ptr };
|
||||
let proto_ptr = world.bigint_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
|
||||
configure_native_constructor(ctor, oxide_builtins::bigint::bigint_constructor::<crate::vm::Vm> as *const (), 1);
|
||||
|
||||
apply_binding_table(
|
||||
world,
|
||||
proto,
|
||||
core,
|
||||
&[
|
||||
("toString", oxide_builtins::bigint::bigint_to_string::<crate::vm::Vm> as *const (), 0),
|
||||
("valueOf", oxide_builtins::bigint::bigint_value_of::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
bind_global_value(core, global, "BigInt", JsValue::from_js_object(ctor_ptr));
|
||||
}
|
||||
@@ -8,9 +8,8 @@ use oxide_types::value::JsValue;
|
||||
|
||||
use crate::bindings::{bind_global_value, configure_native_constructor};
|
||||
|
||||
const STUBS: [(&str, *const (), u8); 8] = [
|
||||
const STUBS: [(&str, *const (), u8); 7] = [
|
||||
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2),
|
||||
("BigInt", oxide_builtins::stubs::bigint_stub::<crate::vm::Vm> as *const (), 1),
|
||||
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
|
||||
("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0),
|
||||
("WeakRef", oxide_builtins::stubs::weakref_stub::<crate::vm::Vm> as *const (), 1),
|
||||
@@ -27,7 +26,7 @@ const STUBS: [(&str, *const (), u8); 8] = [
|
||||
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
|
||||
];
|
||||
|
||||
/// 把未实现内置(Proxy/BigInt/WeakMap/WeakSet/WeakRef/FinalizationRegistry/SharedArrayBuffer/Atomics)
|
||||
/// 把未实现内置(Proxy/WeakMap/WeakSet/WeakRef/FinalizationRegistry/SharedArrayBuffer/Atomics)
|
||||
/// 的 stub 构造器绑定到 global,并登记到 `stub_objects` 供快照跟踪。
|
||||
pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) {
|
||||
let builtin_world = Arc::get_mut(&mut session.builtin_world)
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
pub mod bind_array;
|
||||
/// ArrayBuffer 构造器与原型的 native 方法绑定。
|
||||
pub mod bind_array_buffer;
|
||||
/// BigInt 构造器与原型的 native 方法绑定。
|
||||
pub mod bind_bigint;
|
||||
/// Boolean 构造器与原型的 native 方法绑定。
|
||||
pub mod bind_boolean;
|
||||
/// DataView 构造器与原型的 native 方法绑定。
|
||||
@@ -371,7 +373,6 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
|
||||
fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
for (index, (name, native_fn, arg_count)) in [
|
||||
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2),
|
||||
("BigInt", oxide_builtins::stubs::bigint_stub::<crate::vm::Vm> as *const (), 1),
|
||||
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
|
||||
("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0),
|
||||
("WeakRef", oxide_builtins::stubs::weakref_stub::<crate::vm::Vm> as *const (), 1),
|
||||
@@ -603,6 +604,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
|
||||
bind_reflect_global(core, session, global);
|
||||
bind_iterator_global(core, session, global);
|
||||
bind_stub_globals(core, session, global);
|
||||
bind_bigint::bind_bigint(core, session, global);
|
||||
bind_global_functions(core, session, global);
|
||||
let global_this = JsValue::from_js_object(global as *mut JsObject);
|
||||
bind_existing_global(core, global, "globalThis", global_this);
|
||||
@@ -674,6 +676,9 @@ pub fn rebind_dirty_builtins(core: &Arc<KernelCore>, session: &mut KernelSession
|
||||
if dirty.map_or(true, |d| d.stubs) {
|
||||
bind_stubs::bind_stubs(core, session, global);
|
||||
}
|
||||
if dirty.map_or(true, |d| d.stubs) {
|
||||
bind_bigint::bind_bigint(core, session, global);
|
||||
}
|
||||
}
|
||||
|
||||
/// 完整初始化一个 session 的内置对象(全量绑定 + `globalThis` + 快照记录)。
|
||||
|
||||
@@ -13,8 +13,25 @@ impl Vm {
|
||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 + rv.as_int() as f64);
|
||||
return Ok(());
|
||||
}
|
||||
if lv.is_bigint() && rv.is_bigint() {
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(lv) + self.bigint_value(rv));
|
||||
return Ok(());
|
||||
}
|
||||
// 注意:不能在此前置检查 BigInt 混合(`lv.is_bigint() || rv.is_bigint()`)——
|
||||
// 对象操作数(如 `{valueOf: () => 2n}`)需先 ToPrimitive 再判定,混合检查
|
||||
// 必须放在 coerce 之后。
|
||||
let lhs = self.coerce_primitive_bounded(lv, false)?;
|
||||
let rhs = self.coerce_primitive_bounded(rv, false)?;
|
||||
if lhs.is_bigint() && rhs.is_bigint() {
|
||||
// 包装对象 coerce 后暴露双 BigInt(如 Object(2n) + 2n)。
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(lhs) + self.bigint_value(rhs));
|
||||
return Ok(());
|
||||
}
|
||||
if lhs.is_bigint() != rhs.is_bigint() {
|
||||
// 包装对象 coerce 后暴露 BigInt:与另一非 BigInt 操作数混合加法必须抛
|
||||
// TypeError(规范不允许 BigInt 与 Number/String 相加)。
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
if lhs.is_string() || rhs.is_string() {
|
||||
let lbytes = if lhs.is_string() { unsafe { (*lhs.as_string_ptr()).len() } } else { 0 };
|
||||
let rbytes = if rhs.is_string() { unsafe { (*rhs.as_string_ptr()).len() } } else { 0 };
|
||||
@@ -38,7 +55,12 @@ impl Vm {
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_neg(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("NEG rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let v = self.coerce_number_bounded(self.regs[a])?;
|
||||
let v = self.regs[a];
|
||||
if v.is_bigint() {
|
||||
self.regs[rd] = self.new_bigint(-self.bigint_value(v));
|
||||
return Ok(());
|
||||
}
|
||||
let v = self.coerce_number_bounded(v)?;
|
||||
self.regs[rd] = JsValue::float(-v);
|
||||
Ok(())
|
||||
}
|
||||
@@ -46,7 +68,12 @@ impl Vm {
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_unary_plus(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||
vm_trace!("UNARY_PLUS rd={} r{}={:?}", rd, a, self.regs[a]);
|
||||
let v = self.coerce_number_bounded(self.regs[a])?;
|
||||
let v = self.regs[a];
|
||||
if v.is_bigint() {
|
||||
// 一元 + 对 BigInt 必须抛 TypeError(ToNumber(BigInt) 在隐式路径禁止)。
|
||||
return self.raise_type_error("Cannot convert a BigInt value to a number");
|
||||
}
|
||||
let v = self.coerce_number_bounded(v)?;
|
||||
self.regs[rd] = JsValue::float(v);
|
||||
Ok(())
|
||||
}
|
||||
@@ -59,8 +86,19 @@ impl Vm {
|
||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 + rv.as_int() as f64);
|
||||
return Ok(());
|
||||
}
|
||||
if lv.is_bigint() && rv.is_bigint() {
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(lv) + self.bigint_value(rv));
|
||||
return Ok(());
|
||||
}
|
||||
let lhs = self.coerce_primitive_bounded(lv, false)?;
|
||||
let rhs = self.coerce_primitive_bounded(rv, false)?;
|
||||
if lhs.is_bigint() && rhs.is_bigint() {
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(lhs) + self.bigint_value(rhs));
|
||||
return Ok(());
|
||||
}
|
||||
if lhs.is_bigint() != rhs.is_bigint() {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
if lhs.is_string() || rhs.is_string() {
|
||||
let lbytes = if lhs.is_string() { unsafe { (*lhs.as_string_ptr()).len() } } else { 0 };
|
||||
let rbytes = if rhs.is_string() { unsafe { (*rhs.as_string_ptr()).len() } } else { 0 };
|
||||
@@ -90,9 +128,22 @@ impl Vm {
|
||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 - rv.as_int() as f64);
|
||||
return Ok(());
|
||||
}
|
||||
let l = self.coerce_number_bounded(lv)?;
|
||||
let r = self.coerce_number_bounded(rv)?;
|
||||
self.regs[rd] = JsValue::float(l - r);
|
||||
if lv.is_bigint() && rv.is_bigint() {
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(lv) - self.bigint_value(rv));
|
||||
return Ok(());
|
||||
}
|
||||
let l = self.coerce_primitive_bounded(lv, false)?;
|
||||
let r = self.coerce_primitive_bounded(rv, false)?;
|
||||
if l.is_bigint() && r.is_bigint() {
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(l) - self.bigint_value(r));
|
||||
return Ok(());
|
||||
}
|
||||
if l.is_bigint() != r.is_bigint() {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
let ln = coercion::to_number(l);
|
||||
let rn = coercion::to_number(r);
|
||||
self.regs[rd] = JsValue::float(ln - rn);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -105,9 +156,22 @@ impl Vm {
|
||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 * rv.as_int() as f64);
|
||||
return Ok(());
|
||||
}
|
||||
let l = self.coerce_number_bounded(lv)?;
|
||||
let r = self.coerce_number_bounded(rv)?;
|
||||
self.regs[rd] = JsValue::float(l * r);
|
||||
if lv.is_bigint() && rv.is_bigint() {
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(lv) * self.bigint_value(rv));
|
||||
return Ok(());
|
||||
}
|
||||
let l = self.coerce_primitive_bounded(lv, false)?;
|
||||
let r = self.coerce_primitive_bounded(rv, false)?;
|
||||
if l.is_bigint() && r.is_bigint() {
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(l) * self.bigint_value(r));
|
||||
return Ok(());
|
||||
}
|
||||
if l.is_bigint() != r.is_bigint() {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
let ln = coercion::to_number(l);
|
||||
let rn = coercion::to_number(r);
|
||||
self.regs[rd] = JsValue::float(ln * rn);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -120,9 +184,30 @@ impl Vm {
|
||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 / rv.as_int() as f64);
|
||||
return Ok(());
|
||||
}
|
||||
let l = self.coerce_number_bounded(lv)?;
|
||||
let r = self.coerce_number_bounded(rv)?;
|
||||
self.regs[rd] = JsValue::float(l / r);
|
||||
if lv.is_bigint() && rv.is_bigint() {
|
||||
let r = self.bigint_value(rv);
|
||||
if r == 0 {
|
||||
return self.raise_error_kind("RangeError", "Division by zero");
|
||||
}
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(lv) / r);
|
||||
return Ok(());
|
||||
}
|
||||
let l = self.coerce_primitive_bounded(lv, false)?;
|
||||
let r = self.coerce_primitive_bounded(rv, false)?;
|
||||
if l.is_bigint() && r.is_bigint() {
|
||||
let rv = self.bigint_value(r);
|
||||
if rv == 0 {
|
||||
return self.raise_error_kind("RangeError", "Division by zero");
|
||||
}
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(l) / rv);
|
||||
return Ok(());
|
||||
}
|
||||
if l.is_bigint() != r.is_bigint() {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
let ln = coercion::to_number(l);
|
||||
let rn = coercion::to_number(r);
|
||||
self.regs[rd] = JsValue::float(ln / rn);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -135,9 +220,30 @@ impl Vm {
|
||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 % rv.as_int() as f64);
|
||||
return Ok(());
|
||||
}
|
||||
let l = self.coerce_number_bounded(lv)?;
|
||||
let r = self.coerce_number_bounded(rv)?;
|
||||
self.regs[rd] = JsValue::float(l % r);
|
||||
if lv.is_bigint() && rv.is_bigint() {
|
||||
let r = self.bigint_value(rv);
|
||||
if r == 0 {
|
||||
return self.raise_error_kind("RangeError", "Division by zero");
|
||||
}
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(lv) % r);
|
||||
return Ok(());
|
||||
}
|
||||
let l = self.coerce_primitive_bounded(lv, false)?;
|
||||
let r = self.coerce_primitive_bounded(rv, false)?;
|
||||
if l.is_bigint() && r.is_bigint() {
|
||||
let rv = self.bigint_value(r);
|
||||
if rv == 0 {
|
||||
return self.raise_error_kind("RangeError", "Division by zero");
|
||||
}
|
||||
self.regs[rd] = self.new_bigint(self.bigint_value(l) % rv);
|
||||
return Ok(());
|
||||
}
|
||||
if l.is_bigint() != r.is_bigint() {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
let ln = coercion::to_number(l);
|
||||
let rn = coercion::to_number(r);
|
||||
self.regs[rd] = JsValue::float(ln % rn);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ impl Vm {
|
||||
vm_trace!("LT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
|
||||
if (va.is_bigint() || vb.is_bigint()) && (va.is_symbol() || vb.is_symbol()) {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
match coercion::relational_compare(va, vb) {
|
||||
Some(r) => self.regs[rd] = JsValue::bool(r),
|
||||
None => {
|
||||
@@ -44,6 +47,9 @@ impl Vm {
|
||||
vm_trace!("GT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
|
||||
if (va.is_bigint() || vb.is_bigint()) && (va.is_symbol() || vb.is_symbol()) {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
match coercion::relational_compare(vb, va) {
|
||||
Some(r) => self.regs[rd] = JsValue::bool(r),
|
||||
None => {
|
||||
@@ -59,6 +65,9 @@ impl Vm {
|
||||
vm_trace!("LTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
|
||||
if (va.is_bigint() || vb.is_bigint()) && (va.is_symbol() || vb.is_symbol()) {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
match coercion::relational_compare(vb, va) {
|
||||
Some(r) => self.regs[rd] = JsValue::bool(!r),
|
||||
None => {
|
||||
@@ -74,6 +83,9 @@ impl Vm {
|
||||
vm_trace!("GTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
||||
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
|
||||
if (va.is_bigint() || vb.is_bigint()) && (va.is_symbol() || vb.is_symbol()) {
|
||||
return self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions");
|
||||
}
|
||||
match coercion::relational_compare(va, vb) {
|
||||
Some(r) => self.regs[rd] = JsValue::bool(!r),
|
||||
None => {
|
||||
|
||||
@@ -36,6 +36,8 @@ impl Vm {
|
||||
"string"
|
||||
} else if val.is_symbol() {
|
||||
"symbol"
|
||||
} else if val.is_bigint() {
|
||||
"bigint"
|
||||
} else if val.is_object() {
|
||||
let obj = unsafe { &*val.as_js_object_ptr() };
|
||||
if obj.is_function() {
|
||||
|
||||
@@ -65,6 +65,11 @@ impl Vm {
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
if val.is_bigint() {
|
||||
let proto_ptr = self.session.builtin_world().bigint_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &*proto_ptr };
|
||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,40 @@ macro_rules! binary_arith {
|
||||
let rv = $self.regs[$b];
|
||||
if lv.is_int() && rv.is_int() {
|
||||
$self.regs[$rd] = JsValue::float(lv.as_int() as f64 $op rv.as_int() as f64);
|
||||
} else if lv.is_bigint() && rv.is_bigint() {
|
||||
let l = $self.bigint_value(lv);
|
||||
let r = $self.bigint_value(rv);
|
||||
if r == 0 {
|
||||
// BigInt 除/模零:规范要求 RangeError(Number 路径走 f64 inf/NaN)。
|
||||
$self.raise_error_kind("RangeError", "Division by zero")?;
|
||||
$self.regs[$rd] = JsValue::undefined();
|
||||
} else {
|
||||
$self.regs[$rd] = $self.new_bigint(l $op r);
|
||||
}
|
||||
} else {
|
||||
let l = $self.coerce_number_bounded(lv)?;
|
||||
let r = $self.coerce_number_bounded(rv)?;
|
||||
$self.regs[$rd] = JsValue::float(l $op r);
|
||||
// 注意:混合 BigInt/Number 检查不能在此前置(对象操作数如
|
||||
// `{valueOf: () => 2n}` 需先 ToPrimitive 再判定),统一放 coerce 之后。
|
||||
let l = $self.coerce_primitive_bounded(lv, false)?;
|
||||
let r = $self.coerce_primitive_bounded(rv, false)?;
|
||||
if l.is_bigint() && r.is_bigint() {
|
||||
// 包装对象 coerce 后暴露双 BigInt(如 Object(2n) / 2n)。
|
||||
let lv = $self.bigint_value(l);
|
||||
let rv = $self.bigint_value(r);
|
||||
if rv == 0 {
|
||||
$self.raise_error_kind("RangeError", "Division by zero")?;
|
||||
$self.regs[$rd] = JsValue::undefined();
|
||||
} else {
|
||||
$self.regs[$rd] = $self.new_bigint(lv $op rv);
|
||||
}
|
||||
} else if l.is_bigint() != r.is_bigint() {
|
||||
// 包装对象 coerce 后暴露 BigInt 混合(如 Object(1n) - 1)。
|
||||
$self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions")?;
|
||||
$self.regs[$rd] = JsValue::undefined();
|
||||
} else {
|
||||
let ln = coercion::to_number(l);
|
||||
let rn = coercion::to_number(r);
|
||||
$self.regs[$rd] = JsValue::float(ln $op rn);
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
@@ -1745,6 +1775,12 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
fn new_string(&mut self, s: &str) -> JsValue {
|
||||
self.new_string(s)
|
||||
}
|
||||
fn new_bigint(&mut self, v: i128) -> JsValue {
|
||||
Vm::new_bigint(self, v)
|
||||
}
|
||||
fn bigint_value(&mut self, val: JsValue) -> i128 {
|
||||
Vm::bigint_value(self, val)
|
||||
}
|
||||
fn kernel_core(&self) -> &Arc<KernelCore> {
|
||||
self.kernel_core()
|
||||
}
|
||||
|
||||
@@ -8,14 +8,13 @@
|
||||
//!
|
||||
//! 本文件只定义字段;方法随 `Vm` 存放。
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rustc_hash::FxBuildHasher;
|
||||
|
||||
use oxide_types::object::{JsObject, JsString};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::session_gc::SessionGc;
|
||||
use crate::vm::ForInIter;
|
||||
|
||||
@@ -30,6 +29,10 @@ pub(crate) struct GcState {
|
||||
pub(crate) epoch_object_ptrs: Vec<*mut JsObject>,
|
||||
pub(crate) session_object_ptrs: Vec<*mut JsObject>,
|
||||
pub(crate) session_string_ptrs: Vec<*mut JsString>,
|
||||
/// BigInt 堆 box(`Box<i128>`)追踪表。`RefCell` 使 `&self` 的
|
||||
/// `convert_immutables` 也能登记新 box。与字符串不同,BigInt 不参与
|
||||
/// mark/sweep 回收(值量少),只在 full_reset 统一释放。
|
||||
pub(crate) session_bigint_ptrs: RefCell<Vec<*mut i128>>,
|
||||
pub(crate) session_bytes_allocated: usize,
|
||||
pub(crate) forwarding: HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ impl Vm {
|
||||
epoch_object_ptrs: Vec::new(),
|
||||
session_object_ptrs: Vec::new(),
|
||||
session_string_ptrs: Vec::new(),
|
||||
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_bytes_allocated: 0,
|
||||
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
||||
},
|
||||
@@ -171,6 +172,7 @@ impl Vm {
|
||||
epoch_object_ptrs: Vec::new(),
|
||||
session_object_ptrs: Vec::new(),
|
||||
session_string_ptrs: Vec::new(),
|
||||
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||
session_bytes_allocated: 0,
|
||||
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
||||
},
|
||||
@@ -275,6 +277,7 @@ impl Vm {
|
||||
self.gc_state.session_bytes_allocated = 0;
|
||||
self.gc_state.session_gc = crate::session_gc::SessionGc::new();
|
||||
self.free_session_string_heap_data();
|
||||
self.free_session_bigint_heap_data();
|
||||
self.symbols.reset();
|
||||
self.root_reg_limit = 0;
|
||||
self.active_reg_limit = 0;
|
||||
@@ -477,10 +480,39 @@ impl Vm {
|
||||
format!("{val}")
|
||||
}
|
||||
|
||||
/// 分配一个 BigInt 值:把 `i128` 堆分配为 box 并返回携带指针的 `JsValue`。
|
||||
///
|
||||
/// box 指针登记进 `gc_state.session_bigint_ptrs`,在 `full_reset` 统一释放。
|
||||
/// `&self` 使 `convert_immutables`(常量池 → JsValue)也能分配。
|
||||
pub fn new_bigint(&self, v: i128) -> JsValue {
|
||||
let ptr = Box::into_raw(Box::new(v));
|
||||
self.gc_state.session_bigint_ptrs.borrow_mut().push(ptr);
|
||||
JsValue::bigint(ptr)
|
||||
}
|
||||
|
||||
/// 读取 BigInt 值;调用方须保证 `val.is_bigint()`。
|
||||
pub fn bigint_value(&self, val: JsValue) -> i128 {
|
||||
// SAFETY: bigint 指针由 new_bigint 经 Box::into_raw 产生,存活至 full_reset。
|
||||
unsafe { *val.as_bigint_ptr() }
|
||||
}
|
||||
|
||||
/// 释放全部 session 堆 `i128` box。仅在完全隔离重置(`full_reset`)时调用,
|
||||
/// 此时没有存活的 session 对象/寄存器会引用它们。
|
||||
fn free_session_bigint_heap_data(&mut self) {
|
||||
for ptr in self.gc_state.session_bigint_ptrs.borrow_mut().drain(..) {
|
||||
// SAFETY: 每个指针来自 new_bigint 的 Box::into_raw(Box::new(i128)),
|
||||
// 且只在这里恰好释放一次。
|
||||
unsafe {
|
||||
drop(Box::from_raw(ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_constant(&self, constant: &Constant) -> JsValue {
|
||||
match constant {
|
||||
Constant::Number(v) => JsValue::float(*v),
|
||||
Constant::Int(v) => JsValue::int(*v),
|
||||
Constant::BigInt(v) => self.new_bigint(*v),
|
||||
Constant::String(s) => self.perm_string(s),
|
||||
Constant::Boolean(b) => JsValue::bool(*b),
|
||||
Constant::Null => JsValue::null(),
|
||||
@@ -752,4 +784,79 @@ mod tests {
|
||||
));
|
||||
assert!(!vm.session.is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bigint_literal_arithmetic_and_comparison() {
|
||||
let mut vm = Vm::new();
|
||||
assert_eq!(run_source(&mut vm, "100n + 23n"), run_source(&mut vm, "123n"));
|
||||
assert_eq!(run_source(&mut vm, "100n - 30n"), run_source(&mut vm, "70n"));
|
||||
assert_eq!(run_source(&mut vm, "7n * 6n"), run_source(&mut vm, "42n"));
|
||||
assert_eq!(run_source(&mut vm, "10n / 4n"), run_source(&mut vm, "2n"));
|
||||
assert_eq!(run_source(&mut vm, "10n % 3n"), run_source(&mut vm, "1n"));
|
||||
assert_eq!(run_source(&mut vm, "-7n"), run_source(&mut vm, "0n - 7n"));
|
||||
assert_eq!(run_source(&mut vm, "123n == 123n"), JsValue::bool(true));
|
||||
assert_eq!(run_source(&mut vm, "123n === 123n"), JsValue::bool(true));
|
||||
assert_eq!(run_source(&mut vm, "5n < 3n"), JsValue::bool(false));
|
||||
assert_eq!(run_source(&mut vm, "5n > 3n"), JsValue::bool(true));
|
||||
assert_eq!(run_source(&mut vm, "1n === 1"), JsValue::bool(false));
|
||||
let typeof_result = run_source(&mut vm, "typeof 123n");
|
||||
assert!(typeof_result.is_string());
|
||||
assert_eq!(vm.lookup_str(typeof_result).as_deref(), Some("bigint"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bigint_constructor_and_string() {
|
||||
let mut vm = Vm::new();
|
||||
assert_eq!(run_source(&mut vm, "BigInt(42)"), run_source(&mut vm, "42n"));
|
||||
assert_eq!(run_source(&mut vm, "BigInt('123')"), run_source(&mut vm, "123n"));
|
||||
assert_eq!(run_source(&mut vm, "BigInt('0x10')"), run_source(&mut vm, "16n"));
|
||||
let s = run_source(&mut vm, "String(123n)");
|
||||
assert!(s.is_string());
|
||||
assert_eq!(vm.lookup_str(s).as_deref(), Some("123"));
|
||||
let ts = run_source(&mut vm, "(123n).toString()");
|
||||
assert!(ts.is_string());
|
||||
assert_eq!(vm.lookup_str(ts).as_deref(), Some("123"));
|
||||
assert_eq!(run_source(&mut vm, "Number(5n)"), JsValue::int(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bigint_mixed_type_throws() {
|
||||
let mut vm = Vm::new();
|
||||
let te = run_source(&mut vm, "try { 1n + 1 } catch(e) { e.name }");
|
||||
assert_eq!(vm.lookup_str(te).as_deref(), Some("TypeError"));
|
||||
let re = run_source(&mut vm, "try { 1n / 0n } catch(e) { e.name }");
|
||||
assert_eq!(vm.lookup_str(re).as_deref(), Some("RangeError"));
|
||||
let ne = run_source(&mut vm, "try { new BigInt(1) } catch(e) { e.name }");
|
||||
assert_eq!(vm.lookup_str(ne).as_deref(), Some("TypeError"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bigint_survives_reset_and_gc() {
|
||||
let mut vm = Vm::new();
|
||||
let result = run_source(&mut vm, "100n + 23n");
|
||||
assert!(result.is_bigint());
|
||||
assert_eq!(vm.bigint_value(result), 123);
|
||||
vm.reset();
|
||||
// reset 保留 session 字符串/bigint box:值仍可读。
|
||||
assert_eq!(vm.bigint_value(result), 123);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bigint_wrapped_and_number_comparison() {
|
||||
let mut vm = Vm::new();
|
||||
// 包装对象 coerce 后双 BigInt 运算。
|
||||
assert_eq!(run_source(&mut vm, "Object(2n) / 2n"), run_source(&mut vm, "1n"));
|
||||
assert_eq!(run_source(&mut vm, "Object(2n) * 3n"), run_source(&mut vm, "6n"));
|
||||
assert_eq!(run_source(&mut vm, "2n + Object(3n)"), run_source(&mut vm, "5n"));
|
||||
// BigInt 与 Number 精确关系比较(超出 f64 精度仍精确)。
|
||||
assert_eq!(run_source(&mut vm, "9007199254740993n > 9007199254740992"), JsValue::bool(true));
|
||||
assert_eq!(run_source(&mut vm, "9007199254740993n < 9007199254740994"), JsValue::bool(true));
|
||||
assert_eq!(run_source(&mut vm, "2n < 3"), JsValue::bool(true));
|
||||
assert_eq!(run_source(&mut vm, "3n >= 3"), JsValue::bool(true));
|
||||
// NaN 关系比较为 false。
|
||||
assert_eq!(run_source(&mut vm, "0n < NaN"), JsValue::bool(false));
|
||||
// 混合算术抛 TypeError。
|
||||
let te = run_source(&mut vm, "try { Object(1n) - 1 } catch(e) { e.name }");
|
||||
assert_eq!(vm.lookup_str(te).as_deref(), Some("TypeError"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user