feat: 对象层惰性模块命名空间求值触发

This commit is contained in:
2026-10-09 15:13:19 +00:00
parent 7900bb92e6
commit b240cbd305
21 changed files with 615 additions and 82 deletions
@@ -187,10 +187,12 @@ pub(crate) fn unit_string_value<H: VmHost>(vm: &mut H, unit: u16) -> JsValue {
/// 索引位置存在性判定(规范 HasProperty:自身与原型链任一层命中即存在,
/// 数组元素区 hole 视同缺失,TypedArray 整数索引按视图长度判在界)。
/// 数组/类数组目标非 deferred namespace,触发恒 no-op。
pub(crate) fn arraylike_index_present<H: VmHost>(vm: &mut H, ptr: *mut JsObject, i: usize) -> bool {
// SAFETY: ptr 来自 this/参数对象,指向存活对象。
let key_si = vm.string_key_si(&i.to_string());
vm.has_property(unsafe { &*ptr }, key_si)
.unwrap_or(false)
}
/// 元素读取失败时直接把异常作为 NativeResult::Err 返回。
+8 -4
View File
@@ -26,7 +26,8 @@ fn gated_get<H: VmHost>(
vm: &mut H, arr_ptr: *mut JsObject, index: usize, recv: JsValue,
) -> Result<Option<JsValue>, JsValue> {
let key_si = vm.string_key_si(&index.to_string());
if !vm.has_property(unsafe { &*arr_ptr }, key_si) {
// 数组目标非 deferred namespace,触发恒 no-op;错误按缺失处理(不可达)。
if !vm.has_property(unsafe { &*arr_ptr }, key_si).unwrap_or(false) {
return Ok(None);
}
let val = match vm.ordinary_get(unsafe { &*arr_ptr }, key_si, recv) {
@@ -40,7 +41,9 @@ fn gated_get<H: VmHost>(
/// 成功。数组元素区与命名属性双路径交由 delete_own_property_outcome 判定。
fn delete_prop_or_throw<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> Result<(), JsValue> {
match crate::object::delete_own_property_outcome(vm, obj, key_si) {
crate::object::DeleteOutcome::NonConfigurable => Err(array_type_error(vm, "Cannot delete property")),
// deferred namespace 求值触发失败(cyclic / 失败缓存重抛)原值上抛。
Err(exc) => Err(exc),
Ok(crate::object::DeleteOutcome::NonConfigurable) => Err(array_type_error(vm, "Cannot delete property")),
_ => Ok(()),
}
}
@@ -769,7 +772,8 @@ pub fn array_reverse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let lower_si = vm.string_key_si(&lower.to_string());
let upper_si = vm.string_key_si(&upper.to_string());
// 先下后上读值(getter 副作用序对齐规范);hole 与原型缺失判端不存在。
let lower_val = if vm.has_property(unsafe { &*arr_ptr }, lower_si) {
// 数组目标非 deferred namespace,触发恒 no-op;错误按缺失处理(不可达)。
let lower_val = if vm.has_property(unsafe { &*arr_ptr }, lower_si).unwrap_or(false) {
match vm.ordinary_get(unsafe { &*arr_ptr }, lower_si, recv) {
Ok(v) => Some(v),
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
@@ -782,7 +786,7 @@ pub fn array_reverse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if let Some(v) = lower_val {
vm.set_reg(0, v);
}
let upper_val = if vm.has_property(unsafe { &*arr_ptr }, upper_si) {
let upper_val = if vm.has_property(unsafe { &*arr_ptr }, upper_si).unwrap_or(false) {
match vm.ordinary_get(unsafe { &*arr_ptr }, upper_si, recv) {
Ok(v) => Some(v),
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
+2
View File
@@ -627,6 +627,8 @@ fn same_value_number(a: f64, b: f64) -> bool {
/// 均静默不抛(规范明注)。
fn apply_child_result<H: VmHost>(vm: &mut H, child_ptr: *mut JsObject, child_si: u32, new_val: JsValue) {
if new_val.is_undefined() {
// 子节点为 JSON.parse 新建的 plain 对象(非 deferred namespace),触发
// 恒 no-op;删除失败静默不抛(规范明注)。
let _ = crate::object::delete_own_property(vm, unsafe { &mut *child_ptr }, child_si);
} else {
let _ = vm.define_data_property(
+113 -7
View File
@@ -70,11 +70,14 @@ pub struct DeferredNsState {
/// undefined(哨兵:自导入/祖先引用,模块函数在导入方 prelude 期不可得)。
pub module: JsValue,
/// `[[Evaluated]]`:依赖模块是否已求值(mixed 依赖构造时即为真;
/// 首次触发成功后置真,不回退)。
/// 求值完成(成功或失败)后置真,不回退)。
pub evaluated: bool,
/// `[[Namespace]]`:求值后返回的命名空间对象(mixed 依赖构造时即为真实 ns;
/// 求值前为 undefined)。
pub namespace: JsValue,
/// 求值失败时缓存的原始错误值(再读再抛同一错误对象,保身份);
/// 成功求值与求值前恒 `None`。
pub error: Option<JsValue>,
}
/// 模块命名空间条目表:挂在 ns 对象 `native_data` 上,是 exotic [[Get]] 的权威
@@ -125,6 +128,52 @@ pub fn deferred_state(obj: &JsObject) -> Option<&DeferredNsState> {
table_ref.deferred.as_ref()
}
/// 可变访问 deferred namespace 状态盒(求值触发点写回 `[[Evaluated]]` /
/// `[[Namespace]]` / 失败错误缓存)。
///
/// # 边界与前提
/// - 返回 `None`:非 module namespace / 无条目表 / 非 deferred 对象(`deferred` 为 `None`)。
/// - 返回 `Some`:deferred 对象且已安装状态盒,供求值触发点读写内部槽。
///
/// # 注意事项
/// - 经 `native_data` 裸指针取 `&mut`(Box 堆分配、GC 不搬移,指针跨调用稳定),
/// 不借 `&mut JsObject` 本体。
#[expect(clippy::mut_from_ref)]
pub fn deferred_state_mut(obj: &JsObject) -> Option<&mut DeferredNsState> {
let table = ns_table_ptr(obj);
if table.is_null() {
return None;
}
// SAFETY: table 归本 ns 对象持有,生命周期见 `deferred_state`。
let table_ref = unsafe { &mut *table };
table_ref.deferred.as_mut()
}
/// deferred namespace 读路径的权威命名空间对象:已求值且 `[[Namespace]]` 为
/// 对象时返回真实 ns(活值与真实槽位都在真实 ns 的条目表,deferred 对象自身
/// 槽位是逐名预注册占位);其余形态(非 deferred / 未求值 / 哨兵)返回对象自身。
///
/// # 边界与前提
/// - 非 deferred 对象恒返回自身,调用方无需先判 deferred 标志。
/// - 求值成功后 `[[Namespace]]` 为 `__moduleEval` 返回的真实 ns 对象;求值失败
/// 或哨兵(自导入/祖先)形态下无真实 ns,读路径回落对象自身。
pub fn deferred_ns_read_target(obj: &JsObject) -> &JsObject {
if let Some(state) = deferred_state(obj) {
if state.evaluated {
let ns = state.namespace;
if ns.is_object() {
let ptr = ns.as_js_object_ptr();
if !ptr.is_null() {
// SAFETY: ns 由 [[Namespace]] 槽持有(GC 边已登记),
// 与 deferred 对象同代存活。
return unsafe { &*ptr };
}
}
}
}
obj
}
/// 在条目表中按 interned 键定位条目下标。
fn entry_index(table: &ModuleNsTable, key_si: u32) -> Option<usize> {
table.entries.iter().position(|e| e.name_si == key_si)
@@ -232,9 +281,10 @@ pub fn module_ns_export(obj: &JsObject, key_si: u32) -> Option<ModuleNsQuery> {
/// 收集条目表持有的 JsValue 边(已初始化 `Value` 与全部 `Cell` 内值),供 GC mark。
///
/// deferred namespace 对象额外持有 `[[Module]]` 与 `[[Namespace]]` 两条边:
/// 依赖模块函数与求值后命名空间对象须经本单一注册点入 mark,漏登则函数在
/// deferred ns 存活期间被回收,触发点解引用悬垂。
/// deferred namespace 对象额外持有 `[[Module]]`、`[[Namespace]]` 与失败缓存
/// `error`(Some 时)三条边:依赖模块函数、求值后命名空间对象与失败错误对象
/// 须经本单一注册点入 mark,漏登则对应值在 deferred ns 存活期间被回收,
/// 触发点解引用悬垂。
pub fn module_ns_native_edges(obj: &JsObject) -> Vec<JsValue> {
let table = ns_table_ptr(obj);
if table.is_null() {
@@ -261,6 +311,10 @@ pub fn module_ns_native_edges(obj: &JsObject) -> Vec<JsValue> {
if let Some(deferred) = &table_ref.deferred {
edges.push(deferred.module);
edges.push(deferred.namespace);
// 失败缓存的错误对象是 GC 边:漏登则 deferred ns 存活期被回收,再读解引用悬垂。
if let Some(err) = &deferred.error {
edges.push(*err);
}
}
edges
}
@@ -844,6 +898,10 @@ pub fn module_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
}
/// `__moduleEval(fn)`:同步执行依赖模块(fn 为编译期 CREATE_CLOSURE 的函数对象),返回其命名空间。
///
/// # 副作用
/// - 调用前置「正在求值模块」标记为 `Some(fn)`、返回后还原(save/restore,嵌套
/// `__moduleEval` 安全):deferred namespace 求值触发的 cyclic 守卫据此判自求值。
pub fn module_eval<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return type_error(vm, "__moduleEval: 1 argument required");
@@ -856,10 +914,15 @@ pub fn module_eval<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if ptr.is_null() || !unsafe { (*ptr).is_function() } {
return type_error(vm, "__moduleEval: target is not a function");
}
match vm.call_function_sync(fn_val, JsValue::undefined(), &[]) {
// 置位标记,调用返回后无条件还原(异常展开亦不残留)。
let saved = vm.evaluating_module();
vm.set_evaluating_module(Some(fn_val));
let result = match vm.call_function_sync(fn_val, JsValue::undefined(), &[]) {
Ok(v) => NativeResult::Ok(v),
Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)),
}
};
vm.set_evaluating_module(saved);
result
}
/// `__moduleDeferObject(fn, real_ns)`:创建 deferred namespace 对象(`import defer * as` 产物)。
@@ -871,7 +934,7 @@ pub fn module_eval<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
/// 2. 建 null 原型对象,定义 `@@toStringTag` = "Deferred Module"(不可写不可枚举不可配置)。
/// 3. 置 module namespace + deferred 双标志,置 non-extensible。
/// 4. 安装条目表并写状态盒:`[[Module]]` = fn、`[[Evaluated]]` = `real_ns` 为对象、
/// `[[Namespace]]` = `real_ns`(或 undefined)。
/// `[[Namespace]]` = `real_ns`(或 undefined)、失败错误缓存恒 `None`。
///
/// # 副作用
/// - 新对象创建起即标记 module namespace exotic 并置 non-extensible(同 `__moduleObject`);
@@ -912,6 +975,7 @@ pub fn module_defer_object<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
module: fn_val,
evaluated: ns_val.is_object(),
namespace: ns_val,
error: None,
});
}
NativeResult::Ok(JsValue::from_js_object(obj))
@@ -1069,4 +1133,46 @@ mod tests {
let state = deferred_state(ns_obj).expect("deferred state 应存活");
assert_eq!(state.module.as_js_object_ptr(), fn_ptr, "[[Module]] 指针应稳定");
}
#[test]
fn module_defer_ns_gc_edge_keeps_error_alive() {
let (mut vm, _fn_val) = vm_with_function();
// 失败缓存的错误对象(模拟求值失败后缓存的原值)。
let err_val = crate::error::create_type_error(&mut vm, "cached failure");
let err_ptr = err_val.as_js_object_ptr();
// 创建 deferred ns(哨兵形态)。
vm.set_reg(1, JsValue::undefined());
vm.set_reg(2, JsValue::undefined());
let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap();
// 直接写入 error 字段(模拟失败缓存),置已求值。
{
let ns_obj = unsafe { &*ns_val.as_js_object_ptr() };
let state = deferred_state_mut(ns_obj).expect("deferred state 应已安装");
state.error = Some(err_val);
state.evaluated = true;
}
// 仅 ns 为根:清掉持有 err 与 fn 的寄存器,err 唯一可达路径是 ns 的 error 边。
vm.set_reg(0, ns_val);
vm.set_reg(1, JsValue::undefined());
vm.set_reg(2, JsValue::undefined());
// 首收集达稳态(fn 无根被回收),记录稳态计数。
vm.collect_session_gc();
let steady_count = vm.session_object_count();
// 再收集一次:若 error 边漏登,err 被回收、计数回落。
vm.collect_session_gc();
assert_eq!(
vm.session_object_count(),
steady_count,
"失败缓存错误应跨收集存活(GC 边已登记)"
);
// 原地 sweep 不搬移:ns 与状态盒原地保留,error 指针稳定。
let ns_obj = unsafe { &*ns_val.as_js_object_ptr() };
let state = deferred_state(ns_obj).expect("deferred state 应存活");
assert_eq!(state.error.unwrap().as_js_object_ptr(), err_ptr, "error 指针应稳定");
}
}
+106 -14
View File
@@ -259,6 +259,12 @@ pub fn object_get_own_property_symbols<H: VmHost>(vm: &mut H, args: &[u8]) -> Na
Err(err) => return NativeResult::Err(err),
};
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
let obj = unsafe { &*obj_ptr };
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
return NativeResult::Err(exc);
}
let symbols: Vec<JsValue> = {
let obj = unsafe { &*obj_ptr };
own_symbol_key_values(vm, obj)
@@ -332,17 +338,28 @@ pub enum DeleteOutcome {
/// # 注意事项
/// - 数组元素存在性以 `prop_meta_at` 的 hole 标记判定,删除后重新写入元素
/// 会自动清除 hole 标记恢复存在
pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> DeleteOutcome {
pub fn delete_own_property_outcome<H: VmHost>(
vm: &mut H, obj: &mut JsObject, key_si: u32,
) -> Result<DeleteOutcome, JsValue> {
// deferred namespace 求值触发([[Delete]],symbol-like 键内部 no-op):
// 触发失败(cyclic / 失败缓存重抛)以异常值上抛,由调用边界恢复。
// uncaught 侧通道已还原为原错误值,原值重抛保身份。
if let Err(msg) = vm.ensure_deferred_ns_evaluation(obj, Some(key_si)) {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
return Err(exc);
}
// TA 数值键:界内索引不可配置(删除失败,元素零修改、镜像槽不同步);
// 数字无效键视为缺失(删除成功,镜像槽同步 undefined,对非全局内置 TA
// 为 no-op);非数字串 / symbol 键落下方普通属性路径。live 长 0
// (detach / 收缩越界)时全数值键自然归数字无效,与读 / 写 / 定义面同口径。
if obj.is_typed_array_obj() {
match crate::typed_array::ta_index_gate(vm, obj, key_si) {
crate::typed_array::TaIndexGate::NumericValid(_) => return DeleteOutcome::NonConfigurable,
crate::typed_array::TaIndexGate::NumericValid(_) => return Ok(DeleteOutcome::NonConfigurable),
crate::typed_array::TaIndexGate::NumericInvalid => {
vm.sync_global_builtin_mirror(obj, key_si, JsValue::undefined());
return DeleteOutcome::Missing;
return Ok(DeleteOutcome::Missing);
}
crate::typed_array::TaIndexGate::Ordinary => {}
}
@@ -355,21 +372,21 @@ pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, ke
let meta = obj.prop_meta_at(index);
// 已是 hole 视为不存在;非 configurable 不可删。
if meta.is_some_and(|m| m.is_hole()) {
return DeleteOutcome::Missing;
return Ok(DeleteOutcome::Missing);
}
if meta.is_some_and(|m| !m.attributes.configurable()) {
return DeleteOutcome::NonConfigurable;
return Ok(DeleteOutcome::NonConfigurable);
}
obj.mark_hole_at(index);
obj.bump_generation();
return DeleteOutcome::Deleted;
return Ok(DeleteOutcome::Deleted);
}
// 大索引越出元素区时是命名属性(shape 链),落穿下方查删路径。
}
// 非下标键:length 是虚拟属性(无 shape 槽、不在元素区),但描述符声明
// configurable:false,删除恒失败。
if key_si == vm.perm_intern("length") {
return DeleteOutcome::NonConfigurable;
return Ok(DeleteOutcome::NonConfigurable);
}
}
@@ -383,7 +400,7 @@ pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, ke
// 属性缺失:delete 按规范返 true,镜像槽同步 undefined 保持
// "槽 = A 侧原始存储"不变式(与入口预载缺位语义幂等)。
vm.sync_global_builtin_mirror(obj, key_si, JsValue::undefined());
return DeleteOutcome::Missing;
return Ok(DeleteOutcome::Missing);
};
// walk_own_keys 已返回绝对存储索引(数组含元素区偏移),直接使用。
if obj
@@ -391,7 +408,7 @@ pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, ke
.map(|meta| !meta.attributes.configurable())
.unwrap_or(false)
{
return DeleteOutcome::NonConfigurable;
return Ok(DeleteOutcome::NonConfigurable);
}
// 数组重建前保存元素区(值 + meta),重建后恢复到命名属性之前。
@@ -456,13 +473,13 @@ pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, ke
// 删除成功:镜像槽同步 undefined(成员形删除 / Reflect.deleteProperty /
// 0x9C 清槽同源收口,后两者对此幂等)。
vm.sync_global_builtin_mirror(obj, key_si, JsValue::undefined());
DeleteOutcome::Deleted
Ok(DeleteOutcome::Deleted)
}
/// 删除对象自身属性并投影为布尔(`Reflect.deleteProperty` 语义):成功返回 true,
/// 属性不可配置返回 false,恒不抛错。
pub fn delete_own_property<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> bool {
delete_own_property_outcome(vm, obj, key_si) != DeleteOutcome::NonConfigurable
/// 属性不可配置返回 false;deferred namespace 求值触发失败以异常值上抛。
pub fn delete_own_property<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> Result<bool, JsValue> {
Ok(delete_own_property_outcome(vm, obj, key_si)? != DeleteOutcome::NonConfigurable)
}
/// JS `Object()` 构造逻辑:创建空对象(prototype 为 null,由 VM 补装内置原型)。
@@ -533,7 +550,9 @@ fn namespace_export_get(obj: &JsObject, key_si: u32) -> Result<Option<JsValue>,
if is_symbol_key(key_si) {
return Ok(None);
}
match crate::module::module_ns_export(obj, key_si) {
// deferred namespace 求值后活值在真实 ns 条目表,读路径按权威对象查询。
let target = crate::module::deferred_ns_read_target(obj);
match crate::module::module_ns_export(target, key_si) {
None => Ok(None),
Some(crate::module::ModuleNsQuery::Initialized(v)) => Ok(Some(v)),
Some(crate::module::ModuleNsQuery::Uninitialized) => Err(crate::module::NS_UNINITIALIZED_MESSAGE),
@@ -580,6 +599,10 @@ pub fn object_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
};
let obj = unsafe { &*obj_ptr };
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
return NativeResult::Err(exc);
}
let owned_keys: Vec<(u32, u32)> = walk_own_keys(vm, obj)
.into_iter()
.filter(|(_si, offset)| {
@@ -685,6 +708,10 @@ pub fn object_assign<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if source_ptr.is_null() {
continue;
}
// [[OwnPropertyKeys]] 触发(源对象,deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, unsafe { &*source_ptr }) {
return NativeResult::Err(exc);
}
let source_keys: Vec<(u32, u32)> = {
let source = unsafe { &*source_ptr };
walk_own_keys(vm, source)
@@ -769,6 +796,15 @@ impl From<JsValue> for DefineDescFailure {
pub(crate) fn define_from_descriptor<H: VmHost>(
vm: &mut H, obj_ptr: *mut JsObject, key_si: u32, desc_val: JsValue,
) -> Result<(), DefineDescFailure> {
// [[DefineOwnProperty]] 触发(deferred namespace 先求值,不经 GOP 传导;
// symbol-like 键内部 no-op)。触发失败时 uncaught 侧通道已还原为原错误值,
// 原值重抛保身份(cyclic 无原值时按文本重建)。
if let Err(msg) = vm.ensure_deferred_ns_evaluation(unsafe { &*obj_ptr }, Some(key_si)) {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
return Err(DefineDescFailure::User(exc));
}
if !desc_val.is_object() {
return Err(DefineDescFailure::Engine("Property description must be an object".to_string()));
}
@@ -1060,6 +1096,16 @@ pub fn object_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
/// # 副作用
/// - 在 epoch 分配一个描述符对象
fn own_descriptor_of<H: VmHost>(vm: &mut H, obj: &JsObject, key_si: u32) -> Result<Option<JsValue>, JsValue> {
// deferred namespace 求值触发([[GetOwnProperty]],symbol-like 键内部 no-op)。
// 触发失败时 uncaught 侧通道已还原为原错误值,原值重抛保身份。
if let Err(msg) = vm.ensure_deferred_ns_evaluation(obj, Some(key_si)) {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
return Err(exc);
}
// 求值后描述符读真实 ns(deferred 对象自身槽位是预注册占位,活值在真实 ns)。
let obj = crate::module::deferred_ns_read_target(obj);
// TA 数值键:界内索引返回元素值 + 常量四字段描述符(writable/enumerable/
// configurable 恒真);数字无效与非数字串键落下方槽位路径(真实 own 属性
// 原样返回,无属性则 None)。
@@ -1194,6 +1240,10 @@ pub fn object_get_own_property_descriptors<H: VmHost>(vm: &mut H, args: &[u8]) -
};
let obj = unsafe { &*obj_ptr };
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
return NativeResult::Err(exc);
}
// 三段自身键序:字符串/整数键(整键升序在前)后接 Symbol 键插入序。
let mut keys = walk_own_keys(vm, obj);
@@ -1245,6 +1295,20 @@ fn push_desc_prop(obj: &mut JsObject, shape_forge: &ShapeForge, prop_si: u32, va
obj.push_prop(val);
}
/// deferred namespace [[OwnPropertyKeys]] 触发(无键):失败时 uncaught 侧通道
/// 已还原为原错误值,原值重抛保身份(cyclic 无原值时按文本重建)。
fn ensure_deferred_own_keys<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<(), JsValue> {
match vm.ensure_deferred_ns_evaluation(obj, None) {
Ok(()) => Ok(()),
Err(msg) => {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
Err(exc)
}
}
}
/// 校验并取出 Object 类方法的目标对象:`args[0]` 为接收者,`args[1]` 为待转换值,
/// 缺参或值经 ToObject 失败(null/undefined)时抛 TypeError。
fn require_obj_arg<H: VmHost>(vm: &mut H, args: &[u8], fn_name: &str) -> Result<*mut JsObject, JsValue> {
@@ -1313,6 +1377,10 @@ pub fn object_freeze<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
return NativeResult::Ok(val);
}
let obj_ptr = val.as_js_object_ptr();
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, unsafe { &*obj_ptr }) {
return NativeResult::Err(exc);
}
{
let obj = unsafe { &*obj_ptr };
// 模块命名空间导出保持 writable:true,冻结须失败(规范 SetIntegrityLevel)。
@@ -1363,6 +1431,10 @@ pub fn object_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
return NativeResult::Ok(val);
}
let obj_ptr = val.as_js_object_ptr();
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, unsafe { &*obj_ptr }) {
return NativeResult::Err(exc);
}
{
let obj = unsafe { &mut *obj_ptr };
let keys = walk_own_keys(vm, obj);
@@ -1418,6 +1490,10 @@ pub fn object_is_frozen<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
return NativeResult::Ok(JsValue::bool(true));
}
let obj = unsafe { &*obj_ptr };
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
return NativeResult::Err(exc);
}
if obj.is_frozen() {
return NativeResult::Ok(JsValue::bool(true));
}
@@ -1454,6 +1530,10 @@ pub fn object_is_sealed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
return NativeResult::Ok(JsValue::bool(true));
}
let obj = unsafe { &*obj_ptr };
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
return NativeResult::Err(exc);
}
if obj.is_sealed() {
return NativeResult::Ok(JsValue::bool(true));
}
@@ -1500,6 +1580,10 @@ pub fn object_get_own_property_names<H: VmHost>(vm: &mut H, args: &[u8]) -> Nati
let obj_ptr = native_try!(require_obj_arg(vm, args, "getOwnPropertyNames"));
let obj = unsafe { &*obj_ptr };
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
return NativeResult::Err(exc);
}
let keys = walk_own_keys(vm, obj);
let n = keys.len();
let array_proto = vm.builtin_proto(ProtoKind::ArrayProto);
@@ -2121,6 +2205,10 @@ pub fn object_proto_define_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeR
pub fn object_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj_ptr = native_try!(require_obj_arg(vm, args, "entries"));
let obj = unsafe { &*obj_ptr };
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
return NativeResult::Err(exc);
}
let keys = walk_own_keys(vm, obj);
let owned_keys: Vec<(u32, u32)> = keys
.into_iter()
@@ -2296,6 +2384,10 @@ pub fn object_group_by<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn object_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj_ptr = native_try!(require_obj_arg(vm, args, "values"));
let obj = unsafe { &*obj_ptr };
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
return NativeResult::Err(exc);
}
let keys = walk_own_keys(vm, obj);
let owned_keys: Vec<(u32, u32)> = keys
.into_iter()
+17 -2
View File
@@ -157,7 +157,11 @@ pub fn reflect_delete_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
};
let target = unsafe { &mut *target_ptr };
NativeResult::Ok(JsValue::bool(delete_own_property(vm, target, key_si)))
match delete_own_property(vm, target, key_si) {
Ok(deleted) => NativeResult::Ok(JsValue::bool(deleted)),
// deferred namespace 求值触发失败(cyclic / 失败缓存重抛)原值上抛。
Err(exc) => NativeResult::Err(exc),
}
}
/// `Reflect.get(target, key, receiver)`:读取属性(含原型链与 accessor)。
@@ -213,7 +217,11 @@ pub fn reflect_has<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
Ok(si) => si,
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
};
NativeResult::Ok(JsValue::bool(vm.has_property(unsafe { &*target_ptr }, key_si)))
// deferred namespace 目标先触发求值(cyclic 抛错 / 失败缓存重抛)。
match vm.has_property(unsafe { &*target_ptr }, key_si) {
Ok(found) => NativeResult::Ok(JsValue::bool(found)),
Err(msg) => NativeResult::Err(from_engine_error(vm, &msg)),
}
}
/// `Reflect.isExtensible(target)`:对象是否可扩展。
@@ -240,6 +248,13 @@ pub fn reflect_own_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
return type_error(vm, "Reflect.ownKeys target is not an object");
};
let target = unsafe { &*target_ptr };
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
if let Err(msg) = vm.ensure_deferred_ns_evaluation(target, None) {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
return NativeResult::Err(exc);
}
// Symbol 键分流在消费端追加:底层字符串枚举源(Object.keys / JSON / for-in)
// 语义不变,不泄漏 Symbol 键。
@@ -335,6 +335,14 @@ fn fill_object_props<H: VmHost>(
vm: &mut H, state: &mut CloneState, src: &JsObject, clone_ptr: *mut JsObject,
) -> Result<(), JsValue> {
let src_val = JsValue::from_js_object(src as *const JsObject as *mut JsObject);
// [[OwnPropertyKeys]] 触发(源对象,deferred namespace 先求值,cyclic 抛错)。
// plain 对象判定已排除 module namespace,此触发对实际可达路径恒 no-op。
if let Err(msg) = vm.ensure_deferred_ns_evaluation(src, None) {
let exc = vm
.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
return Err(exc);
}
let str_keys = walk_own_keys(vm, src);
let sym_keys = walk_own_symbol_keys(vm, src);
for (si, store) in str_keys {