mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 10:41:11 +08:00
fix: 补 Error 族原型与 Math 六函数边及 Symbol 面描述符
This commit is contained in:
@@ -1,20 +1,30 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_runtime_api::{to_string, to_string_full, NativeResult, VmHost};
|
||||
use oxide_runtime_api::{to_string_full, NativeResult, VmHost};
|
||||
use oxide_types::mem::P;
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
fn set_own_message<H: VmHost>(host: &mut H, this: *mut JsObject, args: &[u8]) {
|
||||
fn set_own_message<H: VmHost>(host: &mut H, this: *mut JsObject, args: &[u8]) -> NativeResult {
|
||||
if args.len() <= 1 {
|
||||
return;
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
}
|
||||
let msg_val = host.reg(args[1]);
|
||||
if msg_val.is_undefined() {
|
||||
return;
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
}
|
||||
let msg_str = to_string(msg_val);
|
||||
// 完整 ToString:对象经 ToPrimitive(string hint),Symbol 抛 TypeError,
|
||||
// 用户转换方法抛出的异常原样传播。
|
||||
let msg_str = match to_string_full(msg_val, host) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
if let Some(exc) = host.take_uncaught_value() {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
return NativeResult::Err(create_type_error(host, "Cannot convert value to a string"));
|
||||
}
|
||||
};
|
||||
let sf = Arc::clone(host.kernel_core().perm_interner());
|
||||
let sh = Arc::clone(host.kernel_core().shape_forge());
|
||||
let si = sf.intern("message").0;
|
||||
@@ -26,6 +36,7 @@ fn set_own_message<H: VmHost>(host: &mut H, this: *mut JsObject, args: &[u8]) {
|
||||
// message 按规范为非枚举数据属性(CreateNonEnumerableDataPropertyOrThrow)。
|
||||
(*this).set_data_meta(pos, PropAttributes::new(true, false, true));
|
||||
}
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// 按错误类型名创建对应 Error 对象(message 非空时写为自身属性)。
|
||||
@@ -170,7 +181,14 @@ macro_rules! error_ctor {
|
||||
unsafe {
|
||||
(*this).type_tag = JsObject::OBJ_TYPE_ERROR;
|
||||
}
|
||||
set_own_message(host, this, args);
|
||||
if let NativeResult::Err(e) = set_own_message(host, this, args) {
|
||||
return NativeResult::Err(e);
|
||||
}
|
||||
// InstallErrorCause:message 转换之后、构造收尾之前。
|
||||
let options = if args.len() > 2 { host.reg(args[2]) } else { JsValue::undefined() };
|
||||
if let NativeResult::Err(e) = install_error_cause(host, this, options) {
|
||||
return NativeResult::Err(e);
|
||||
}
|
||||
NativeResult::Ok(JsValue::from_js_object(this))
|
||||
}
|
||||
};
|
||||
@@ -198,6 +216,30 @@ fn set_own_data_prop<H: VmHost>(host: &mut H, obj: *mut JsObject, key: &str, val
|
||||
}
|
||||
}
|
||||
|
||||
/// InstallErrorCause:options 为 Object 且 HasProperty(options, "cause") 时,
|
||||
/// Get(options, "cause") 并写入 O 的非枚举数据属性。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - options 非对象或无 "cause" 属性(含链上)→ 不写属性;
|
||||
/// - "cause" 访问器 getter 抛出的异常原样传播。
|
||||
pub fn install_error_cause<H: VmHost>(host: &mut H, obj: *mut JsObject, options: JsValue) -> NativeResult {
|
||||
if !options.is_object() {
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
}
|
||||
// SAFETY: is_object 保证指针非空且对象本 session 存活。
|
||||
let opts = unsafe { &*options.as_js_object_ptr() };
|
||||
let si = host.kernel_core().perm_interner().intern("cause").0;
|
||||
if host.resolve_property(opts, si).is_none() {
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
}
|
||||
let cause = match host.ordinary_get(opts, si, options) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(crate::iterator::engine_error(host, &e)),
|
||||
};
|
||||
set_own_data_prop(host, obj, "cause", cause);
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `SuppressedError(error, suppressed, message)` 构造器:三参,length=3。
|
||||
///
|
||||
/// 以规范 CreateSuppressedError 语义建对象,三字段属性按 message → error
|
||||
|
||||
@@ -2,7 +2,7 @@ use num_bigint::BigInt;
|
||||
use num_traits::{FromPrimitive, One, Signed, ToPrimitive, Zero};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use oxide_runtime_api::{NativeResult, VmHost};
|
||||
use oxide_runtime_api::{to_number_full, NativeResult, VmHost};
|
||||
|
||||
fn num<H: VmHost>(vm: &mut H, reg: u8) -> f64 {
|
||||
vm.coerce_number_bounded(vm.reg(reg)).unwrap_or(f64::NAN)
|
||||
@@ -76,11 +76,37 @@ pub fn math_atan2<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(a.atan2(b)))
|
||||
}
|
||||
|
||||
/// `Math.round`:四舍五入到最接近的整数,.5 时向正无穷取整(JS 语义)。
|
||||
/// `Math.round`:舍入到最接近的整数,.5 时向正无穷取整。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. NaN → NaN;±∞ → 原值。
|
||||
/// 2. |x| ≥ 2^52 时 x 已是整数,恒等返回(免浮点加 0.5 在指数区丢精度)。
|
||||
/// 3. 其余 |x| < 2^52:`floor(x + 0.5)`(该区间 x+0.5 精确可表示),
|
||||
/// 结果为 0 且 x 带负号时返回 -0(规范 -0.5 与 -0 臂)。
|
||||
pub fn math_round<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let x = arg1(vm, args);
|
||||
let r = if x < 0.0 { (x - 0.5).ceil() } else { (x + 0.5).floor() };
|
||||
NativeResult::Ok(JsValue::float(r))
|
||||
NativeResult::Ok(JsValue::float(round_f64(x)))
|
||||
}
|
||||
|
||||
/// 规范 `Math.round` 数值核心(21.1.3.25):|x| ≥ 2^52 恒等,其余 `floor(x+0.5)`,
|
||||
/// 零结果按 x 符号定 ±0。
|
||||
fn round_f64(x: f64) -> f64 {
|
||||
if x.is_nan() {
|
||||
return f64::NAN;
|
||||
}
|
||||
if x.is_infinite() {
|
||||
return x;
|
||||
}
|
||||
// 2^52:该界上 x 必为整数,round 即恒等。
|
||||
const TWO_POW_52: f64 = 4_503_599_627_370_496.0;
|
||||
if x >= TWO_POW_52 || x <= -TWO_POW_52 {
|
||||
return x;
|
||||
}
|
||||
let r = (x + 0.5).floor();
|
||||
if r == 0.0 && x.is_sign_negative() {
|
||||
return -0.0;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// `Math.sign`:返回 1 / -1 / 0 / -0 / NaN。
|
||||
@@ -97,9 +123,21 @@ pub fn math_sign<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Math.clz32`:返回 32 位无符号整数表示的前导零个数。
|
||||
/// 规范 ToUint32(§7.1.10):NaN/±∞ → 0,其余按 2^32 取模归入 [0, 2^32)。
|
||||
fn to_uint32(x: f64) -> u32 {
|
||||
if x.is_nan() || x.is_infinite() {
|
||||
return 0;
|
||||
}
|
||||
let mut n = x % 4_294_967_296.0;
|
||||
if n < 0.0 {
|
||||
n += 4_294_967_296.0;
|
||||
}
|
||||
n as u32
|
||||
}
|
||||
|
||||
/// `Math.clz32`:返回 ToUint32(x) 的 32 位无符号表示的前导零个数。
|
||||
pub fn math_clz32<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let n = arg1(vm, args) as u32;
|
||||
let n = to_uint32(arg1(vm, args));
|
||||
NativeResult::Ok(JsValue::int(n.leading_zeros() as i32))
|
||||
}
|
||||
|
||||
@@ -212,57 +250,180 @@ pub fn math_f16round<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::float(f16_bits_to_f64(f64_to_f16_bits(arg1(vm, args)))))
|
||||
}
|
||||
|
||||
/// `Math.hypot`:返回 sqrt(a²+b²)(当前仅支持两个参数)。
|
||||
/// `Math.hypot`:返回实参平方和的平方根(sqrt(Σx²)),Kahan 求和免溢出/下溢。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 逐实参 ToNumber(传播异常;首个错误即停,后续实参不再求值)。
|
||||
/// 2. 无实参 → +0;任一 ±∞ → +∞(先于 NaN 判定);任一 NaN → NaN。
|
||||
/// 3. 按 max(|x|) 缩放后 Kahan 累加平方,全零 → +0。
|
||||
pub fn math_hypot<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let (a, b) = arg2(vm, args);
|
||||
NativeResult::Ok(JsValue::float(a.hypot(b)))
|
||||
let n = vm.native_arg_count(args);
|
||||
let mut vals: Vec<f64> = Vec::with_capacity(n);
|
||||
for i in 0..n {
|
||||
let v = vm.native_arg_at(args, i);
|
||||
match to_number_full(v, vm) {
|
||||
Ok(x) => vals.push(x),
|
||||
Err(e) => return NativeResult::Err(crate::iterator::engine_error(vm, &e)),
|
||||
}
|
||||
}
|
||||
if vals.is_empty() {
|
||||
return NativeResult::Ok(JsValue::float(0.0));
|
||||
}
|
||||
let mut max = 0.0f64;
|
||||
let mut has_nan = false;
|
||||
for &x in &vals {
|
||||
if x.is_infinite() {
|
||||
return NativeResult::Ok(JsValue::float(f64::INFINITY));
|
||||
}
|
||||
if x.is_nan() {
|
||||
has_nan = true;
|
||||
}
|
||||
let a = x.abs();
|
||||
if a > max {
|
||||
max = a;
|
||||
}
|
||||
}
|
||||
if has_nan {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
if max == 0.0 {
|
||||
return NativeResult::Ok(JsValue::float(0.0));
|
||||
}
|
||||
// Kahan 求和:补偿平方和的舍入误差。
|
||||
let mut sum = 0.0f64;
|
||||
let mut comp = 0.0f64;
|
||||
for &x in &vals {
|
||||
let n = x / max;
|
||||
let y = n * n - comp;
|
||||
let t = sum + y;
|
||||
comp = (t - sum) - y;
|
||||
sum = t;
|
||||
}
|
||||
NativeResult::Ok(JsValue::float(sum.sqrt() * max))
|
||||
}
|
||||
|
||||
/// `Math.imul`:按 32 位整数做 wrap-around 乘法。
|
||||
pub fn math_imul<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let (a, b) = arg2(vm, args);
|
||||
NativeResult::Ok(JsValue::int((a as i32).wrapping_mul(b as i32)))
|
||||
let product = (to_uint32(a) as u64).wrapping_mul(to_uint32(b) as u64) & 0xFFFF_FFFF;
|
||||
NativeResult::Ok(JsValue::int(product as i32))
|
||||
}
|
||||
|
||||
/// `Math.pow(base, exp)`:返回幂运算结果。
|
||||
pub fn math_pow<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let (a, b) = arg2(vm, args);
|
||||
NativeResult::Ok(JsValue::float(a.powf(b)))
|
||||
NativeResult::Ok(JsValue::float(pow_f64(a, b)))
|
||||
}
|
||||
|
||||
/// `Math.max`:返回参数中的最大值;任一无参时返回 -Infinity,任一 NaN 时返回 NaN。
|
||||
/// 规范 `Math.pow` 数值核心(Applying the `**` operator):特殊值
|
||||
/// (NaN/±∞/±0/±1)按规范臂判定,一般情形委托 IEEE 幂运算。
|
||||
fn pow_f64(a: f64, b: f64) -> f64 {
|
||||
if b.is_nan() || a.is_nan() {
|
||||
return f64::NAN;
|
||||
}
|
||||
if a.is_infinite() {
|
||||
return if b == 0.0 { 1.0 } else if b > 0.0 { f64::INFINITY } else { 0.0 };
|
||||
}
|
||||
if a == 0.0 {
|
||||
// 保 a 的符号:+0^+b → +0,-0^+b → -0。
|
||||
return if b > 0.0 { a } else if b < 0.0 { f64::INFINITY } else { 1.0 };
|
||||
}
|
||||
if a == 1.0 {
|
||||
return if b.is_infinite() { f64::NAN } else { 1.0 };
|
||||
}
|
||||
if a == -1.0 {
|
||||
if b.is_infinite() {
|
||||
return f64::NAN;
|
||||
}
|
||||
if b.fract() == 0.0 {
|
||||
return if b % 2.0 == 0.0 { 1.0 } else { -1.0 };
|
||||
}
|
||||
return f64::NAN;
|
||||
}
|
||||
if a < 0.0 {
|
||||
return if b.fract() == 0.0 { a.powf(b) } else { f64::NAN };
|
||||
}
|
||||
a.powf(b)
|
||||
}
|
||||
|
||||
/// 变长实参 Math 函数(max/min)的公共前段:逐实参 ToNumber 成列表
|
||||
/// (传播异常,全部元素先转换再比较)。
|
||||
fn to_number_all<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<Vec<f64>, JsValue> {
|
||||
let n = vm.native_arg_count(args);
|
||||
let mut vals: Vec<f64> = Vec::with_capacity(n);
|
||||
for i in 0..n {
|
||||
let v = vm.native_arg_at(args, i);
|
||||
match to_number_full(v, vm) {
|
||||
Ok(x) => vals.push(x),
|
||||
Err(e) => return Err(crate::iterator::engine_error(vm, &e)),
|
||||
}
|
||||
}
|
||||
Ok(vals)
|
||||
}
|
||||
|
||||
/// `Math.max`:返回实参中的最大值;无实参返回 -Infinity,任一 NaN 返回 NaN。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 全部实参 ToNumber(传播异常)。
|
||||
/// 2. 任一 NaN → NaN。
|
||||
/// 3. +0 视为大于 -0:结果为 0 且出现过 +0 时返回 +0。
|
||||
pub fn math_max<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
let vals = match to_number_all(vm, args) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
if vals.is_empty() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NEG_INFINITY));
|
||||
}
|
||||
let mut m = f64::NEG_INFINITY;
|
||||
for &r in args.iter().skip(1) {
|
||||
let x = num(vm, r);
|
||||
let mut has_plus_zero = false;
|
||||
for &x in &vals {
|
||||
if x.is_nan() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
if x == 0.0 && !x.is_sign_negative() {
|
||||
has_plus_zero = true;
|
||||
}
|
||||
if x > m {
|
||||
m = x;
|
||||
}
|
||||
}
|
||||
if m == 0.0 && has_plus_zero {
|
||||
m = 0.0;
|
||||
}
|
||||
NativeResult::Ok(JsValue::float(m))
|
||||
}
|
||||
|
||||
/// `Math.min`:返回参数中的最小值;无参时返回 Infinity,任一 NaN 时返回 NaN。
|
||||
/// `Math.min`:返回实参中的最小值;无实参返回 Infinity,任一 NaN 返回 NaN。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 全部实参 ToNumber(传播异常)。
|
||||
/// 2. 任一 NaN → NaN。
|
||||
/// 3. +0 视为大于 -0:结果为 0 且出现过 -0 时返回 -0。
|
||||
pub fn math_min<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
let vals = match to_number_all(vm, args) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
if vals.is_empty() {
|
||||
return NativeResult::Ok(JsValue::float(f64::INFINITY));
|
||||
}
|
||||
let mut m = f64::INFINITY;
|
||||
for &r in args.iter().skip(1) {
|
||||
let x = num(vm, r);
|
||||
let mut has_minus_zero = false;
|
||||
for &x in &vals {
|
||||
if x.is_nan() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
if x == 0.0 && x.is_sign_negative() {
|
||||
has_minus_zero = true;
|
||||
}
|
||||
if x < m {
|
||||
m = x;
|
||||
}
|
||||
}
|
||||
if m == 0.0 && has_minus_zero {
|
||||
m = -0.0;
|
||||
}
|
||||
NativeResult::Ok(JsValue::float(m))
|
||||
}
|
||||
|
||||
|
||||
@@ -1845,23 +1845,13 @@ pub fn object_proto_lookup_setter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeR
|
||||
lookup_accessor_field(vm, args, false)
|
||||
}
|
||||
|
||||
/// 沿原型链判断是否为 Error 家族对象:对象自身带 `OBJ_TYPE_ERROR` 标签,或原型链上
|
||||
/// 含 Error.prototype(等价 [[ErrorData]] 内部槽语义,覆盖 Error 用户子类)。
|
||||
fn is_error_family<H: VmHost>(vm: &H, ptr: *mut JsObject) -> bool {
|
||||
let error_proto_ptr = vm.session().builtin_world().error_proto.as_ptr() as *mut JsObject;
|
||||
let mut cur = ptr;
|
||||
while !cur.is_null() {
|
||||
if std::ptr::eq(cur, error_proto_ptr) {
|
||||
return true;
|
||||
}
|
||||
// SAFETY: cur 沿原型链遍历,链上每个节点都是合法 JsObject(proto 非对象时为空指针终止)。
|
||||
let o = unsafe { &*cur };
|
||||
if o.is_error_obj() {
|
||||
return true;
|
||||
}
|
||||
cur = o.proto().as_js_object_ptr();
|
||||
}
|
||||
false
|
||||
/// 判断是否为 Error 家族对象:只看对象自身的 `OBJ_TYPE_ERROR` 标签(等价
|
||||
/// [[ErrorData]] 内部槽语义)。原型本体与 `Object.create(Error.prototype)`
|
||||
/// 伪错误无标签,均按普通对象品牌。
|
||||
fn is_error_family(ptr: *mut JsObject) -> bool {
|
||||
// SAFETY: 调用方保证 ptr 为本 session 存活对象。
|
||||
let o = unsafe { &*ptr };
|
||||
o.is_error_obj()
|
||||
}
|
||||
|
||||
/// `Object.prototype.toString`:返回 `[object Tag]`。
|
||||
@@ -1911,7 +1901,7 @@ pub fn object_proto_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
"TypedArray"
|
||||
} else if obj.is_arguments_obj() {
|
||||
"Arguments"
|
||||
} else if is_error_family(vm, obj_val.as_js_object_ptr()) {
|
||||
} else if is_error_family(obj_val.as_js_object_ptr()) {
|
||||
"Error"
|
||||
} else {
|
||||
"Object"
|
||||
|
||||
@@ -95,6 +95,44 @@ pub fn symbol_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(vm.new_string_owned(result))
|
||||
}
|
||||
|
||||
/// `Symbol.prototype[Symbol.toPrimitive](hint)`:thisSymbolValue 校验后按
|
||||
/// hint 返回 Symbol 或其描述串。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. this 非 Symbol 或 Symbol 包装对象 → TypeError。
|
||||
/// 2. hint 缺省 → 返回 Symbol。
|
||||
/// 3. hint 做完整 ToString(异常传播);结果串为 "string" → 返回
|
||||
/// `Symbol(description)` 描述串,其余值(含 "number")→ 返回 Symbol。
|
||||
pub fn symbol_to_primitive<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(args[0]);
|
||||
let sym = match this_symbol_value(vm, this_val) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return NativeResult::Err(e),
|
||||
};
|
||||
if args.len() <= 1 {
|
||||
return NativeResult::Ok(sym);
|
||||
}
|
||||
let hint = vm.reg(args[1]);
|
||||
if hint.is_undefined() {
|
||||
return NativeResult::Ok(sym);
|
||||
}
|
||||
let hint_str = match oxide_runtime_api::to_string_full(hint, vm) {
|
||||
Ok(s) => s,
|
||||
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 string"));
|
||||
}
|
||||
};
|
||||
if hint_str == "string" {
|
||||
let idx = sym.as_symbol_index();
|
||||
let desc = vm.symbol_description(idx).unwrap_or("").to_string();
|
||||
return NativeResult::Ok(vm.new_string_owned(format!("Symbol({})", desc)));
|
||||
}
|
||||
NativeResult::Ok(sym)
|
||||
}
|
||||
|
||||
/// `Symbol.prototype.valueOf`:Symbol 原样返回;包装对象解盒返回其原始 Symbol。
|
||||
pub fn symbol_value_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
match this_symbol_value(vm, vm.reg(args[0])) {
|
||||
|
||||
@@ -216,6 +216,14 @@ impl BuiltinWorld {
|
||||
let name_pos = proto.push_prop(name_val);
|
||||
// 子类型原型上的 name 同 Error.prototype.name:非枚举数据属性。
|
||||
proto.set_data_meta(name_pos, oxide_types::object::PropAttributes::new(true, false, true));
|
||||
// 子类型原型上的 message 初始值为空串,描述符同 name。
|
||||
let si_message = string_forge.intern("message").0;
|
||||
let empty_si = string_forge.intern("").0;
|
||||
let empty_val = JsValue::perm_string(string_forge.string_ptr(empty_si));
|
||||
let msg_shape = shape_forge.make_shape(proto.shape_id(), si_message);
|
||||
proto.set_shape_id(msg_shape);
|
||||
let msg_pos = proto.push_prop(empty_val);
|
||||
proto.set_data_meta(msg_pos, oxide_types::object::PropAttributes::new(true, false, true));
|
||||
}
|
||||
|
||||
/// 把 String 家族方法安装到 String 构造器与原型上。
|
||||
|
||||
@@ -7,7 +7,8 @@ use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use super::construct::{
|
||||
builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, tag_number_proto, tag_string_proto,
|
||||
builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, tag_boolean_proto, tag_number_proto,
|
||||
tag_string_proto,
|
||||
wire_builtin_world_links, ErrorSubtypeProtos, TypedArrayFamily,
|
||||
};
|
||||
use super::BuiltinWorld;
|
||||
@@ -196,7 +197,11 @@ impl BuiltinWorld {
|
||||
(current.number_proto.clone(), current.number_constructor.clone())
|
||||
};
|
||||
let (boolean_proto, boolean_constructor) = if dirty.boolean {
|
||||
make_named_pair(string_forge, shape_forge, labels, "Boolean")
|
||||
// 脏重建与全量构造同形:原型本体须带 Boolean 对象 tag 与 false 包值,
|
||||
// 漏此分支 full_reset 后 tag 翻回 "Object"。
|
||||
let (proto, ctor) = make_named_pair(string_forge, shape_forge, labels, "Boolean");
|
||||
tag_boolean_proto(&proto);
|
||||
(proto, ctor)
|
||||
} else {
|
||||
(current.boolean_proto.clone(), current.boolean_constructor.clone())
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::bindings::{apply_binding_table, bind_accessor_getter, configure_native_constructor};
|
||||
use crate::bindings::{apply_binding_table, bind_accessor_getter, bind_well_known_method, configure_native_constructor};
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::private_key::{make_well_known_symbol_key, WELL_KNOWN_SYMBOL_TO_PRIMITIVE};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::bind_constructor;
|
||||
@@ -45,6 +46,22 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
oxide_builtins::symbol::symbol_description_getter::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
|
||||
// [Symbol.toPrimitive] 方法:bind 默认描述符可写,规范为不可写,绑定后改 meta。
|
||||
bind_well_known_method(
|
||||
session.builtin_world(),
|
||||
core,
|
||||
proto,
|
||||
WELL_KNOWN_SYMBOL_TO_PRIMITIVE,
|
||||
"[Symbol.toPrimitive]",
|
||||
oxide_builtins::symbol::symbol_to_primitive::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
);
|
||||
let to_prim_key = make_well_known_symbol_key(WELL_KNOWN_SYMBOL_TO_PRIMITIVE);
|
||||
if let Some(pos) = core.shape_forge().lookup_position(proto.shape_id(), to_prim_key) {
|
||||
proto.set_data_meta(pos, PropAttributes::new(false, false, true));
|
||||
proto.bump_generation();
|
||||
}
|
||||
|
||||
// well-known symbol 以符号原语绑定,下标与内建符号表(0..WELL_KNOWN_SYMBOL_COUNT)
|
||||
// 一一对应;名称表是 id/名映射的唯一来源,属性名去掉 `Symbol.` 前缀。
|
||||
for (id, full_name) in oxide_types::private_key::WELL_KNOWN_SYMBOL_NAMES.iter().enumerate() {
|
||||
@@ -60,5 +77,8 @@ fn bind_well_known_symbol(core: &Arc<KernelCore>, ctor: &mut JsObject, name: &st
|
||||
let shape_id = core.shape_forge().make_shape(ctor.shape_id(), si);
|
||||
ctor.set_shape_id(shape_id);
|
||||
ctor.ensure_hash_props().push(val);
|
||||
// well-known symbol 属性按规范 { writable:false, enumerable:false, configurable:false }。
|
||||
let pos = ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||
ctor.set_data_meta(pos, PropAttributes::new(false, false, false));
|
||||
ctor.bump_generation();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,17 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
/// SET 路径的接收者解析:对象直取;非对象经 ToObject 自动装箱
|
||||
/// (null/undefined 抛 TypeError,其余原始值写盒后丢弃盒,与规范
|
||||
/// OrdinarySetWithOwnDescriptor 的 ToObject 基语义一致)。
|
||||
fn set_prop_base(&mut self, receiver: JsValue) -> Result<Option<*mut JsObject>, String> {
|
||||
if receiver.is_object() {
|
||||
return self.checked_object_ptr(receiver, "Cannot create property on non-object");
|
||||
}
|
||||
let boxed = coercion::to_object(receiver, self)?;
|
||||
Ok(Some(boxed.as_js_object_ptr()))
|
||||
}
|
||||
|
||||
fn private_key_from_reg(&self, reg: usize) -> u32 {
|
||||
let value = self.regs[reg];
|
||||
let local_id = if value.is_int() {
|
||||
@@ -401,7 +412,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_ic_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "Cannot create property on non-object")? else {
|
||||
let Some(obj_ptr) = self.set_prop_base(self.regs[rd])? else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@@ -484,7 +495,7 @@ impl Vm {
|
||||
|
||||
fn dispatch_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("SET_PROP rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "Cannot create property on non-object")? else {
|
||||
let Some(obj_ptr) = self.set_prop_base(self.regs[rd])? else {
|
||||
return Ok(());
|
||||
};
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
@@ -560,7 +571,7 @@ impl Vm {
|
||||
|
||||
fn dispatch_set_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
vm_trace!("SET_PROP_DYNAMIC rd={} a={} b={}", rd, a, b);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "Cannot create property on non-object")? else {
|
||||
let Some(obj_ptr) = self.set_prop_base(self.regs[rd])? else {
|
||||
return Ok(());
|
||||
};
|
||||
let prop_name_si = self.property_key_si(self.regs[a])?;
|
||||
|
||||
@@ -191,7 +191,6 @@ impl Vm {
|
||||
pub(crate) fn init_aggregate_error_intrinsics(&mut self) {
|
||||
let sf = self.kernel_core.perm_interner().as_ref();
|
||||
let sh = self.kernel_core.shape_forge().as_ref();
|
||||
let fn_proto_val = self.session.builtin_world().fn_proto_val();
|
||||
let error_proto_val =
|
||||
JsValue::from_js_object(self.session.builtin_world().error_proto.as_ptr() as *mut JsObject);
|
||||
|
||||
@@ -213,8 +212,10 @@ impl Vm {
|
||||
let msg_pos = proto.push_prop(JsValue::perm_string(sf.string_ptr(sf.intern("").0)));
|
||||
proto.set_data_meta(msg_pos, PropAttributes::new(true, false, true));
|
||||
|
||||
// %AggregateError% 构造器:proto = %Function.prototype%,length 2。
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val));
|
||||
// %AggregateError% 构造器:proto = %Error%(NativeError 构造器继承 Error 构造器),length 2。
|
||||
let error_ctor_val =
|
||||
JsValue::from_js_object(self.session.builtin_world().error_constructor.as_ptr() as *mut JsObject);
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_ctor_val));
|
||||
ctor.set_function(true);
|
||||
ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR;
|
||||
// SAFETY: aggregate_error_constructor 是 NativeFn 函数项。
|
||||
@@ -1071,6 +1072,11 @@ fn aggregate_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
// SAFETY: this 是本次构造的存活对象。
|
||||
let _ = vm.define_data_property(unsafe { &mut *this }, msg_si, msg_val, PropAttributes::new(true, false, true));
|
||||
}
|
||||
// InstallErrorCause:options 为第三实参(args[3]),message 之后、errors 收集之前。
|
||||
let options = if args.len() > 3 { vm.reg(args[3]) } else { JsValue::undefined() };
|
||||
if let NativeResult::Err(e) = oxide_builtins::error::install_error_cause(vm, this, options) {
|
||||
return NativeResult::Err(e);
|
||||
}
|
||||
let errors_list = match vm.aggregate_errors_to_list(errors) {
|
||||
Ok(list) => list,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
|
||||
Reference in New Issue
Block a user