feat: 修复 Object.defineProperties/fromEntries 实现,getPrototypeOf/hasOwn/isExtensible 参数校验
This commit is contained in:
@@ -500,29 +500,94 @@ pub fn object_get_own_property_names<H: VmHost>(vm: &mut H, args: &[u8]) -> Nati
|
||||
|
||||
pub fn object_define_properties<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 3 {
|
||||
return NativeResult::Ok(vm.reg(args[1]));
|
||||
return NativeResult::Err(crate::error::create_type_error(
|
||||
vm,
|
||||
"Object.defineProperties: expected at least 2 arguments",
|
||||
));
|
||||
}
|
||||
let _obj_ptr = native_try!(require_obj_arg(vm, args, "defineProperties"));
|
||||
let target_val = vm.reg(args[1]);
|
||||
let target_ptr = match require_obj_arg(vm, args, "defineProperties") {
|
||||
Ok(ptr) => ptr,
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
let desc_val = vm.reg(args[2]);
|
||||
if !desc_val.is_object() {
|
||||
return NativeResult::Ok(vm.reg(args[1]));
|
||||
return NativeResult::Err(crate::error::create_type_error(
|
||||
vm,
|
||||
"Object.defineProperties: descriptor must be an object",
|
||||
));
|
||||
}
|
||||
NativeResult::Ok(vm.reg(args[1]))
|
||||
let desc_ptr = desc_val.as_js_object_ptr();
|
||||
if desc_ptr.is_null() {
|
||||
return NativeResult::Err(crate::error::create_type_error(
|
||||
vm,
|
||||
"Object.defineProperties: descriptor must be an object",
|
||||
));
|
||||
}
|
||||
let prop_keys: Vec<(u32, u32)> = {
|
||||
let desc = unsafe { &*desc_ptr };
|
||||
walk_own_keys(vm, desc)
|
||||
};
|
||||
for (key_si, offset) in prop_keys {
|
||||
let desc = unsafe { &*desc_ptr };
|
||||
let prop_desc_val = desc.get_prop_at(offset);
|
||||
if !prop_desc_val.is_object() {
|
||||
continue;
|
||||
}
|
||||
let value_si = vm.kernel_core().perm_interner().intern("value").0;
|
||||
let desc_obj_ptr = prop_desc_val.as_js_object_ptr();
|
||||
if desc_obj_ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
let desc_obj = unsafe { &*desc_obj_ptr };
|
||||
let prop_val = if let Some(pos) = vm.kernel_core().shape_forge().lookup_position(desc_obj.shape_id(), value_si)
|
||||
{
|
||||
desc_obj.get_prop_at(pos)
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let target = unsafe { &mut *target_ptr };
|
||||
let _ = vm.define_data_property(target, key_si, prop_val, PropAttributes::DEFAULT_DATA);
|
||||
}
|
||||
NativeResult::Ok(target_val)
|
||||
}
|
||||
|
||||
pub fn object_from_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
return NativeResult::Ok(JsValue::null());
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Object.fromEntries: expected 1 argument"));
|
||||
}
|
||||
let entries = vm.reg(args[1]);
|
||||
if !entries.is_object() {
|
||||
return NativeResult::Ok(JsValue::null());
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Object.fromEntries: argument must be iterable"));
|
||||
}
|
||||
let obj_ptr = vm.alloc_object(JsObject::new_empty(
|
||||
let entries_ptr = entries.as_js_object_ptr();
|
||||
if entries_ptr.is_null() {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Object.fromEntries: argument must be iterable"));
|
||||
}
|
||||
let obj = vm.alloc_object(JsObject::new_empty(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject),
|
||||
));
|
||||
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
|
||||
let target_val = JsValue::from_js_object(obj);
|
||||
let n: usize = unsafe { (*entries_ptr).prop_vec_len() };
|
||||
for i in 0..n {
|
||||
let pair_val = unsafe { (*entries_ptr).get_prop_at(i) };
|
||||
if !pair_val.is_object() {
|
||||
continue;
|
||||
}
|
||||
let pair_ptr = pair_val.as_js_object_ptr();
|
||||
if pair_ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
let pair = unsafe { &*pair_ptr };
|
||||
let key_val = pair.get_prop_at(0);
|
||||
let value_val = pair.get_prop_at(1);
|
||||
let key_str = oxide_runtime_api::to_string(key_val);
|
||||
let si = vm.kernel_core().perm_interner().intern(&key_str).0;
|
||||
let promoted = vm.promote_if_needed_for_write_ptr(obj, value_val);
|
||||
let _ = vm.ordinary_set(unsafe { &mut *obj }, si, promoted, target_val);
|
||||
}
|
||||
NativeResult::Ok(target_val)
|
||||
}
|
||||
|
||||
pub fn object_get_prototype_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
@@ -142,4 +142,57 @@ fn test_is_extensible_non_object_returns_false() {
|
||||
assert!(result.is_bool() && !result.as_bool(), "non-object should not be extensible per ES spec");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_extensible_no_args_type_error() {
|
||||
assert!(eval("Object.isExtensible()").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_prototype_of_null_throws_type_error() {
|
||||
assert!(eval("Object.getPrototypeOf(null)").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_prototype_of_undefined_throws_type_error() {
|
||||
assert!(eval("Object.getPrototypeOf(undefined)").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_prototype_of_plain_object() {
|
||||
let (_vm, result) = eval("var o = {}; Object.getPrototypeOf(o)").unwrap();
|
||||
assert!(result.is_object() || result.is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_own_own_property() {
|
||||
let (_vm, result) = eval("var o = {a:1}; Object.hasOwn(o, 'a')").unwrap();
|
||||
assert!(result.is_bool() && result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_own_inherited_property() {
|
||||
let (_vm, result) = eval("Object.hasOwn({}, 'toString')").unwrap();
|
||||
assert!(result.is_bool() && !result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_define_properties_no_args_type_error() {
|
||||
assert!(eval("Object.defineProperties()").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_define_properties_applies_multiple_props() {
|
||||
let (_vm, result) = eval("var o = {}; Object.defineProperties(o, {a: {value: 1}, b: {value: 2}}); o.a").unwrap();
|
||||
assert!(result.is_int() || result.is_double());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_entries_no_args_type_error() {
|
||||
assert!(eval("Object.fromEntries()").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_entries_empty_array() {
|
||||
let (_vm, result) = eval("Object.fromEntries([])").unwrap();
|
||||
assert!(result.is_object(), "fromEntries([]) should return an empty object");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user