fix: Object 内置方法 ToObject 装箱(原始值不再误抛 TypeError)

This commit is contained in:
爱特每羊队
2026-08-10 20:08:40 +08:00
parent 069e8920c4
commit e8506e6f29
2 changed files with 45 additions and 47 deletions
+34 -38
View File
@@ -607,14 +607,11 @@ pub fn object_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
"Object.defineProperty: expected at least 3 arguments",
));
}
let obj_val = vm.reg(args[1]);
if !obj_val.is_object() {
return NativeResult::Err(crate::error::create_type_error(vm, "Object.defineProperty called on non-object"));
}
let obj_val = match oxide_runtime_api::to_object(vm.reg(args[1]), vm) {
Ok(v) => v,
Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)),
};
let obj_ptr = obj_val.as_js_object_ptr();
if obj_ptr.is_null() {
return NativeResult::Err(crate::error::create_type_error(vm, "Object.defineProperty called on non-object"));
}
// well-known symbol 等特殊键统一走 property_key_si(映射到各自的 Symbol 键),
// 保证与计算属性访问、Reflect.defineProperty 等读键路径一致。
let si = vm.property_key_si(vm.reg(args[2]));
@@ -634,14 +631,11 @@ pub fn object_get_own_property_descriptor<H: VmHost>(vm: &mut H, args: &[u8]) ->
"Object.getOwnPropertyDescriptor called on non-object",
));
}
let obj_val = vm.reg(args[1]);
if !obj_val.is_object() {
return NativeResult::Ok(JsValue::undefined());
}
let obj_val = match oxide_runtime_api::to_object(vm.reg(args[1]), vm) {
Ok(v) => v,
Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)),
};
let obj_ptr = obj_val.as_js_object_ptr();
if obj_ptr.is_null() {
return NativeResult::Ok(JsValue::undefined());
}
let prop_name_str = oxide_runtime_api::to_string(vm.reg(args[2]));
let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0;
@@ -717,14 +711,12 @@ fn require_obj_arg<H: VmHost>(vm: &mut H, args: &[u8], fn_name: &str) -> Result<
return Err(crate::error::create_type_error(vm, &format!("Object.{fn_name} called on non-object")));
}
let val = vm.reg(args[1]);
if !val.is_object() {
return Err(crate::error::create_type_error(vm, &format!("Object.{fn_name} called on non-object")));
}
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
return Err(crate::error::create_type_error(vm, &format!("Object.{fn_name} called on non-object")));
}
Ok(ptr)
// ToObject:原始值装箱后返回其对象指针;null/undefined 抛 TypeError。
let obj_val = match oxide_runtime_api::to_object(val, vm) {
Ok(v) => v,
Err(msg) => return Err(crate::error::create_type_error(vm, &msg)),
};
Ok(obj_val.as_js_object_ptr())
}
macro_rules! native_try {
@@ -1044,36 +1036,40 @@ pub fn object_proto_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
}
/// `Object.prototype.hasOwnProperty(key)`:this 是否有指定自身属性。
///
/// # 步骤
/// 1. 先 ToPropertyKey 求键(spec 顺序:键先于 ToObject)。
/// 2. ToObject 装箱 this(null/undefined 抛 TypeError,原始值装箱后查 own 槽)。
pub fn object_proto_has_own_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let this_val = vm.reg(args[0]);
if !this_val.is_object() || this_val.as_js_object_ptr().is_null() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Object.prototype.hasOwnProperty called on non-object",
));
}
let key_si = vm.property_key_si(vm.reg(args[1]));
let obj = unsafe { &*this_val.as_js_object_ptr() };
let this_val = vm.reg(args[0]);
let obj_val = match oxide_runtime_api::to_object(this_val, vm) {
Ok(v) => v,
Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)),
};
let obj = unsafe { &*obj_val.as_js_object_ptr() };
NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some()))
}
/// `Object.prototype.propertyIsEnumerable(key)`:指定自身属性是否可枚举。
///
/// # 步骤
/// 1. 先 ToPropertyKey 求键。
/// 2. ToObject 装箱 this(null/undefined 抛 TypeError)。
pub fn object_proto_property_is_enumerable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let this_val = vm.reg(args[0]);
if !this_val.is_object() || this_val.as_js_object_ptr().is_null() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Object.prototype.propertyIsEnumerable called on non-object",
));
}
let key_si = vm.property_key_si(vm.reg(args[1]));
let obj = unsafe { &*this_val.as_js_object_ptr() };
let this_val = vm.reg(args[0]);
let obj_val = match oxide_runtime_api::to_object(this_val, vm) {
Ok(v) => v,
Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)),
};
let obj = unsafe { &*obj_val.as_js_object_ptr() };
let Some(pos) = vm.get_own_property_slot(obj, key_si) else {
return NativeResult::Ok(JsValue::bool(false));
};
@@ -17,8 +17,10 @@ fn object_keys_no_args_type_error() {
}
#[test]
fn object_keys_non_object_type_error() {
assert!(eval("Object.keys(42)").is_err(), "Object.keys(42) should throw TypeError");
fn object_keys_boxes_primitive() {
// ToObject 装箱:Object.keys(42) 返回装箱对象的自身键(空)。
let (_vm, result) = eval("Object.keys(42).join(',')").unwrap();
assert!(result.is_string() && unsafe { &*result.as_string_ptr() }.as_str() == "");
}
#[test]
@@ -51,11 +53,9 @@ fn object_get_own_property_names_no_args_type_error() {
}
#[test]
fn object_get_own_property_names_non_object_type_error() {
assert!(
eval("Object.getOwnPropertyNames(42)").is_err(),
"Object.getOwnPropertyNames(42) should throw TypeError"
);
fn object_get_own_property_names_boxes_primitive() {
let (_vm, result) = eval("Object.getOwnPropertyNames(42).join(',')").unwrap();
assert!(result.is_string(), "Object.getOwnPropertyNames(42) should box the primitive");
}
#[test]
@@ -227,8 +227,10 @@ fn test_get_own_property_descriptor_no_args_type_error() {
}
#[test]
fn test_has_own_property_non_object_this_type_error() {
assert!(eval("Object.prototype.hasOwnProperty.call(42, 'x')").is_err());
fn test_has_own_property_boxes_primitive_this() {
// ToObject 装箱 this:原始值上 hasOwnProperty 返回 false 而非抛错。
let (_vm, result) = eval("Object.prototype.hasOwnProperty.call(42, 'x')").unwrap();
assert!(result.is_bool() && !result.as_bool());
}
#[test]