mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 实现最小 BigInt 子集(字面量/算术/比较/构造器,Temporal 前置)
This commit is contained in:
@@ -0,0 +1,131 @@
|
|||||||
|
use oxide_runtime_api::{NativeResult, VmHost};
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
/// JS `BigInt()` 构造逻辑:把参数转成 BigInt 值。
|
||||||
|
///
|
||||||
|
/// 普通调用返回原始 BigInt;`new BigInt()` 抛 TypeError(BigInt 不可构造)。
|
||||||
|
/// 参数转换规则(最小子集):
|
||||||
|
/// - 无参数 → `0n`
|
||||||
|
/// - BigInt → 原样返回
|
||||||
|
/// - Number → 截断为整数(非整数抛 RangeError)
|
||||||
|
/// - String → 解析十进制整数
|
||||||
|
/// - Boolean → `0n`/`1n`
|
||||||
|
/// - 其它类型 → TypeError
|
||||||
|
pub fn bigint_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
if args.len() > 1 {
|
||||||
|
let this_val = vm.reg(args[0]);
|
||||||
|
if this_val.is_object() {
|
||||||
|
// new 语义:BigInt 不可 new,抛 TypeError。
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(
|
||||||
|
vm,
|
||||||
|
"BigInt is not a constructor",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||||
|
match to_bigint_value(vm, val) {
|
||||||
|
Ok(v) => NativeResult::Ok(v),
|
||||||
|
Err(err) => NativeResult::Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `BigInt.prototype.toString`:返回 BigInt 的十进制字符串。
|
||||||
|
pub fn bigint_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let this_val = vm.reg(args[0]);
|
||||||
|
if !this_val.is_bigint() {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(
|
||||||
|
vm,
|
||||||
|
"BigInt.prototype.toString called on incompatible receiver",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let v = unsafe { oxide_runtime_api::bigint_data(this_val) };
|
||||||
|
NativeResult::Ok(vm.new_string(&oxide_runtime_api::bigint_to_string(v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `BigInt.prototype.valueOf`:返回包装的 BigInt 原始值。
|
||||||
|
pub fn bigint_value_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let this_val = vm.reg(args[0]);
|
||||||
|
if this_val.is_bigint() {
|
||||||
|
return NativeResult::Ok(this_val);
|
||||||
|
}
|
||||||
|
if this_val.is_object() {
|
||||||
|
let ptr = this_val.as_js_object_ptr();
|
||||||
|
if !ptr.is_null() {
|
||||||
|
let obj = unsafe { &*ptr };
|
||||||
|
if obj.proto().is_object() {
|
||||||
|
let proto_ptr = obj.proto().as_js_object_ptr();
|
||||||
|
let bigint_proto = vm.session().builtin_world().bigint_proto.as_ptr() as *mut oxide_types::object::JsObject;
|
||||||
|
if !proto_ptr.is_null() && std::ptr::eq(proto_ptr, bigint_proto) {
|
||||||
|
let v = obj.get_prop_at(0);
|
||||||
|
if v.is_bigint() {
|
||||||
|
return NativeResult::Ok(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeResult::Err(crate::error::create_type_error(
|
||||||
|
vm,
|
||||||
|
"BigInt.prototype.valueOf called on incompatible receiver",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 把原始值转为 BigInt 值(`BigInt(value)` 的参数转换)。
|
||||||
|
fn to_bigint_value<H: VmHost>(vm: &mut H, val: JsValue) -> Result<JsValue, JsValue> {
|
||||||
|
if val.is_bigint() {
|
||||||
|
return Ok(val);
|
||||||
|
}
|
||||||
|
if val.is_undefined() {
|
||||||
|
return Ok(vm.new_bigint(0));
|
||||||
|
}
|
||||||
|
if val.is_bool() {
|
||||||
|
return Ok(vm.new_bigint(if val.as_bool() { 1 } else { 0 }));
|
||||||
|
}
|
||||||
|
if val.is_int() {
|
||||||
|
return Ok(vm.new_bigint(val.as_int() as i128));
|
||||||
|
}
|
||||||
|
if val.is_double() {
|
||||||
|
let d = val.as_double();
|
||||||
|
if d.is_nan() || d.is_infinite() {
|
||||||
|
return Err(crate::error::create_range_error(vm, "The number cannot be converted to a BigInt because it is not an integer"));
|
||||||
|
}
|
||||||
|
if d.trunc() != d {
|
||||||
|
return Err(crate::error::create_range_error(vm, "The number cannot be converted to a BigInt because it is not an integer"));
|
||||||
|
}
|
||||||
|
return Ok(vm.new_bigint(d.trunc() as i128));
|
||||||
|
}
|
||||||
|
if val.is_string() {
|
||||||
|
let s = unsafe { oxide_runtime_api::string_data(val) }.to_string();
|
||||||
|
let s = s.trim();
|
||||||
|
let sign = if let Some(rest) = s.strip_prefix('-') {
|
||||||
|
(true, rest)
|
||||||
|
} else if let Some(rest) = s.strip_prefix('+') {
|
||||||
|
(false, rest)
|
||||||
|
} else {
|
||||||
|
(false, s)
|
||||||
|
};
|
||||||
|
let (radix, digits) = if let Some(hex) = sign.1.strip_prefix("0x").or_else(|| sign.1.strip_prefix("0X")) {
|
||||||
|
(16u32, hex)
|
||||||
|
} else if let Some(oct) = sign.1.strip_prefix("0o").or_else(|| sign.1.strip_prefix("0O")) {
|
||||||
|
(8u32, oct)
|
||||||
|
} else if let Some(bin) = sign.1.strip_prefix("0b").or_else(|| sign.1.strip_prefix("0B")) {
|
||||||
|
(2u32, bin)
|
||||||
|
} else {
|
||||||
|
(10u32, sign.1)
|
||||||
|
};
|
||||||
|
let magnitude = match i128::from_str_radix(digits, radix) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(crate::error::create_syntax_error(
|
||||||
|
vm,
|
||||||
|
"Cannot convert string to BigInt: invalid integer literal",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Ok(vm.new_bigint(if sign.0 { -magnitude } else { magnitude }));
|
||||||
|
}
|
||||||
|
Err(crate::error::create_type_error(
|
||||||
|
vm,
|
||||||
|
"Cannot convert value to a BigInt",
|
||||||
|
))
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ pub mod array;
|
|||||||
pub mod array_buffer;
|
pub mod array_buffer;
|
||||||
/// Boolean 内置对象实现(constructor 与 prototype 的 valueOf/toString)。
|
/// Boolean 内置对象实现(constructor 与 prototype 的 valueOf/toString)。
|
||||||
pub mod boolean;
|
pub mod boolean;
|
||||||
|
/// BigInt 内置对象实现(constructor 与 prototype 的 toString)。
|
||||||
|
pub mod bigint;
|
||||||
/// builtins 层日志宏(`builtins_error`/`builtins_info` 等),target 为 `oxide::builtins`。
|
/// builtins 层日志宏(`builtins_error`/`builtins_info` 等),target 为 `oxide::builtins`。
|
||||||
pub mod builtins_log;
|
pub mod builtins_log;
|
||||||
/// DataView 内置对象实现(对 ArrayBuffer 的定点读写视图)。
|
/// DataView 内置对象实现(对 ArrayBuffer 的定点读写视图)。
|
||||||
|
|||||||
@@ -310,6 +310,13 @@ pub fn object_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
obj_ref.set_prop_at(0, val);
|
obj_ref.set_prop_at(0, val);
|
||||||
return NativeResult::Ok(JsValue::from_js_object(obj));
|
return NativeResult::Ok(JsValue::from_js_object(obj));
|
||||||
}
|
}
|
||||||
|
if val.is_bigint() {
|
||||||
|
let proto = vm.session().builtin_world().bigint_proto.as_ptr() as *mut JsObject;
|
||||||
|
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto)));
|
||||||
|
let obj_ref = unsafe { &mut *obj };
|
||||||
|
obj_ref.set_prop_at(0, val);
|
||||||
|
return NativeResult::Ok(JsValue::from_js_object(obj));
|
||||||
|
}
|
||||||
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
||||||
NativeResult::Ok(JsValue::from_js_object(obj))
|
NativeResult::Ok(JsValue::from_js_object(obj))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ fn stub_error<H: VmHost>(vm: &mut H, name: &str) -> NativeResult {
|
|||||||
pub fn proxy_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
pub fn proxy_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||||
stub_error(vm, "Proxy")
|
stub_error(vm, "Proxy")
|
||||||
}
|
}
|
||||||
/// `BigInt` 全局占位:始终抛 TypeError。BigInt 按设计延后实现。
|
|
||||||
pub fn bigint_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
|
||||||
stub_error(vm, "BigInt")
|
|
||||||
}
|
|
||||||
/// `WeakMap` 全局占位:始终抛 TypeError。WeakMap 按设计延后实现。
|
/// `WeakMap` 全局占位:始终抛 TypeError。WeakMap 按设计延后实现。
|
||||||
pub fn weakmap_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
pub fn weakmap_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||||
stub_error(vm, "WeakMap")
|
stub_error(vm, "WeakMap")
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::opcode::{self, OpCode};
|
|||||||
pub enum Constant {
|
pub enum Constant {
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
|
BigInt(i128),
|
||||||
String(String),
|
String(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Null,
|
Null,
|
||||||
|
|||||||
@@ -197,6 +197,8 @@ fn format_js_value(
|
|||||||
// SAFETY: val 已确认是字符串值。
|
// SAFETY: val 已确认是字符串值。
|
||||||
let s = unsafe { (*val.as_string_ptr()).data.clone() };
|
let s = unsafe { (*val.as_string_ptr()).data.clone() };
|
||||||
format!("\"{s}\"")
|
format!("\"{s}\"")
|
||||||
|
} else if val.is_bigint() {
|
||||||
|
format!("{}", vm.bigint_value(val))
|
||||||
} else if val.is_object() {
|
} else if val.is_object() {
|
||||||
let obj = unsafe { &*val.as_js_object_ptr() };
|
let obj = unsafe { &*val.as_js_object_ptr() };
|
||||||
if obj.is_promise_obj() {
|
if obj.is_promise_obj() {
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ impl ParamSpec<'_> {
|
|||||||
enum ConstantKey {
|
enum ConstantKey {
|
||||||
Number(u64),
|
Number(u64),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
|
BigInt(i128),
|
||||||
String(String),
|
String(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Null,
|
Null,
|
||||||
@@ -642,6 +643,7 @@ impl ConstantKey {
|
|||||||
match value {
|
match value {
|
||||||
Constant::Number(v) => Some(Self::Number(v.to_bits())),
|
Constant::Number(v) => Some(Self::Number(v.to_bits())),
|
||||||
Constant::Int(v) => Some(Self::Int(*v)),
|
Constant::Int(v) => Some(Self::Int(*v)),
|
||||||
|
Constant::BigInt(v) => Some(Self::BigInt(*v)),
|
||||||
Constant::String(v) => Some(Self::String(v.clone())),
|
Constant::String(v) => Some(Self::String(v.clone())),
|
||||||
Constant::Boolean(v) => Some(Self::Boolean(*v)),
|
Constant::Boolean(v) => Some(Self::Boolean(*v)),
|
||||||
Constant::Null => Some(Self::Null),
|
Constant::Null => Some(Self::Null),
|
||||||
@@ -1255,6 +1257,7 @@ impl Emitter {
|
|||||||
pub(crate) fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
pub(crate) fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||||
match expr {
|
match expr {
|
||||||
Expression::NumericLiteral(_)
|
Expression::NumericLiteral(_)
|
||||||
|
| Expression::BigIntLiteral(_)
|
||||||
| Expression::StringLiteral(_)
|
| Expression::StringLiteral(_)
|
||||||
| Expression::BooleanLiteral(_)
|
| Expression::BooleanLiteral(_)
|
||||||
| Expression::NullLiteral(_)
|
| Expression::NullLiteral(_)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ impl Emitter {
|
|||||||
pub(crate) fn emit_literal(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
pub(crate) fn emit_literal(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||||
match expr {
|
match expr {
|
||||||
Expression::NumericLiteral(n) => self.emit_numeric_literal_expression(n, ctx),
|
Expression::NumericLiteral(n) => self.emit_numeric_literal_expression(n, ctx),
|
||||||
|
Expression::BigIntLiteral(b) => self.emit_bigint_literal_expression(b, ctx),
|
||||||
Expression::StringLiteral(s) => self.emit_string_literal_expression(s, ctx),
|
Expression::StringLiteral(s) => self.emit_string_literal_expression(s, ctx),
|
||||||
Expression::BooleanLiteral(b) => self.emit_boolean_literal_expression(b, ctx),
|
Expression::BooleanLiteral(b) => self.emit_boolean_literal_expression(b, ctx),
|
||||||
Expression::NullLiteral(_) => self.emit_null_literal_expression(ctx),
|
Expression::NullLiteral(_) => self.emit_null_literal_expression(ctx),
|
||||||
@@ -33,6 +34,19 @@ impl Emitter {
|
|||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_bigint_literal_expression(
|
||||||
|
&self, b: &oxide_parser::BigIntLiteral, ctx: &mut CompileCtx,
|
||||||
|
) -> Result<u32, String> {
|
||||||
|
let value = b
|
||||||
|
.value
|
||||||
|
.parse::<i128>()
|
||||||
|
.map_err(|_| format!("BigInt literal out of range: {}", b.value))?;
|
||||||
|
let idx = ctx.add_constant(Constant::BigInt(value));
|
||||||
|
let r = ctx.alloc_reg();
|
||||||
|
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_string_literal_expression(
|
fn emit_string_literal_expression(
|
||||||
&self, s: &oxide_parser::StringLiteral, ctx: &mut CompileCtx,
|
&self, s: &oxide_parser::StringLiteral, ctx: &mut CompileCtx,
|
||||||
) -> Result<u32, String> {
|
) -> Result<u32, String> {
|
||||||
|
|||||||
@@ -251,6 +251,8 @@ pub struct BuiltinWorld {
|
|||||||
pub plain_date_proto: P<JsObject>,
|
pub plain_date_proto: P<JsObject>,
|
||||||
pub plain_time_constructor: P<JsObject>,
|
pub plain_time_constructor: P<JsObject>,
|
||||||
pub plain_time_proto: P<JsObject>,
|
pub plain_time_proto: P<JsObject>,
|
||||||
|
pub bigint_constructor: P<JsObject>,
|
||||||
|
pub bigint_proto: P<JsObject>,
|
||||||
pub stub_objects: Vec<P<JsObject>>,
|
pub stub_objects: Vec<P<JsObject>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,9 +504,10 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
|
|||||||
wire_ctor_proto(&world.instant_constructor, &world.instant_proto);
|
wire_ctor_proto(&world.instant_constructor, &world.instant_proto);
|
||||||
wire_ctor_proto(&world.plain_date_constructor, &world.plain_date_proto);
|
wire_ctor_proto(&world.plain_date_constructor, &world.plain_date_proto);
|
||||||
wire_ctor_proto(&world.plain_time_constructor, &world.plain_time_proto);
|
wire_ctor_proto(&world.plain_time_constructor, &world.plain_time_proto);
|
||||||
|
wire_ctor_proto(&world.bigint_constructor, &world.bigint_proto);
|
||||||
|
|
||||||
let obj_proto_val = JsValue::from_js_object(world.object_proto.as_ptr() as *mut JsObject);
|
let obj_proto_val = JsValue::from_js_object(world.object_proto.as_ptr() as *mut JsObject);
|
||||||
let non_object_protos: [&P<JsObject>; 17] = [
|
let non_object_protos: [&P<JsObject>; 18] = [
|
||||||
&world.array_proto,
|
&world.array_proto,
|
||||||
&world.function_proto,
|
&world.function_proto,
|
||||||
&world.string_proto,
|
&world.string_proto,
|
||||||
@@ -522,6 +525,7 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
|
|||||||
&world.instant_proto,
|
&world.instant_proto,
|
||||||
&world.plain_date_proto,
|
&world.plain_date_proto,
|
||||||
&world.plain_time_proto,
|
&world.plain_time_proto,
|
||||||
|
&world.bigint_proto,
|
||||||
];
|
];
|
||||||
for proto in &non_object_protos {
|
for proto in &non_object_protos {
|
||||||
set_proto_if_changed(proto, obj_proto_val);
|
set_proto_if_changed(proto, obj_proto_val);
|
||||||
@@ -656,6 +660,8 @@ impl BuiltinWorld {
|
|||||||
BuiltinId::PlainDateProto => &self.plain_date_proto,
|
BuiltinId::PlainDateProto => &self.plain_date_proto,
|
||||||
BuiltinId::PlainTimeConstructor => &self.plain_time_constructor,
|
BuiltinId::PlainTimeConstructor => &self.plain_time_constructor,
|
||||||
BuiltinId::PlainTimeProto => &self.plain_time_proto,
|
BuiltinId::PlainTimeProto => &self.plain_time_proto,
|
||||||
|
BuiltinId::BigIntConstructor => &self.bigint_constructor,
|
||||||
|
BuiltinId::BigIntProto => &self.bigint_proto,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,6 +710,7 @@ impl BuiltinWorld {
|
|||||||
make_named_pair(string_forge, shape_forge, labels, "PlainDate");
|
make_named_pair(string_forge, shape_forge, labels, "PlainDate");
|
||||||
let (plain_time_proto, plain_time_constructor) =
|
let (plain_time_proto, plain_time_constructor) =
|
||||||
make_named_pair(string_forge, shape_forge, labels, "PlainTime");
|
make_named_pair(string_forge, shape_forge, labels, "PlainTime");
|
||||||
|
let (bigint_proto, bigint_constructor) = make_named_pair(string_forge, shape_forge, labels, "BigInt");
|
||||||
let stub_objects = Vec::new();
|
let stub_objects = Vec::new();
|
||||||
|
|
||||||
let world = Self {
|
let world = Self {
|
||||||
@@ -784,6 +791,8 @@ impl BuiltinWorld {
|
|||||||
plain_date_proto,
|
plain_date_proto,
|
||||||
plain_time_constructor,
|
plain_time_constructor,
|
||||||
plain_time_proto,
|
plain_time_proto,
|
||||||
|
bigint_constructor,
|
||||||
|
bigint_proto,
|
||||||
stub_objects,
|
stub_objects,
|
||||||
};
|
};
|
||||||
wire_builtin_world_links(&world);
|
wire_builtin_world_links(&world);
|
||||||
@@ -987,6 +996,11 @@ impl BuiltinWorld {
|
|||||||
current.plain_time_constructor.clone(),
|
current.plain_time_constructor.clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
let (bigint_proto, bigint_constructor) = if dirty.stubs {
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "BigInt")
|
||||||
|
} else {
|
||||||
|
(current.bigint_proto.clone(), current.bigint_constructor.clone())
|
||||||
|
};
|
||||||
let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() };
|
let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() };
|
||||||
|
|
||||||
let world = BuiltinWorld {
|
let world = BuiltinWorld {
|
||||||
@@ -1067,6 +1081,8 @@ impl BuiltinWorld {
|
|||||||
plain_date_proto,
|
plain_date_proto,
|
||||||
plain_time_constructor,
|
plain_time_constructor,
|
||||||
plain_time_proto,
|
plain_time_proto,
|
||||||
|
bigint_constructor,
|
||||||
|
bigint_proto,
|
||||||
stub_objects,
|
stub_objects,
|
||||||
};
|
};
|
||||||
wire_builtin_world_links(&world);
|
wire_builtin_world_links(&world);
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ pub struct KernelSession {
|
|||||||
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
|
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
|
||||||
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
|
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
|
||||||
/// builtin 家族。
|
/// builtin 家族。
|
||||||
pub const NUM_BUILTINS: usize = 76;
|
pub const NUM_BUILTINS: usize = 78;
|
||||||
|
|
||||||
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
|
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
|
||||||
///
|
///
|
||||||
@@ -310,6 +310,8 @@ pub enum BuiltinId {
|
|||||||
PlainDateProto = 73,
|
PlainDateProto = 73,
|
||||||
PlainTimeConstructor = 74,
|
PlainTimeConstructor = 74,
|
||||||
PlainTimeProto = 75,
|
PlainTimeProto = 75,
|
||||||
|
BigIntConstructor = 76,
|
||||||
|
BigIntProto = 77,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuiltinId {
|
impl BuiltinId {
|
||||||
@@ -391,6 +393,8 @@ impl BuiltinId {
|
|||||||
BuiltinId::PlainDateProto,
|
BuiltinId::PlainDateProto,
|
||||||
BuiltinId::PlainTimeConstructor,
|
BuiltinId::PlainTimeConstructor,
|
||||||
BuiltinId::PlainTimeProto,
|
BuiltinId::PlainTimeProto,
|
||||||
|
BuiltinId::BigIntConstructor,
|
||||||
|
BuiltinId::BigIntProto,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +628,10 @@ impl KernelSession {
|
|||||||
|| gen(BuiltinId::PlainDateProto) != snap(BuiltinId::PlainDateProto)
|
|| gen(BuiltinId::PlainDateProto) != snap(BuiltinId::PlainDateProto)
|
||||||
|| gen(BuiltinId::PlainTimeConstructor) != snap(BuiltinId::PlainTimeConstructor)
|
|| gen(BuiltinId::PlainTimeConstructor) != snap(BuiltinId::PlainTimeConstructor)
|
||||||
|| gen(BuiltinId::PlainTimeProto) != snap(BuiltinId::PlainTimeProto),
|
|| gen(BuiltinId::PlainTimeProto) != snap(BuiltinId::PlainTimeProto),
|
||||||
stubs: world.stub_objects.len() != snapshot.stub_objects_len || stub_generations_dirty,
|
stubs: world.stub_objects.len() != snapshot.stub_objects_len
|
||||||
|
|| stub_generations_dirty
|
||||||
|
|| gen(BuiltinId::BigIntConstructor) != snap(BuiltinId::BigIntConstructor)
|
||||||
|
|| gen(BuiltinId::BigIntProto) != snap(BuiltinId::BigIntProto),
|
||||||
global: BuiltinSnapshot::gen(&self.global_object) != snapshot.global_object_generation,
|
global: BuiltinSnapshot::gen(&self.global_object) != snapshot.global_object_generation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
mod runtime_api_log;
|
mod runtime_api_log;
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||||
@@ -73,6 +74,10 @@ pub trait VmHost {
|
|||||||
// 对象分配 / 字符串创建
|
// 对象分配 / 字符串创建
|
||||||
fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject;
|
fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject;
|
||||||
fn new_string(&mut self, s: &str) -> JsValue;
|
fn new_string(&mut self, s: &str) -> JsValue;
|
||||||
|
/// 分配 BigInt 值(`i128` box 登记到 VM,返回携带指针的 `JsValue`)。
|
||||||
|
fn new_bigint(&mut self, v: i128) -> JsValue;
|
||||||
|
/// 读取 BigInt 值;调用方须保证 `val.is_bigint()`。
|
||||||
|
fn bigint_value(&mut self, val: JsValue) -> i128;
|
||||||
|
|
||||||
// 内核访问器
|
// 内核访问器
|
||||||
fn kernel_core(&self) -> &Arc<KernelCore>;
|
fn kernel_core(&self) -> &Arc<KernelCore>;
|
||||||
@@ -162,6 +167,23 @@ pub fn string_value_eq(a: JsValue, b: JsValue) -> bool {
|
|||||||
sa.data == sb.data
|
sa.data == sb.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 借出 BigInt 值。
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `val` 必须是 BigInt `JsValue`,且其 `i128` box 存活(VM 在 full_reset 前保证)。
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn bigint_data(val: JsValue) -> i128 {
|
||||||
|
*val.as_bigint_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BigInt 的十进制字符串表示(`i128` → 十进制)。
|
||||||
|
///
|
||||||
|
/// 与 ECMA-262 `Number::toString` 无关:BigInt 恒以十进制输出,负号前缀。
|
||||||
|
#[inline]
|
||||||
|
pub fn bigint_to_string(v: i128) -> String {
|
||||||
|
v.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
/// 对原始值执行 ECMAScript ToNumber 的快速路径(不触发对象 coercion)。
|
/// 对原始值执行 ECMAScript ToNumber 的快速路径(不触发对象 coercion)。
|
||||||
///
|
///
|
||||||
/// Number/String/Boolean/null 按规范转换;undefined 与不可解析字符串为 `NaN`;
|
/// Number/String/Boolean/null 按规范转换;undefined 与不可解析字符串为 `NaN`;
|
||||||
@@ -186,6 +208,11 @@ pub fn to_number(val: JsValue) -> f64 {
|
|||||||
let s = unsafe { string_data(val) };
|
let s = unsafe { string_data(val) };
|
||||||
return parse_js_number(s);
|
return parse_js_number(s);
|
||||||
}
|
}
|
||||||
|
if val.is_bigint() {
|
||||||
|
// BigInt → Number 近似转换:i128 超出 f64 精度时舍入为近似值。
|
||||||
|
// 规范路径(显式 Number(bigint) / 位运算)在 builtins / dispatch 层精确处理。
|
||||||
|
return unsafe { bigint_data(val) } as f64;
|
||||||
|
}
|
||||||
if val.is_object() {
|
if val.is_object() {
|
||||||
return f64::NAN;
|
return f64::NAN;
|
||||||
}
|
}
|
||||||
@@ -382,6 +409,11 @@ pub fn push_to_string(val: JsValue, buf: &mut String) {
|
|||||||
unsafe { buf.push_str(string_data(val)) };
|
unsafe { buf.push_str(string_data(val)) };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if val.is_bigint() {
|
||||||
|
use std::fmt::Write;
|
||||||
|
let _ = write!(buf, "{}", unsafe { bigint_data(val) });
|
||||||
|
return;
|
||||||
|
}
|
||||||
if val.is_object() {
|
if val.is_object() {
|
||||||
buf.push_str("[object]");
|
buf.push_str("[object]");
|
||||||
}
|
}
|
||||||
@@ -410,6 +442,9 @@ pub fn to_string(val: JsValue) -> String {
|
|||||||
if val.is_string() {
|
if val.is_string() {
|
||||||
return unsafe { string_data(val) }.to_string();
|
return unsafe { string_data(val) }.to_string();
|
||||||
}
|
}
|
||||||
|
if val.is_bigint() {
|
||||||
|
return unsafe { bigint_data(val) }.to_string();
|
||||||
|
}
|
||||||
if val.is_object() {
|
if val.is_object() {
|
||||||
return "[object]".to_string();
|
return "[object]".to_string();
|
||||||
}
|
}
|
||||||
@@ -434,6 +469,9 @@ pub fn to_boolean(val: JsValue) -> bool {
|
|||||||
if val.is_string() {
|
if val.is_string() {
|
||||||
return !unsafe { (*val.as_string_ptr()).is_empty() };
|
return !unsafe { (*val.as_string_ptr()).is_empty() };
|
||||||
}
|
}
|
||||||
|
if val.is_bigint() {
|
||||||
|
return unsafe { bigint_data(val) } != 0;
|
||||||
|
}
|
||||||
if val.is_object() {
|
if val.is_object() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -467,6 +505,9 @@ fn same_type(a: JsValue, b: JsValue) -> bool {
|
|||||||
if a.is_symbol() && b.is_symbol() {
|
if a.is_symbol() && b.is_symbol() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if a.is_bigint() && b.is_bigint() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,6 +527,22 @@ pub fn abstract_eq<H: VmHost>(lhs: JsValue, rhs: JsValue, host: &mut H) -> Resul
|
|||||||
if (lhs.is_null() && rhs.is_undefined()) || (lhs.is_undefined() && rhs.is_null()) {
|
if (lhs.is_null() && rhs.is_undefined()) || (lhs.is_undefined() && rhs.is_null()) {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
// BigInt 与 Number:转 f64 比较(精度内精确;超出精度近似)。
|
||||||
|
if lhs.is_bigint() && (rhs.is_int() || rhs.is_double()) {
|
||||||
|
return Ok(unsafe { bigint_data(lhs) } as f64 == to_number(rhs));
|
||||||
|
}
|
||||||
|
if (lhs.is_int() || lhs.is_double()) && rhs.is_bigint() {
|
||||||
|
return Ok(to_number(lhs) == unsafe { bigint_data(rhs) } as f64);
|
||||||
|
}
|
||||||
|
// BigInt 与 String:字符串解析为数字后比较。
|
||||||
|
if lhs.is_bigint() && rhs.is_string() {
|
||||||
|
let r = parse_js_number(unsafe { string_data(rhs) });
|
||||||
|
return Ok(unsafe { bigint_data(lhs) } as f64 == r);
|
||||||
|
}
|
||||||
|
if lhs.is_string() && rhs.is_bigint() {
|
||||||
|
let l = parse_js_number(unsafe { string_data(lhs) });
|
||||||
|
return Ok(l == unsafe { bigint_data(rhs) } as f64);
|
||||||
|
}
|
||||||
// 步骤 5-6:Number 与 String。
|
// 步骤 5-6:Number 与 String。
|
||||||
if (lhs.is_int() || lhs.is_double()) && rhs.is_string() {
|
if (lhs.is_int() || lhs.is_double()) && rhs.is_string() {
|
||||||
return Ok(strict_double_eq(to_number(lhs), to_number(rhs)));
|
return Ok(strict_double_eq(to_number(lhs), to_number(rhs)));
|
||||||
@@ -547,6 +604,9 @@ pub fn strict_eq(lhs: JsValue, rhs: JsValue) -> bool {
|
|||||||
if lhs.is_symbol() && rhs.is_symbol() {
|
if lhs.is_symbol() && rhs.is_symbol() {
|
||||||
return lhs.as_symbol_index() == rhs.as_symbol_index();
|
return lhs.as_symbol_index() == rhs.as_symbol_index();
|
||||||
}
|
}
|
||||||
|
if lhs.is_bigint() && rhs.is_bigint() {
|
||||||
|
return unsafe { bigint_data(lhs) } == unsafe { bigint_data(rhs) };
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,6 +626,37 @@ pub fn relational_compare(lhs: JsValue, rhs: JsValue) -> Option<bool> {
|
|||||||
let rs = unsafe { string_data(rhs) };
|
let rs = unsafe { string_data(rhs) };
|
||||||
return Some(ls < rs);
|
return Some(ls < rs);
|
||||||
}
|
}
|
||||||
|
if lhs.is_bigint() && rhs.is_bigint() {
|
||||||
|
let l = unsafe { bigint_data(lhs) };
|
||||||
|
let r = unsafe { bigint_data(rhs) };
|
||||||
|
return Some(l < r);
|
||||||
|
}
|
||||||
|
// BigInt 与 Number 混合:Number 为整数且落在 i128 范围时转 i128 精确比较,
|
||||||
|
// 否则(非整数/超出范围/NaN/±Infinity)转 f64 比较(NaN 结果为未定义)。
|
||||||
|
if lhs.is_bigint() && (rhs.is_int() || rhs.is_double()) {
|
||||||
|
if rhs.is_double() && rhs.as_double().is_nan() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let l = unsafe { bigint_data(lhs) };
|
||||||
|
return Some(bigint_cmp_number(l, rhs) == Ordering::Less);
|
||||||
|
}
|
||||||
|
if (lhs.is_int() || lhs.is_double()) && rhs.is_bigint() {
|
||||||
|
if lhs.is_double() && lhs.as_double().is_nan() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let r = unsafe { bigint_data(rhs) };
|
||||||
|
// lhs(Number) < rhs(BigInt) ⇔ 反向比较(lhs 作为"number 侧")。
|
||||||
|
return Some(number_cmp_bigint(lhs, r) == Ordering::Less);
|
||||||
|
}
|
||||||
|
// BigInt 与 String/Boolean 混合:转 Number 后比较。
|
||||||
|
if lhs.is_bigint() || rhs.is_bigint() {
|
||||||
|
let l = to_number(lhs);
|
||||||
|
let r = to_number(rhs);
|
||||||
|
if l.is_nan() || r.is_nan() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return l.partial_cmp(&r).map(|o| o.is_lt());
|
||||||
|
}
|
||||||
let l = to_number(lhs);
|
let l = to_number(lhs);
|
||||||
let r = to_number(rhs);
|
let r = to_number(rhs);
|
||||||
if l.is_nan() || r.is_nan() {
|
if l.is_nan() || r.is_nan() {
|
||||||
@@ -592,6 +683,31 @@ fn to_f64(val: JsValue) -> f64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// BigInt 与 Number 的序比较:Number 为整数且落于 i128 范围时精确比较,
|
||||||
|
/// 否则退化为 f64 近似比较(非有限/超出 i128 范围时)。
|
||||||
|
fn bigint_cmp_number(big: i128, num: JsValue) -> Ordering {
|
||||||
|
number_cmp_bigint(num, big).reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number 与 BigInt 的序比较(`num` 与 `big` 的 `<`/`=`/`>`)。
|
||||||
|
fn number_cmp_bigint(num: JsValue, big: i128) -> Ordering {
|
||||||
|
if num.is_int() {
|
||||||
|
return (num.as_int() as i128).cmp(&big);
|
||||||
|
}
|
||||||
|
let d = num.as_double();
|
||||||
|
if d.is_nan() {
|
||||||
|
return Ordering::Greater;
|
||||||
|
}
|
||||||
|
if d.is_infinite() {
|
||||||
|
return if d.is_sign_positive() { Ordering::Greater } else { Ordering::Less };
|
||||||
|
}
|
||||||
|
let truncated = d.trunc();
|
||||||
|
if truncated != d || truncated.abs() > i128::MAX as f64 {
|
||||||
|
return (d).partial_cmp(&(big as f64)).unwrap_or(Ordering::Equal);
|
||||||
|
}
|
||||||
|
(truncated as i128).cmp(&big)
|
||||||
|
}
|
||||||
|
|
||||||
/// SameValue(x, y)(ECMA-262 §7.2.9):与 `===` 的区别在于 NaN 视为相等、+0/-0 视为不同。
|
/// SameValue(x, y)(ECMA-262 §7.2.9):与 `===` 的区别在于 NaN 视为相等、+0/-0 视为不同。
|
||||||
pub fn same_value(lhs: JsValue, rhs: JsValue) -> bool {
|
pub fn same_value(lhs: JsValue, rhs: JsValue) -> bool {
|
||||||
if lhs.is_double() && rhs.is_double() {
|
if lhs.is_double() && rhs.is_double() {
|
||||||
@@ -639,6 +755,9 @@ pub fn same_value(lhs: JsValue, rhs: JsValue) -> bool {
|
|||||||
if lhs.is_symbol() && rhs.is_symbol() {
|
if lhs.is_symbol() && rhs.is_symbol() {
|
||||||
return lhs.as_symbol_index() == rhs.as_symbol_index();
|
return lhs.as_symbol_index() == rhs.as_symbol_index();
|
||||||
}
|
}
|
||||||
|
if lhs.is_bigint() && rhs.is_bigint() {
|
||||||
|
return unsafe { bigint_data(lhs) } == unsafe { bigint_data(rhs) };
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,6 +818,8 @@ pub fn to_object<H: VmHost>(val: JsValue, host: &mut H) -> Result<JsValue, Strin
|
|||||||
(P::as_ptr(&world.number_proto) as *mut JsObject, JsObject::OBJ_TYPE_NUMBER_OBJ)
|
(P::as_ptr(&world.number_proto) as *mut JsObject, JsObject::OBJ_TYPE_NUMBER_OBJ)
|
||||||
} else if val.is_bool() {
|
} else if val.is_bool() {
|
||||||
(P::as_ptr(&world.boolean_proto) as *mut JsObject, JsObject::OBJ_TYPE_BOOLEAN_OBJ)
|
(P::as_ptr(&world.boolean_proto) as *mut JsObject, JsObject::OBJ_TYPE_BOOLEAN_OBJ)
|
||||||
|
} else if val.is_bigint() {
|
||||||
|
(P::as_ptr(&world.bigint_proto) as *mut JsObject, JsObject::OBJ_TYPE_PLAIN)
|
||||||
} else {
|
} else {
|
||||||
(P::as_ptr(&world.object_proto) as *mut JsObject, JsObject::OBJ_TYPE_PLAIN)
|
(P::as_ptr(&world.object_proto) as *mut JsObject, JsObject::OBJ_TYPE_PLAIN)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ const TAG_UNDEFINED: u64 = 3;
|
|||||||
const TAG_OBJECT: u64 = 4;
|
const TAG_OBJECT: u64 = 4;
|
||||||
const TAG_STRING: u64 = 5;
|
const TAG_STRING: u64 = 5;
|
||||||
const TAG_SYMBOL: u64 = 6;
|
const TAG_SYMBOL: u64 = 6;
|
||||||
|
/// BigInt 指针(见 `bigint`/`as_bigint_ptr`)。tag 7 原为 NaN 规范化
|
||||||
|
/// 编码所在,现 NaN 规范化改用普通 quiet NaN 位模式(见 [`JsValue::float`])。
|
||||||
|
const TAG_BIGINT: u64 = 7;
|
||||||
|
|
||||||
/// 48-bit pointer mask (x86-64 canonical VA)
|
/// 48-bit pointer mask (x86-64 canonical VA)
|
||||||
pub const PTR_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
|
pub const PTR_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
|
||||||
@@ -83,6 +86,11 @@ impl PartialEq for JsValue {
|
|||||||
if self.is_symbol() && other.is_symbol() {
|
if self.is_symbol() && other.is_symbol() {
|
||||||
return self.as_symbol_index() == other.as_symbol_index();
|
return self.as_symbol_index() == other.as_symbol_index();
|
||||||
}
|
}
|
||||||
|
if self.is_bigint() && other.is_bigint() {
|
||||||
|
// 按值比较:不同分配但数值相等的 BigInt 视为相等。
|
||||||
|
// SAFETY: bigint 指针由 JsValue::bigint 构造,指向存活的 i128。
|
||||||
|
return unsafe { *self.as_bigint_ptr() == *other.as_bigint_ptr() };
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,10 +116,13 @@ impl JsValue {
|
|||||||
///
|
///
|
||||||
/// NaN 会被规范化为引擎内唯一的安静 NaN 编码,保证
|
/// NaN 会被规范化为引擎内唯一的安静 NaN 编码,保证
|
||||||
/// `float(x).as_double()` 幂等且 `PartialEq` 对 NaN 恒为 false。
|
/// `float(x).as_double()` 幂等且 `PartialEq` 对 NaN 恒为 false。
|
||||||
|
/// 该编码落在 `is_nan_boxed` 范围之外(不占任何 tag),把 tag 7 空间
|
||||||
|
/// 留给 [`bigint`](JsValue::bigint)。
|
||||||
pub fn float(v: f64) -> Self {
|
pub fn float(v: f64) -> Self {
|
||||||
let bits = v.to_bits();
|
let bits = v.to_bits();
|
||||||
if is_nan_bits(bits) {
|
if is_nan_bits(bits) {
|
||||||
Self(QNAN_PREFIX | (7u64 << TAG_SHIFT) | 1)
|
// 指数全 1、尾数非 0 的 quiet NaN,非 NaN-box 前缀(0xFFF8..=0xFFFF)。
|
||||||
|
Self(0x7FF8_0000_0000_0000)
|
||||||
} else {
|
} else {
|
||||||
Self(bits)
|
Self(bits)
|
||||||
}
|
}
|
||||||
@@ -291,6 +302,28 @@ impl JsValue {
|
|||||||
debug_assert!(self.is_symbol());
|
debug_assert!(self.is_symbol());
|
||||||
(self.0 & INT_MASK) as u32
|
(self.0 & INT_MASK) as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 构造 BigInt 值(payload 为堆分配 `i128` 的 48 位指针)。
|
||||||
|
///
|
||||||
|
/// 与 JsString 同机制:i128 本体存于 VM 管理的堆 box,这里携带指针。
|
||||||
|
/// tag 7 原为 NaN 规范化编码,NaN 已改用普通 quiet NaN 位模式,故 tag 7
|
||||||
|
/// 空出给 BigInt。
|
||||||
|
pub fn bigint(ptr: *const i128) -> Self {
|
||||||
|
let addr = ptr as u64;
|
||||||
|
debug_assert!(addr <= PTR_MASK, "bigint pointer must fit in 48 bits");
|
||||||
|
Self(make_tag(TAG_BIGINT) | addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 是否为 BigInt。
|
||||||
|
pub fn is_bigint(&self) -> bool {
|
||||||
|
is_nan_boxed(self.0) && get_tag(self.0) == TAG_BIGINT
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解出 BigInt 底层指针;调用方须先保证 [`is_bigint`](JsValue::is_bigint)。
|
||||||
|
pub fn as_bigint_ptr(&self) -> *const i128 {
|
||||||
|
debug_assert!(self.is_bigint());
|
||||||
|
(self.0 & PTR_MASK) as *const i128
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_nan_bits(bits: u64) -> bool {
|
fn is_nan_bits(bits: u64) -> bool {
|
||||||
@@ -298,7 +331,7 @@ fn is_nan_bits(bits: u64) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_nan_boxed(bits: u64) -> bool {
|
fn is_nan_boxed(bits: u64) -> bool {
|
||||||
(0xFFF8..=0xFFFE).contains(&((bits >> 48) as u16))
|
(0xFFF8..=0xFFFF).contains(&((bits >> 48) as u16))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for JsValue {
|
impl fmt::Display for JsValue {
|
||||||
@@ -330,6 +363,9 @@ impl fmt::Display for JsValue {
|
|||||||
write!(f, "{{string}}")
|
write!(f, "{{string}}")
|
||||||
} else if self.is_symbol() {
|
} else if self.is_symbol() {
|
||||||
write!(f, "Symbol(idx={})", self.as_symbol_index())
|
write!(f, "Symbol(idx={})", self.as_symbol_index())
|
||||||
|
} else if self.is_bigint() {
|
||||||
|
// SAFETY: bigint 指针指向存活的 i128。
|
||||||
|
write!(f, "BigInt({})", unsafe { *self.as_bigint_ptr() })
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{{unknown}}")
|
write!(f, "{{unknown}}")
|
||||||
}
|
}
|
||||||
@@ -359,6 +395,9 @@ impl fmt::Debug for JsValue {
|
|||||||
write!(f, "JsValue(String({:p}))", self.as_string_ptr())
|
write!(f, "JsValue(String({:p}))", self.as_string_ptr())
|
||||||
} else if self.is_symbol() {
|
} else if self.is_symbol() {
|
||||||
write!(f, "JsValue(Symbol(idx={}))", self.as_symbol_index())
|
write!(f, "JsValue(Symbol(idx={}))", self.as_symbol_index())
|
||||||
|
} else if self.is_bigint() {
|
||||||
|
// SAFETY: bigint 指针指向存活的 i128。
|
||||||
|
write!(f, "JsValue(BigInt({}))", unsafe { *self.as_bigint_ptr() })
|
||||||
} else {
|
} else {
|
||||||
write!(f, "JsValue(Unknown)")
|
write!(f, "JsValue(Unknown)")
|
||||||
}
|
}
|
||||||
@@ -427,6 +466,7 @@ mod tests {
|
|||||||
val.is_object(),
|
val.is_object(),
|
||||||
val.is_string(),
|
val.is_string(),
|
||||||
val.is_symbol(),
|
val.is_symbol(),
|
||||||
|
val.is_bigint(),
|
||||||
];
|
];
|
||||||
let count = matched.iter().filter(|&&x| x).count();
|
let count = matched.iter().filter(|&&x| x).count();
|
||||||
assert_eq!(count, 1, "bits={bits:#018x} matched {count} types");
|
assert_eq!(count, 1, "bits={bits:#018x} matched {count} types");
|
||||||
@@ -491,4 +531,30 @@ mod tests {
|
|||||||
assert_eq!(va, JsValue::string(&*a));
|
assert_eq!(va, JsValue::string(&*a));
|
||||||
assert_eq!(unsafe { (*va.as_string_ptr()).as_str() }, unsafe { (*vb.as_string_ptr()).as_str() });
|
assert_eq!(unsafe { (*va.as_string_ptr()).as_str() }, unsafe { (*vb.as_string_ptr()).as_str() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bigint_pointer_roundtrip() {
|
||||||
|
let boxed = Box::new(123_i128);
|
||||||
|
let ptr: *const i128 = &*boxed;
|
||||||
|
let val = JsValue::bigint(ptr);
|
||||||
|
assert!(val.is_bigint());
|
||||||
|
assert_eq!(val.as_bigint_ptr(), ptr);
|
||||||
|
assert_eq!(unsafe { *val.as_bigint_ptr() }, 123);
|
||||||
|
assert!(!val.is_double());
|
||||||
|
assert!(!val.is_int());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bigint_value_equality_by_value() {
|
||||||
|
let a = JsValue::bigint(Box::into_raw(Box::new(7_i128)));
|
||||||
|
let b = JsValue::bigint(Box::into_raw(Box::new(7_i128)));
|
||||||
|
let c = JsValue::bigint(Box::into_raw(Box::new(8_i128)));
|
||||||
|
assert_eq!(a, b);
|
||||||
|
assert_ne!(a, c);
|
||||||
|
unsafe {
|
||||||
|
drop(Box::from_raw(a.as_bigint_ptr() as *mut i128));
|
||||||
|
drop(Box::from_raw(b.as_bigint_ptr() as *mut i128));
|
||||||
|
drop(Box::from_raw(c.as_bigint_ptr() as *mut i128));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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};
|
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),
|
("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),
|
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
|
||||||
("WeakSet", oxide_builtins::stubs::weakset_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),
|
("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),
|
("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` 供快照跟踪。
|
/// 的 stub 构造器绑定到 global,并登记到 `stub_objects` 供快照跟踪。
|
||||||
pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) {
|
pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) {
|
||||||
let builtin_world = Arc::get_mut(&mut session.builtin_world)
|
let builtin_world = Arc::get_mut(&mut session.builtin_world)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
pub mod bind_array;
|
pub mod bind_array;
|
||||||
/// ArrayBuffer 构造器与原型的 native 方法绑定。
|
/// ArrayBuffer 构造器与原型的 native 方法绑定。
|
||||||
pub mod bind_array_buffer;
|
pub mod bind_array_buffer;
|
||||||
|
/// BigInt 构造器与原型的 native 方法绑定。
|
||||||
|
pub mod bind_bigint;
|
||||||
/// Boolean 构造器与原型的 native 方法绑定。
|
/// Boolean 构造器与原型的 native 方法绑定。
|
||||||
pub mod bind_boolean;
|
pub mod bind_boolean;
|
||||||
/// DataView 构造器与原型的 native 方法绑定。
|
/// 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) {
|
fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||||
for (index, (name, native_fn, arg_count)) in [
|
for (index, (name, native_fn, arg_count)) in [
|
||||||
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2),
|
("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),
|
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
|
||||||
("WeakSet", oxide_builtins::stubs::weakset_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),
|
("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_reflect_global(core, session, global);
|
||||||
bind_iterator_global(core, session, global);
|
bind_iterator_global(core, session, global);
|
||||||
bind_stub_globals(core, session, global);
|
bind_stub_globals(core, session, global);
|
||||||
|
bind_bigint::bind_bigint(core, session, global);
|
||||||
bind_global_functions(core, session, global);
|
bind_global_functions(core, session, global);
|
||||||
let global_this = JsValue::from_js_object(global as *mut JsObject);
|
let global_this = JsValue::from_js_object(global as *mut JsObject);
|
||||||
bind_existing_global(core, global, "globalThis", global_this);
|
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) {
|
if dirty.map_or(true, |d| d.stubs) {
|
||||||
bind_stubs::bind_stubs(core, session, global);
|
bind_stubs::bind_stubs(core, session, global);
|
||||||
}
|
}
|
||||||
|
if dirty.map_or(true, |d| d.stubs) {
|
||||||
|
bind_bigint::bind_bigint(core, session, global);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 完整初始化一个 session 的内置对象(全量绑定 + `globalThis` + 快照记录)。
|
/// 完整初始化一个 session 的内置对象(全量绑定 + `globalThis` + 快照记录)。
|
||||||
|
|||||||
@@ -13,8 +13,25 @@ impl Vm {
|
|||||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 + rv.as_int() as f64);
|
self.regs[rd] = JsValue::float(lv.as_int() as f64 + rv.as_int() as f64);
|
||||||
return Ok(());
|
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 lhs = self.coerce_primitive_bounded(lv, false)?;
|
||||||
let rhs = self.coerce_primitive_bounded(rv, 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() {
|
if lhs.is_string() || rhs.is_string() {
|
||||||
let lbytes = if lhs.is_string() { unsafe { (*lhs.as_string_ptr()).len() } } else { 0 };
|
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 };
|
let rbytes = if rhs.is_string() { unsafe { (*rhs.as_string_ptr()).len() } } else { 0 };
|
||||||
@@ -38,7 +55,12 @@ impl Vm {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn dispatch_neg(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
pub(crate) fn dispatch_neg(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||||||
vm_trace!("NEG rd={} r{}={:?}", rd, a, self.regs[a]);
|
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);
|
self.regs[rd] = JsValue::float(-v);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -46,7 +68,12 @@ impl Vm {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn dispatch_unary_plus(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
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]);
|
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);
|
self.regs[rd] = JsValue::float(v);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -59,8 +86,19 @@ impl Vm {
|
|||||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 + rv.as_int() as f64);
|
self.regs[rd] = JsValue::float(lv.as_int() as f64 + rv.as_int() as f64);
|
||||||
return Ok(());
|
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 lhs = self.coerce_primitive_bounded(lv, false)?;
|
||||||
let rhs = self.coerce_primitive_bounded(rv, 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() {
|
if lhs.is_string() || rhs.is_string() {
|
||||||
let lbytes = if lhs.is_string() { unsafe { (*lhs.as_string_ptr()).len() } } else { 0 };
|
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 };
|
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);
|
self.regs[rd] = JsValue::float(lv.as_int() as f64 - rv.as_int() as f64);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let l = self.coerce_number_bounded(lv)?;
|
if lv.is_bigint() && rv.is_bigint() {
|
||||||
let r = self.coerce_number_bounded(rv)?;
|
self.regs[rd] = self.new_bigint(self.bigint_value(lv) - self.bigint_value(rv));
|
||||||
self.regs[rd] = JsValue::float(l - 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() {
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +156,22 @@ impl Vm {
|
|||||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 * rv.as_int() as f64);
|
self.regs[rd] = JsValue::float(lv.as_int() as f64 * rv.as_int() as f64);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let l = self.coerce_number_bounded(lv)?;
|
if lv.is_bigint() && rv.is_bigint() {
|
||||||
let r = self.coerce_number_bounded(rv)?;
|
self.regs[rd] = self.new_bigint(self.bigint_value(lv) * self.bigint_value(rv));
|
||||||
self.regs[rd] = JsValue::float(l * 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() {
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +184,30 @@ impl Vm {
|
|||||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 / rv.as_int() as f64);
|
self.regs[rd] = JsValue::float(lv.as_int() as f64 / rv.as_int() as f64);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let l = self.coerce_number_bounded(lv)?;
|
if lv.is_bigint() && rv.is_bigint() {
|
||||||
let r = self.coerce_number_bounded(rv)?;
|
let r = self.bigint_value(rv);
|
||||||
self.regs[rd] = JsValue::float(l / r);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,9 +220,30 @@ impl Vm {
|
|||||||
self.regs[rd] = JsValue::float(lv.as_int() as f64 % rv.as_int() as f64);
|
self.regs[rd] = JsValue::float(lv.as_int() as f64 % rv.as_int() as f64);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let l = self.coerce_number_bounded(lv)?;
|
if lv.is_bigint() && rv.is_bigint() {
|
||||||
let r = self.coerce_number_bounded(rv)?;
|
let r = self.bigint_value(rv);
|
||||||
self.regs[rd] = JsValue::float(l % r);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ impl Vm {
|
|||||||
vm_trace!("LT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
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 va = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||||
let vb = self.coerce_primitive_bounded(self.regs[b], 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) {
|
match coercion::relational_compare(va, vb) {
|
||||||
Some(r) => self.regs[rd] = JsValue::bool(r),
|
Some(r) => self.regs[rd] = JsValue::bool(r),
|
||||||
None => {
|
None => {
|
||||||
@@ -44,6 +47,9 @@ impl Vm {
|
|||||||
vm_trace!("GT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
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 va = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||||
let vb = self.coerce_primitive_bounded(self.regs[b], 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) {
|
match coercion::relational_compare(vb, va) {
|
||||||
Some(r) => self.regs[rd] = JsValue::bool(r),
|
Some(r) => self.regs[rd] = JsValue::bool(r),
|
||||||
None => {
|
None => {
|
||||||
@@ -59,6 +65,9 @@ impl Vm {
|
|||||||
vm_trace!("LTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
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 va = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||||
let vb = self.coerce_primitive_bounded(self.regs[b], 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) {
|
match coercion::relational_compare(vb, va) {
|
||||||
Some(r) => self.regs[rd] = JsValue::bool(!r),
|
Some(r) => self.regs[rd] = JsValue::bool(!r),
|
||||||
None => {
|
None => {
|
||||||
@@ -74,6 +83,9 @@ impl Vm {
|
|||||||
vm_trace!("GTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
|
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 va = self.coerce_primitive_bounded(self.regs[a], false)?;
|
||||||
let vb = self.coerce_primitive_bounded(self.regs[b], 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) {
|
match coercion::relational_compare(va, vb) {
|
||||||
Some(r) => self.regs[rd] = JsValue::bool(!r),
|
Some(r) => self.regs[rd] = JsValue::bool(!r),
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ impl Vm {
|
|||||||
"string"
|
"string"
|
||||||
} else if val.is_symbol() {
|
} else if val.is_symbol() {
|
||||||
"symbol"
|
"symbol"
|
||||||
|
} else if val.is_bigint() {
|
||||||
|
"bigint"
|
||||||
} else if val.is_object() {
|
} else if val.is_object() {
|
||||||
let obj = unsafe { &*val.as_js_object_ptr() };
|
let obj = unsafe { &*val.as_js_object_ptr() };
|
||||||
if obj.is_function() {
|
if obj.is_function() {
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ impl Vm {
|
|||||||
let proto = unsafe { &*proto_ptr };
|
let proto = unsafe { &*proto_ptr };
|
||||||
return self.ordinary_get(proto, prop_name_si, val).map(Some);
|
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)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,10 +84,40 @@ macro_rules! binary_arith {
|
|||||||
let rv = $self.regs[$b];
|
let rv = $self.regs[$b];
|
||||||
if lv.is_int() && rv.is_int() {
|
if lv.is_int() && rv.is_int() {
|
||||||
$self.regs[$rd] = JsValue::float(lv.as_int() as f64 $op rv.as_int() as f64);
|
$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 {
|
} else {
|
||||||
let l = $self.coerce_number_bounded(lv)?;
|
// 注意:混合 BigInt/Number 检查不能在此前置(对象操作数如
|
||||||
let r = $self.coerce_number_bounded(rv)?;
|
// `{valueOf: () => 2n}` 需先 ToPrimitive 再判定),统一放 coerce 之后。
|
||||||
$self.regs[$rd] = JsValue::float(l $op r);
|
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 {
|
fn new_string(&mut self, s: &str) -> JsValue {
|
||||||
self.new_string(s)
|
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> {
|
fn kernel_core(&self) -> &Arc<KernelCore> {
|
||||||
self.kernel_core()
|
self.kernel_core()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,13 @@
|
|||||||
//!
|
//!
|
||||||
//! 本文件只定义字段;方法随 `Vm` 存放。
|
//! 本文件只定义字段;方法随 `Vm` 存放。
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use rustc_hash::FxBuildHasher;
|
use rustc_hash::FxBuildHasher;
|
||||||
|
|
||||||
use oxide_types::object::{JsObject, JsString};
|
use oxide_types::object::{JsObject, JsString};
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
use crate::session_gc::SessionGc;
|
use crate::session_gc::SessionGc;
|
||||||
use crate::vm::ForInIter;
|
use crate::vm::ForInIter;
|
||||||
|
|
||||||
@@ -30,6 +29,10 @@ pub(crate) struct GcState {
|
|||||||
pub(crate) epoch_object_ptrs: Vec<*mut JsObject>,
|
pub(crate) epoch_object_ptrs: Vec<*mut JsObject>,
|
||||||
pub(crate) session_object_ptrs: Vec<*mut JsObject>,
|
pub(crate) session_object_ptrs: Vec<*mut JsObject>,
|
||||||
pub(crate) session_string_ptrs: Vec<*mut JsString>,
|
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) session_bytes_allocated: usize,
|
||||||
pub(crate) forwarding: HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
pub(crate) forwarding: HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ impl Vm {
|
|||||||
epoch_object_ptrs: Vec::new(),
|
epoch_object_ptrs: Vec::new(),
|
||||||
session_object_ptrs: Vec::new(),
|
session_object_ptrs: Vec::new(),
|
||||||
session_string_ptrs: Vec::new(),
|
session_string_ptrs: Vec::new(),
|
||||||
|
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||||
session_bytes_allocated: 0,
|
session_bytes_allocated: 0,
|
||||||
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
||||||
},
|
},
|
||||||
@@ -171,6 +172,7 @@ impl Vm {
|
|||||||
epoch_object_ptrs: Vec::new(),
|
epoch_object_ptrs: Vec::new(),
|
||||||
session_object_ptrs: Vec::new(),
|
session_object_ptrs: Vec::new(),
|
||||||
session_string_ptrs: Vec::new(),
|
session_string_ptrs: Vec::new(),
|
||||||
|
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||||
session_bytes_allocated: 0,
|
session_bytes_allocated: 0,
|
||||||
forwarding: std::collections::HashMap::with_hasher(rustc_hash::FxBuildHasher),
|
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_bytes_allocated = 0;
|
||||||
self.gc_state.session_gc = crate::session_gc::SessionGc::new();
|
self.gc_state.session_gc = crate::session_gc::SessionGc::new();
|
||||||
self.free_session_string_heap_data();
|
self.free_session_string_heap_data();
|
||||||
|
self.free_session_bigint_heap_data();
|
||||||
self.symbols.reset();
|
self.symbols.reset();
|
||||||
self.root_reg_limit = 0;
|
self.root_reg_limit = 0;
|
||||||
self.active_reg_limit = 0;
|
self.active_reg_limit = 0;
|
||||||
@@ -477,10 +480,39 @@ impl Vm {
|
|||||||
format!("{val}")
|
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 {
|
fn convert_constant(&self, constant: &Constant) -> JsValue {
|
||||||
match constant {
|
match constant {
|
||||||
Constant::Number(v) => JsValue::float(*v),
|
Constant::Number(v) => JsValue::float(*v),
|
||||||
Constant::Int(v) => JsValue::int(*v),
|
Constant::Int(v) => JsValue::int(*v),
|
||||||
|
Constant::BigInt(v) => self.new_bigint(*v),
|
||||||
Constant::String(s) => self.perm_string(s),
|
Constant::String(s) => self.perm_string(s),
|
||||||
Constant::Boolean(b) => JsValue::bool(*b),
|
Constant::Boolean(b) => JsValue::bool(*b),
|
||||||
Constant::Null => JsValue::null(),
|
Constant::Null => JsValue::null(),
|
||||||
@@ -752,4 +784,79 @@ mod tests {
|
|||||||
));
|
));
|
||||||
assert!(!vm.session.is_dirty_since_snapshot());
|
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