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:
2026-08-08 02:25:13 +08:00
parent 8f5446aa8a
commit 0b68baa802
8 changed files with 275 additions and 83 deletions
+12 -20
View File
@@ -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)
}
}