mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 10:41:11 +08:00
265 lines
9.7 KiB
Rust
265 lines
9.7 KiB
Rust
use num_bigint::BigInt;
|
||
use oxide_runtime_api::{to_primitive, NativeResult, ToPrimitiveHint, VmHost};
|
||
use oxide_types::value::JsValue;
|
||
|
||
/// JS `BigInt(value)` 构造逻辑(§21.2.1.1):
|
||
/// - new 形态无条件抛 TypeError(BigInt 不可 new);
|
||
/// - 无参走 ToBigInt(undefined) → TypeError;
|
||
/// - Number 参数走 NumberToBigInt(非整数抛 RangeError,幅值无 2^53 上界),
|
||
/// 其余参数走 ToBigInt。
|
||
pub fn bigint_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
if vm.constructing_native() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "BigInt is not a constructor"));
|
||
}
|
||
// 无参 BigInt() 等价于 ToBigInt(undefined) → TypeError,与显式 undefined 同路。
|
||
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
// ToPrimitive(value, number):对象先出盒,用户 @@toPrimitive/valueOf/toString
|
||
// 抛出的异常原样传播,且装箱转换的异常只触发一次。
|
||
let prim = match to_primitive(val, ToPrimitiveHint::Number, vm) {
|
||
Ok(p) => p,
|
||
Err(_) => {
|
||
if let Some(exc) = vm.take_uncaught_value() {
|
||
return NativeResult::Err(exc);
|
||
}
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a BigInt"));
|
||
}
|
||
};
|
||
// 步骤 3:Type(prim) 为 Number → NumberToBigInt。
|
||
if prim.is_int() {
|
||
return NativeResult::Ok(vm.new_bigint(BigInt::from(prim.as_int())));
|
||
}
|
||
if prim.is_double() {
|
||
let d = prim.as_double();
|
||
if d.is_nan() || d.is_infinite() || d.trunc() != d {
|
||
return NativeResult::Err(crate::error::create_range_error(
|
||
vm,
|
||
"The number cannot be converted to a BigInt because it is not an integer",
|
||
));
|
||
}
|
||
return NativeResult::Ok(vm.new_bigint(integer_double_to_bigint(d)));
|
||
}
|
||
// 步骤 4:其它原始类型走 ToBigInt。
|
||
match to_bigint(vm, prim) {
|
||
Ok(v) => NativeResult::Ok(v),
|
||
Err(e) => NativeResult::Err(e),
|
||
}
|
||
}
|
||
|
||
/// NumberToBigInt 步骤 3:整数 double 的精确值 ℤ(x)。
|
||
/// |x| < 2^53 由尾数右移指数位直转;|x| ≥ 2^53 位分解(尾数左移 exp-1075 位),
|
||
/// 全程精确无饱和。-0 返回 0(BigInt 无负零)。
|
||
fn integer_double_to_bigint(d: f64) -> BigInt {
|
||
let bits = d.to_bits();
|
||
let negative = bits >> 63 != 0;
|
||
let exp_field = (bits >> 52) & 0x7FF;
|
||
let significand = bits & 0xF_FFFF_FFFF_FFFF;
|
||
if exp_field == 0 {
|
||
return BigInt::from(0);
|
||
}
|
||
let mag = if exp_field < 1075 {
|
||
BigInt::from((0x10_0000_0000_0000u64 + significand) >> (1075 - exp_field))
|
||
} else {
|
||
BigInt::from(0x10_0000_0000_0000u64 + significand) << (exp_field - 1075)
|
||
};
|
||
if negative {
|
||
-mag
|
||
} else {
|
||
mag
|
||
}
|
||
}
|
||
|
||
/// `BigInt.prototype.toString(radix)`:thisBigIntValue 后按 radix 输出。
|
||
pub fn bigint_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = vm.reg(args[0]);
|
||
let v = match this_bigint_value(vm, this_val) {
|
||
Ok(v) => v,
|
||
Err(e) => return NativeResult::Err(e),
|
||
};
|
||
let radix = if args.len() > 1 {
|
||
let radix_arg = vm.reg(args[1]);
|
||
if radix_arg.is_undefined() {
|
||
10u32
|
||
} else {
|
||
// radix 走 ToIntegerOrInfinity:ToNumber 拒绝 Symbol/BigInt,
|
||
// 对象先 ToPrimitive(用户异常原样传播),NaN/±0 归 0。
|
||
let raw = match coerce_number_or_throw(vm, radix_arg) {
|
||
Ok(n) => n,
|
||
Err(e) => return NativeResult::Err(e),
|
||
};
|
||
let r = if raw.is_nan() || raw == 0.0 {
|
||
0.0
|
||
} else if raw.is_infinite() {
|
||
raw
|
||
} else {
|
||
raw.trunc()
|
||
};
|
||
if !(2.0..=36.0).contains(&r) {
|
||
return NativeResult::Err(crate::error::create_range_error(
|
||
vm,
|
||
"toString() radix must be between 2 and 36",
|
||
));
|
||
}
|
||
r as u32
|
||
}
|
||
} else {
|
||
10u32
|
||
};
|
||
let text = vm.bigint_value(v).to_str_radix(radix);
|
||
NativeResult::Ok(vm.new_string(&text))
|
||
}
|
||
|
||
/// `BigInt.prototype.toLocaleString`:返回与 toString() 相同的十进制字符串。
|
||
pub fn bigint_to_locale_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
bigint_to_string(vm, args)
|
||
}
|
||
|
||
/// `BigInt.prototype.valueOf`:返回包装的 BigInt 原始值。
|
||
pub fn bigint_value_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = vm.reg(args[0]);
|
||
match this_bigint_value(vm, this_val) {
|
||
Ok(v) => NativeResult::Ok(v),
|
||
Err(e) => NativeResult::Err(e),
|
||
}
|
||
}
|
||
|
||
/// `BigInt.asIntN(bits, bigint)`:ToIndex(bits) + ToBigInt(bigint),
|
||
/// 结果按二进制补码截断为 bits 位有符号整数。
|
||
pub fn bigint_as_int_n<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let bits = match to_index(vm, args) {
|
||
Ok(b) => b,
|
||
Err(e) => return NativeResult::Err(e),
|
||
};
|
||
let arg = bigint_arg(vm, args);
|
||
let val = match to_bigint(vm, arg) {
|
||
Ok(v) => v,
|
||
Err(e) => return NativeResult::Err(e),
|
||
};
|
||
let v = vm.bigint_value(val);
|
||
if bits == 0 {
|
||
return NativeResult::Ok(vm.new_bigint(BigInt::from(0)));
|
||
}
|
||
let modulus = BigInt::from(1) << bits;
|
||
let half = BigInt::from(1) << (bits - 1);
|
||
let mut m = v % &modulus;
|
||
if m.sign() == num_bigint::Sign::Minus {
|
||
m += &modulus;
|
||
}
|
||
if m >= half {
|
||
return NativeResult::Ok(vm.new_bigint(m - modulus));
|
||
}
|
||
NativeResult::Ok(vm.new_bigint(m))
|
||
}
|
||
|
||
/// `BigInt.asUintN(bits, bigint)`:按 `2^bits` 取模的无符号截断。
|
||
pub fn bigint_as_uint_n<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let bits = match to_index(vm, args) {
|
||
Ok(b) => b,
|
||
Err(e) => return NativeResult::Err(e),
|
||
};
|
||
let arg = bigint_arg(vm, args);
|
||
let val = match to_bigint(vm, arg) {
|
||
Ok(v) => v,
|
||
Err(e) => return NativeResult::Err(e),
|
||
};
|
||
let v = vm.bigint_value(val);
|
||
if bits == 0 {
|
||
return NativeResult::Ok(vm.new_bigint(BigInt::from(0)));
|
||
}
|
||
let modulus = BigInt::from(1) << bits;
|
||
let mut m = v % &modulus;
|
||
if m.sign() == num_bigint::Sign::Minus {
|
||
m += &modulus;
|
||
}
|
||
NativeResult::Ok(vm.new_bigint(m))
|
||
}
|
||
|
||
/// thisBigIntValue:BigInt 原样返回;包装对象解盒;其它类型 TypeError。
|
||
fn this_bigint_value<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<JsValue, JsValue> {
|
||
if this_val.is_bigint() {
|
||
return 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.boxed_value();
|
||
if v.is_bigint() {
|
||
return Ok(v);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Err(crate::error::create_type_error(
|
||
vm,
|
||
"BigInt.prototype method called on incompatible receiver",
|
||
))
|
||
}
|
||
|
||
/// ToNumber 且拒绝 Symbol/BigInt(ToIntegerOrInfinity 的 ToNumber 语义)。
|
||
fn coerce_number_or_throw<H: VmHost>(vm: &mut H, value: JsValue) -> Result<f64, JsValue> {
|
||
let prim = match to_primitive(value, ToPrimitiveHint::Number, vm) {
|
||
Ok(p) => p,
|
||
Err(_) => {
|
||
if let Some(exc) = vm.take_uncaught_value() {
|
||
return Err(exc);
|
||
}
|
||
return Err(crate::error::create_type_error(vm, "Cannot convert value to a number"));
|
||
}
|
||
};
|
||
if prim.is_symbol() || prim.is_bigint() {
|
||
return Err(crate::error::create_type_error(vm, "Cannot convert a Symbol or BigInt value to a number"));
|
||
}
|
||
Ok(oxide_runtime_api::to_number(prim))
|
||
}
|
||
|
||
/// ToIndex:ToIntegerOrInfinity 后必须为有限且 `[0, 2^53-1]` 内的整数。
|
||
fn to_index<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<usize, JsValue> {
|
||
// ToIndex 作用在第一个参数(bits)上;缺省为 undefined。
|
||
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
let n = coerce_number_or_throw(vm, value)?;
|
||
if n.is_nan() {
|
||
return Ok(0);
|
||
}
|
||
// ToIntegerOrInfinity 先截断(-0.9 → -0,不越界);再检查有限且 [0, 2^53-1]。
|
||
let int = if n == 0.0 {
|
||
0.0
|
||
} else if n.is_infinite() {
|
||
n
|
||
} else {
|
||
n.trunc()
|
||
};
|
||
if !(0.0..=9007199254740991.0).contains(&int) {
|
||
return Err(crate::error::create_range_error(vm, "Invalid index"));
|
||
}
|
||
Ok(int as usize)
|
||
}
|
||
|
||
/// 取 asIntN/asUintN 的第二个参数;缺省为 undefined。
|
||
fn bigint_arg<H: VmHost>(vm: &mut H, args: &[u8]) -> JsValue {
|
||
if args.len() > 2 {
|
||
vm.reg(args[2])
|
||
} else {
|
||
JsValue::undefined()
|
||
}
|
||
}
|
||
|
||
/// ToBigInt 抽象操作:BigInt 原样;String → StringToBigInt;
|
||
/// Boolean → 0/1;Number/Symbol/undefined/null → TypeError;对象先 ToPrimitive 再递归。
|
||
///
|
||
/// 语义主体在 `oxide_runtime_api::to_bigint_full`,此处只把 String 错误恢复为
|
||
/// 对应 kind 的异常对象(用户回调抛出的原始值经 last_uncaught_value 透传)。
|
||
fn to_bigint<H: VmHost>(vm: &mut H, val: JsValue) -> Result<JsValue, JsValue> {
|
||
match oxide_runtime_api::to_bigint_full(val, vm) {
|
||
Ok(v) => Ok(v),
|
||
Err(e) => Err(vm
|
||
.take_uncaught_value()
|
||
.unwrap_or_else(|| crate::error::create_from_text(vm, &e))),
|
||
}
|
||
}
|