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
+4 -40
View File
@@ -1,9 +1,9 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::mem::P;
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::value::JsValue;
use crate::object::walk_own_keys;
use crate::object::{delete_own_property, walk_own_keys};
use oxide_runtime_api::{NativeResult, VmHost};
@@ -44,7 +44,8 @@ pub fn reflect_construct<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
}
// 不可构造:箭头函数、native 方法(非构造器,OBJ_TYPE_CONSTRUCTOR 标记的除外)。
let nt = unsafe { &*new_target_ptr };
if nt.is_arrow() || (nt.native_fn().is_some() && nt.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR) {
if nt.is_arrow() || (nt.native_fn().is_some() && nt.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR)
{
return type_error(vm, "Reflect.construct newTarget is not a constructor");
}
@@ -288,40 +289,3 @@ fn make_string_array<H: VmHost>(vm: &mut H, parts: &[String]) -> JsValue {
unsafe { &mut *arr }.set_prop_count(parts.len());
JsValue::from_js_object(arr)
}
fn delete_own_property<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> bool {
let keys = walk_own_keys(vm, obj);
let Some((_, delete_pos)) = keys.iter().find(|(si, _)| *si == key_si).copied() else {
return true;
};
if obj
.prop_meta_at(delete_pos)
.map(|meta| !meta.attributes.configurable())
.unwrap_or(false)
{
return false;
}
let retained: Vec<(u32, JsValue, Option<PropMetaEntry>)> = keys
.into_iter()
.filter(|(_, pos)| *pos != delete_pos)
.map(|(si, pos)| (si, obj.get_prop_at(pos), obj.prop_meta_at(pos)))
.collect();
obj.set_shape_id(EMPTY_SHAPE_ID);
obj.set_prop_count(0usize);
for (si, value, meta) in retained {
let shape = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si);
obj.set_shape_id(shape);
let pos = obj.push_prop(value);
if let Some(meta) = meta {
if meta.is_accessor {
obj.set_accessor_meta(pos, meta.get, meta.set, meta.attributes);
} else {
obj.set_data_meta(pos, meta.attributes);
}
}
}
obj.bump_generation();
true
}