diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index 139c4ea..655af79 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -252,12 +252,23 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul let get_field = own_field(vm, desc, get_si); let set_field = own_field(vm, desc, set_si); let writable_field = own_field(vm, desc, writable_si); + + let existing_pos = { + let obj = unsafe { &*obj_ptr }; + vm.get_own_property_slot(obj, si) + }; + let existing_meta = existing_pos.and_then(|pos| { + let obj = unsafe { &*obj_ptr }; + obj.prop_meta_at(pos) + }); + + // 修改已有属性时缺省字段回填现有值,仅定义新属性时缺省才为 false。 let enumerable = own_field(vm, desc, enumerable_si) .map(oxide_runtime_api::to_boolean) - .unwrap_or(false); + .unwrap_or_else(|| existing_meta.map(|m| m.attributes.enumerable()).unwrap_or(false)); let configurable = own_field(vm, desc, configurable_si) .map(oxide_runtime_api::to_boolean) - .unwrap_or(false); + .unwrap_or_else(|| existing_meta.map(|m| m.attributes.configurable()).unwrap_or(false)); let has_data = value_field.is_some() || writable_field.is_some(); let has_accessor = get_field.is_some() || set_field.is_some(); @@ -268,14 +279,6 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul )); } - let existing_pos = { - let obj = unsafe { &*obj_ptr }; - vm.get_own_property_slot(obj, si) - }; - let existing_meta = existing_pos.and_then(|pos| { - let obj = unsafe { &*obj_ptr }; - obj.prop_meta_at(pos) - }); let existing_value = existing_pos.map_or(JsValue::undefined(), |pos| { let obj = unsafe { &*obj_ptr }; obj.get_prop_at(pos) @@ -301,11 +304,9 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul "accessor descriptor get/set must be callable or undefined", )); } - if vm - .define_accessor_property(obj, si, get, set, PropAttributes::new(false, enumerable, configurable)) - .is_err() + if let Err(e) = vm.define_accessor_property(obj, si, get, set, PropAttributes::new(false, enumerable, configurable)) { - return NativeResult::Err(crate::error::create_type_error(vm, "Cannot define property")); + return NativeResult::Err(crate::error::create_type_error(vm, &e)); } } else if has_data { let value = if existing_pos.is_some() { @@ -320,24 +321,18 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul } else { writable_field.map(oxide_runtime_api::to_boolean).unwrap_or(false) }; - if vm - .define_data_property(obj, si, value, PropAttributes::new(writable, enumerable, configurable)) - .is_err() - { - return NativeResult::Err(crate::error::create_type_error(vm, "Cannot define property")); + if let Err(e) = vm.define_data_property(obj, si, value, PropAttributes::new(writable, enumerable, configurable)) { + return NativeResult::Err(crate::error::create_type_error(vm, &e)); } } else { if existing_pos.is_none() { - if vm - .define_data_property( - obj, - si, - JsValue::undefined(), - PropAttributes::new(false, enumerable, configurable), - ) - .is_err() - { - return NativeResult::Err(crate::error::create_type_error(vm, "Cannot define property")); + if let Err(e) = vm.define_data_property( + obj, + si, + JsValue::undefined(), + PropAttributes::new(false, enumerable, configurable), + ) { + return NativeResult::Err(crate::error::create_type_error(vm, &e)); } return NativeResult::Ok(obj_val); } @@ -347,13 +342,11 @@ pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResul if is_accessor { let get = existing_meta.map(|m| m.get).unwrap_or(JsValue::undefined()); let set = existing_meta.map(|m| m.set).unwrap_or(JsValue::undefined()); - if vm.define_accessor_property(obj, si, get, set, attrs).is_err() { - return NativeResult::Err(crate::error::create_type_error(vm, "Cannot define property")); - } - } else { - if vm.define_data_property(obj, si, existing_value, attrs).is_err() { - return NativeResult::Err(crate::error::create_type_error(vm, "Cannot define property")); + if let Err(e) = vm.define_accessor_property(obj, si, get, set, attrs) { + return NativeResult::Err(crate::error::create_type_error(vm, &e)); } + } else if let Err(e) = vm.define_data_property(obj, si, existing_value, attrs) { + return NativeResult::Err(crate::error::create_type_error(vm, &e)); } } NativeResult::Ok(obj_val) diff --git a/crates/oxide_vm/src/dispatch/misc.rs b/crates/oxide_vm/src/dispatch/misc.rs index b48bd16..cdf3eee 100644 --- a/crates/oxide_vm/src/dispatch/misc.rs +++ b/crates/oxide_vm/src/dispatch/misc.rs @@ -157,12 +157,18 @@ impl Vm { // length:实参个数,可写、不可枚举、可配置。 let length_si = self.kernel_core.perm_interner().intern("length").0; - self.define_data_property(obj, length_si, JsValue::int(count as i32), PropAttributes::new(true, false, true))?; + if let Err(msg) = + self.define_data_property(obj, length_si, JsValue::int(count as i32), PropAttributes::new(true, false, true)) + { + return self.raise_error_kind("TypeError", &msg); + } // callee:当前执行函数,可写、不可枚举、可配置(严格模式应抛 TypeError,未支持)。 let callee_si = self.kernel_core.perm_interner().intern("callee").0; let callee = self.current_callee().unwrap_or(JsValue::undefined()); - self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true))?; + if let Err(msg) = self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true)) { + return self.raise_error_kind("TypeError", &msg); + } self.regs[rd] = JsValue::from_js_object(obj_ptr); Ok(()) diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index 70f032a..4fd7068 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -118,7 +118,10 @@ impl Vm { } else { setter }; - self.define_accessor_property(obj, prop_name_si, get, set, PropAttributes::DEFAULT_DATA) + match self.define_accessor_property(obj, prop_name_si, get, set, PropAttributes::DEFAULT_DATA) { + Ok(()) => Ok(()), + Err(msg) => self.raise_error_kind("TypeError", &msg), + } } pub(crate) fn dispatch_return(&mut self, rd: usize) -> Result, String> { diff --git a/crates/oxide_vm/src/vm_props.rs b/crates/oxide_vm/src/vm_props.rs index 3dab249..3946fe1 100644 --- a/crates/oxide_vm/src/vm_props.rs +++ b/crates/oxide_vm/src/vm_props.rs @@ -346,18 +346,18 @@ impl Vm { if let Some(current) = obj.prop_meta_at(pos) { if !current.attributes.configurable() { if current.is_accessor { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } if current.attributes.enumerable() != attributes.enumerable() { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } if !current.attributes.writable() && (attributes.writable() || !coercion::same_value(obj.get_prop_at(pos), val)) { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } if current.attributes.configurable() != attributes.configurable() { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } } } @@ -399,7 +399,7 @@ impl Vm { || current.get != get || current.set != set) { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } } obj.set_prop_storage(pos, JsValue::undefined()); @@ -422,28 +422,28 @@ impl Vm { ) -> Result<(), String> { let pos = index as usize; if pos > oxide_types::object::MAX_DENSE_PROPS { - return self.raise_type_error("array index out of dense range"); + return Err("array index out of dense range".to_string()); } let pos = pos as u32; if let Some(current) = obj.prop_meta_at(pos) { if !current.attributes.configurable() { if current.is_accessor != is_accessor { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } if current.attributes.enumerable() != attributes.enumerable() { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } if current.attributes.configurable() != attributes.configurable() { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } if is_accessor { if current.get != get || current.set != set { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } } else if !current.attributes.writable() && (attributes.writable() || !coercion::same_value(obj.get_prop_at(pos), val)) { - return self.raise_type_error("cannot redefine non-configurable property"); + return Err("cannot redefine non-configurable property".to_string()); } } }