feat: 实现最小 BigInt 子集(字面量/算术/比较/构造器,Temporal 前置)

This commit is contained in:
2026-08-10 23:02:31 +08:00
parent 9e26a13655
commit 078688070d
22 changed files with 706 additions and 32 deletions
+120 -14
View File
@@ -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 => {
+2
View File
@@ -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() {
+5
View File
@@ -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)
}