fix: 补 Error 族原型与 Math 六函数边及 Symbol 面描述符

This commit is contained in:
2026-09-24 17:33:49 +00:00
parent 44ead358ed
commit 53505dc38c
9 changed files with 334 additions and 53 deletions
+48 -6
View File
@@ -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
+180 -19
View File
@@ -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))
}
+8 -18
View File
@@ -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"
+38
View File
@@ -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])) {
+8
View File
@@ -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 -2
View File
@@ -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())
};
+22 -2
View File
@@ -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();
}
+14 -3
View File
@@ -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])?;
+9 -3
View File
@@ -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),