fix: delete 真正删除属性(shape 链重建 + 数组元素 hole 标记)
字节码 delete 原先只清值为 undefined,属性残留在 shape 链,hasOwnProperty/ keys/枚举仍可见。统一走共享 delete_own_property(与 Reflect.deleteProperty 一致): - 命名属性经 shape 链重建移除,数组重建前保存并恢复元素区 - 数组下标元素标记为 hole(PropMetaEntry.hole),length 不变, 存在性检查(resolve_property/get_own_property_slot/ordinary_get/for-in) 判 hole,重赋值自动清除标记 - 不可配置属性统一返回 false(sloppy 语义,strict 抛错留待 emit 追踪严格模式) - set_or_create_prop_value 数组下标键路由元素区,修复 delete 后重赋值误入 shape 链 test262 Object +212 PASS/-102 FAIL,Array +149 PASS/-60 FAIL,无回归。
This commit is contained in:
@@ -170,7 +170,8 @@ impl Vm {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
let prop_name_si = self.property_key_si(self.regs[b]);
|
||||
let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_entry(&self.bytecode, &mut self.pc);
|
||||
if obj.has_prop_meta() { let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?;
|
||||
if obj.has_prop_meta() {
|
||||
let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?;
|
||||
if self.accessor_frame_target_reg.take().is_none() {
|
||||
self.regs[a] = val;
|
||||
}
|
||||
@@ -412,17 +413,13 @@ impl Vm {
|
||||
let key_val = self.immutables().get(prop_idx).copied().unwrap_or_else(JsValue::undefined);
|
||||
let prop_name_si = self.property_key_si(key_val);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "delete on non-object")? else {
|
||||
return Ok(true);
|
||||
self.regs[rd] = JsValue::bool(true);
|
||||
return Ok(false);
|
||||
};
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
let configurable = obj.prop_meta_at(pos).map(|m| m.attributes.configurable()).unwrap_or(true);
|
||||
if !configurable {
|
||||
return self.raise_type_error("cannot delete non-configurable property").map(|()| false);
|
||||
}
|
||||
obj.set_prop_shape(pos, JsValue::undefined());
|
||||
}
|
||||
self.regs[rd] = JsValue::bool(true);
|
||||
// 统一走共享删除逻辑(与 Reflect.deleteProperty 一致);不可配置返回 false。
|
||||
let deleted = oxide_builtins::object::delete_own_property(self, obj, prop_name_si);
|
||||
self.regs[rd] = JsValue::bool(deleted);
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
@@ -430,18 +427,13 @@ impl Vm {
|
||||
vm_trace!("DELETE_PROP_DYNAMIC rd={}", rd);
|
||||
let prop_name_si = self.property_key_si(self.regs[b]);
|
||||
let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "delete on non-object")? else {
|
||||
return Ok(true);
|
||||
self.regs[rd] = JsValue::bool(true);
|
||||
return Ok(false);
|
||||
};
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
let configurable = obj.prop_meta_at(pos).map(|m| m.attributes.configurable()).unwrap_or(true);
|
||||
if !configurable {
|
||||
return self.raise_type_error("cannot delete non-configurable property").map(|()| false);
|
||||
}
|
||||
obj.set_prop_shape(pos, JsValue::undefined());
|
||||
}
|
||||
self.regs[rd] = JsValue::bool(true);
|
||||
// 统一走共享删除逻辑(与 Reflect.deleteProperty 一致);不可配置返回 false。
|
||||
let deleted = oxide_builtins::object::delete_own_property(self, obj, prop_name_si);
|
||||
self.regs[rd] = JsValue::bool(deleted);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -663,7 +663,8 @@ impl Vm {
|
||||
}
|
||||
if obj.is_array() {
|
||||
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
|
||||
if index < obj.prop_vec_len() as u32 {
|
||||
// 数组元素区:hole(删除标记)视为不存在。
|
||||
if index < obj.array_prop_count && !obj.prop_meta_at(index).is_some_and(|m| m.is_hole()) {
|
||||
return Some(obj.get_prop_at(index));
|
||||
}
|
||||
}
|
||||
@@ -701,7 +702,8 @@ impl Vm {
|
||||
}
|
||||
if obj.is_array() {
|
||||
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
|
||||
if index < obj.prop_vec_len() as u32 {
|
||||
// 数组元素区:hole(删除标记)视为不存在。
|
||||
if index < obj.array_prop_count && !obj.prop_meta_at(index).is_some_and(|m| m.is_hole()) {
|
||||
return Some(index);
|
||||
}
|
||||
}
|
||||
@@ -1840,4 +1842,3 @@ mod tests {
|
||||
assert_eq!(vm.reg(255), JsValue::int(13));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,7 @@ impl Vm {
|
||||
}
|
||||
// native 方法(非构造器)不可 new。
|
||||
if ctor_obj.native_fn().is_some() && ctor_obj.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR {
|
||||
return self
|
||||
.raise_type_error("object is not a constructor")
|
||||
.map(|_| true);
|
||||
return self.raise_type_error("object is not a constructor").map(|_| true);
|
||||
}
|
||||
|
||||
let ext = self.bytecode[self.pc];
|
||||
@@ -321,10 +319,14 @@ impl Vm {
|
||||
|
||||
// 数组把整型下标元素存在 prop_vec,而 prop_vec 不属于 shape 链。
|
||||
// 单独枚举它们(ES:数组下标是可枚举字符串键,按升序排在其它自有键之前)。
|
||||
// hole(删除标记)跳过——for-in 不枚举数组稀疏空洞。
|
||||
if current.is_object() {
|
||||
let arr = unsafe { &*current.as_js_object_ptr() };
|
||||
if arr.is_array() {
|
||||
for i in 0..arr.prop_vec_len() {
|
||||
for i in 0..arr.array_prop_count {
|
||||
if arr.prop_meta_at(i).is_some_and(|m| m.is_hole()) {
|
||||
continue;
|
||||
}
|
||||
let is_enum = arr
|
||||
.prop_meta_at(i)
|
||||
.map(|m| m.attributes.enumerable())
|
||||
|
||||
@@ -30,7 +30,8 @@ impl Vm {
|
||||
}
|
||||
if obj.is_array() {
|
||||
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
|
||||
if index < obj.prop_vec_len() as u32 {
|
||||
// 数组元素区:hole(删除标记)视为不存在,落到原型链。
|
||||
if index < obj.array_prop_count && !obj.prop_meta_at(index).is_some_and(|m| m.is_hole()) {
|
||||
// 数组元素为访问器属性(defineProperty getter)时须触发
|
||||
// getter,而非直接读数据槽。
|
||||
if let Some(meta) = obj.prop_meta_at(index) {
|
||||
@@ -309,6 +310,13 @@ impl Vm {
|
||||
|
||||
pub(crate) fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
|
||||
vm_trace!("set_or_create_prop_value: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si);
|
||||
// 数组下标键写入元素区(维护 array_prop_count),不进入 shape 链。
|
||||
if obj.is_array() {
|
||||
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
|
||||
obj.set_prop_at(index, val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_shape(pos, val);
|
||||
} else {
|
||||
@@ -328,7 +336,15 @@ impl Vm {
|
||||
if obj.is_array() {
|
||||
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
|
||||
// 数组索引属性存元素区并维护 array_prop_count(元素数随索引增长)。
|
||||
return self.define_array_index_element(obj, index, val, attributes, false, JsValue::undefined(), JsValue::undefined());
|
||||
return self.define_array_index_element(
|
||||
obj,
|
||||
index,
|
||||
val,
|
||||
attributes,
|
||||
false,
|
||||
JsValue::undefined(),
|
||||
JsValue::undefined(),
|
||||
);
|
||||
}
|
||||
}
|
||||
let pos = if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
@@ -411,14 +427,8 @@ impl Vm {
|
||||
/// 数组索引属性(`"0"`~`"4294967294"`)的 define 路径:存入元素区并维护
|
||||
/// `array_prop_count`(length 随最高索引增长),meta 与元素槽对齐。
|
||||
fn define_array_index_element(
|
||||
&mut self,
|
||||
obj: &mut JsObject,
|
||||
index: u32,
|
||||
val: JsValue,
|
||||
attributes: PropAttributes,
|
||||
is_accessor: bool,
|
||||
get: JsValue,
|
||||
set: JsValue,
|
||||
&mut self, obj: &mut JsObject, index: u32, val: JsValue, attributes: PropAttributes, is_accessor: bool,
|
||||
get: JsValue, set: JsValue,
|
||||
) -> Result<(), String> {
|
||||
let pos = index as usize;
|
||||
if pos > oxide_types::object::MAX_DENSE_PROPS {
|
||||
|
||||
Reference in New Issue
Block a user