feat: freeze/seal/preventExtensions 写路径拦截(逐属性 meta + extensible 检查)

freeze/seal 逐属性改写 meta(数据属性 writable/configurable=false,
访问器 configurable=false,含数组元素区),使 has_prop_meta() 恒 true,
IC 直写路径自动失效;ordinary_set/create_named_prop_fast/
set_or_create_prop_value/define_* 增补 extensible/frozen 检查,
SET_ELEM 直写入口回落 ordinary_set 或拒绝新元素。

引擎未实现 strict 标志,赋值失败统一 raise_type_error(sloppy 静默留待后续)。
This commit is contained in:
2026-08-15 20:55:20 +08:00
parent f4e790978d
commit 970c2e988e
4 changed files with 307 additions and 10 deletions
+75 -9
View File
@@ -530,9 +530,8 @@ fn define_from_descriptor<H: VmHost>(
// 已有属性无显式 meta(普通数据属性/数组元素)时按默认属性回填:
// 描述符缺省字段保持现有值(writable/enumerable/configurable 均 true),
// 而非按新属性处理为 false(Object.defineProperty 省略字段不改已有属性)。
obj.prop_meta_at(pos).unwrap_or_else(|| {
oxide_types::object::PropMetaEntry::data(PropAttributes::DEFAULT_DATA)
})
obj.prop_meta_at(pos)
.unwrap_or_else(|| oxide_types::object::PropMetaEntry::data(PropAttributes::DEFAULT_DATA))
});
// 修改已有属性时缺省字段回填现有值,仅定义新属性时缺省才为 false。
@@ -759,7 +758,40 @@ macro_rules! native_try {
};
}
/// 把指定存储下标的 own 属性 meta 改写为冻结形态:数据属性 writable=false +
/// configurable=false,访问器属性 configurable=false(writable 不适用),
/// enumerable 保留原值。
fn freeze_own_prop_meta(obj: &mut JsObject, store: u32) {
let meta = obj
.prop_meta_at(store)
.unwrap_or_else(|| PropMetaEntry::data(PropAttributes::DEFAULT_DATA));
let attrs = PropAttributes::new(false, meta.attributes.enumerable(), false);
if meta.is_accessor {
obj.set_accessor_meta(store, meta.get, meta.set, attrs);
} else {
obj.set_data_meta(store, attrs);
}
}
/// 把指定存储下标的 own 属性 meta 改写为密封形态:configurable=false,
/// writable/enumerable 与访问器形态保留原值。
fn seal_own_prop_meta(obj: &mut JsObject, store: u32) {
let meta = obj
.prop_meta_at(store)
.unwrap_or_else(|| PropMetaEntry::data(PropAttributes::DEFAULT_DATA));
let attrs = PropAttributes::new(meta.attributes.writable(), meta.attributes.enumerable(), false);
if meta.is_accessor {
obj.set_accessor_meta(store, meta.get, meta.set, attrs);
} else {
obj.set_data_meta(store, attrs);
}
}
/// `Object.freeze(obj)`:冻结对象(不可扩展 + 全部属性不可配置/不可写),返回原对象。
///
/// # 副作用
/// - 逐属性写 meta 使 `has_prop_meta()` 恒 true,IC 直写路径自动失效,后续
/// 写/define 一律回落 ordinary_set / define 检查(writable/configurable 判定)
pub fn object_freeze<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Err(crate::error::create_type_error(vm, "Object.freeze called on non-object"));
@@ -769,14 +801,34 @@ pub fn object_freeze<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
return NativeResult::Ok(val);
}
let obj_ptr = val.as_js_object_ptr();
unsafe {
(*obj_ptr).set_frozen(true);
(*obj_ptr).set_extensible(false);
{
let obj = unsafe { &mut *obj_ptr };
// 命名属性:shape 链(数组对象存储索引 = 元素数 + 槽位)。
let keys = walk_own_keys(vm, obj);
for (_si, pos) in keys {
let store = if obj.is_array() { obj.array_prop_count + pos } else { pos };
freeze_own_prop_meta(obj, store);
}
// 数组元素区独立于 shape 链(hole 非 own 属性,跳过)。
if obj.is_array() {
for i in 0..obj.array_prop_count {
if obj.prop_meta_at(i).is_some_and(|m| m.is_hole()) {
continue;
}
freeze_own_prop_meta(obj, i);
}
}
obj.set_frozen(true);
obj.set_extensible(false);
}
NativeResult::Ok(val)
}
/// `Object.seal(obj)`:密封对象(不可扩展 + 全部属性不可配置),返回原对象。
///
/// # 副作用
/// - 逐属性写 meta 使 `has_prop_meta()` 恒 true,IC 直写路径自动失效,后续
/// define 回落 non-configurable 检查(已有属性写仍可正常进行)
pub fn object_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Err(crate::error::create_type_error(vm, "Object.seal called on non-object"));
@@ -786,9 +838,23 @@ pub fn object_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
return NativeResult::Ok(val);
}
let obj_ptr = val.as_js_object_ptr();
unsafe {
(*obj_ptr).set_sealed(true);
(*obj_ptr).set_extensible(false);
{
let obj = unsafe { &mut *obj_ptr };
let keys = walk_own_keys(vm, obj);
for (_si, pos) in keys {
let store = if obj.is_array() { obj.array_prop_count + pos } else { pos };
seal_own_prop_meta(obj, store);
}
if obj.is_array() {
for i in 0..obj.array_prop_count {
if obj.prop_meta_at(i).is_some_and(|m| m.is_hole()) {
continue;
}
seal_own_prop_meta(obj, i);
}
}
obj.set_sealed(true);
obj.set_extensible(false);
}
NativeResult::Ok(val)
}