feat: 模块命名空间枚举读活值并支持写穿

This commit is contained in:
2026-09-18 00:02:21 +00:00
parent cafac572fa
commit ed65b5282f
11 changed files with 263 additions and 22 deletions
+6
View File
@@ -14,6 +14,12 @@ use oxide_types::value::JsValue;
/// `Symbol.toStringTag` 在 well-known symbol 表中的序号,命名空间标签键按其编码。
const TO_STRING_TAG_SYMBOL_ID: u32 = 9;
/// 未初始化导出被读时的 ReferenceError 文本。
///
/// exotic `[[Get]]` 与 `[[GetOwnProperty]]` 消费端(`Object.*`、for-in)共用同一
/// 文本,错误类型由消费端的 `create_reference_error` / `create_from_text` 恢复。
pub const NS_UNINITIALIZED_MESSAGE: &str = "Cannot access module export before initialization";
/// 模块命名空间导出属性描述符:可写、可枚举、不可配置(module namespace exotic)。
///
/// 规范 10.4.6.5 返回 `{writable: true, enumerable: true, configurable: false}`;
+57 -5
View File
@@ -469,6 +469,25 @@ pub fn object_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::from_js_object(obj))
}
/// 模块命名空间字符串导出按 exotic `[[GetOwnProperty]]` 读取活值状态:
/// - `Ok(Some(v))`:该键是已初始化导出,`v` 为条目活值;
/// - `Ok(None)`:非 module namespace / 非导出名 / symbol 键,调用方落普通属性路径;
/// - `Err(msg)`:导出名已预注册但未初始化,调用方抛 `msg` 对应的 ReferenceError。
///
/// # 注意事项
/// - 只查条目表、不读真实属性槽:条目表是 live 命名空间的权威状态,真实槽可能
/// 只是初始化前的 `undefined` 占位。
fn namespace_export_get(obj: &JsObject, key_si: u32) -> Result<Option<JsValue>, &'static str> {
if is_symbol_key(key_si) {
return Ok(None);
}
match crate::module::module_ns_export(obj, 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),
}
}
/// `Object.keys(obj)`:返回可枚举自身属性的字符串名数组。
pub fn object_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj_ptr = match require_obj_arg(vm, args, "keys") {
@@ -485,6 +504,12 @@ pub fn object_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable())
})
.collect();
// EnumerableOwnProperties 对每个字符串键走 `? [[GetOwnProperty]]`:未初始化导出抛错。
for (si, _offset) in &owned_keys {
if let Err(msg) = namespace_export_get(obj, *si) {
return NativeResult::Err(crate::error::create_reference_error(vm, msg));
}
}
let n = owned_keys.len();
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.alloc_object(JsObject::new_array(
@@ -580,7 +605,8 @@ pub fn object_assign<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
// 触发 getter;getter 抛错按规范中断整个 assign。
let val = match vm.ordinary_get(source, si, source_val) {
Ok(v) => v,
Err(err) => return NativeResult::Err(crate::error::create_type_error(vm, &err)),
// ReferenceError 等错误文本经 create_from_text 恢复类型,不得降级为 TypeError。
Err(err) => return NativeResult::Err(crate::error::create_from_text(vm, &err)),
};
all_assignments.push((si, val));
}
@@ -848,7 +874,12 @@ pub fn object_get_own_property_descriptor<H: VmHost>(vm: &mut H, args: &[u8]) ->
let Some(offset) = vm.get_own_property_slot(obj, key) else {
return NativeResult::Ok(JsValue::undefined());
};
let found_value = obj.get_prop_at(offset);
// 模块命名空间字符串导出:`? [[Get]]` 读条目活值,未初始化抛 ReferenceError。
let found_value = match namespace_export_get(obj, key) {
Ok(Some(value)) => value,
Ok(None) => obj.get_prop_at(offset),
Err(msg) => return NativeResult::Err(crate::error::create_reference_error(vm, msg)),
};
let found_meta = obj.prop_meta_at(offset);
let sf_ptr = vm.kernel_core().perm_interner().as_ref() as *const PermInterner;
@@ -1345,7 +1376,14 @@ pub fn object_proto_has_own_property<H: VmHost>(vm: &mut H, args: &[u8]) -> Nati
Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)),
};
let obj = unsafe { &*obj_val.as_js_object_ptr() };
NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some()))
if vm.get_own_property_slot(obj, key_si).is_none() {
return NativeResult::Ok(JsValue::bool(false));
}
// HasOwnProperty 经 `? [[GetOwnProperty]]`:未初始化导出抛 ReferenceError。
if let Err(msg) = namespace_export_get(obj, key_si) {
return NativeResult::Err(crate::error::create_reference_error(vm, msg));
}
NativeResult::Ok(JsValue::bool(true))
}
/// `Object.prototype.propertyIsEnumerable(key)`:指定自身属性是否可枚举。
@@ -1367,6 +1405,10 @@ pub fn object_proto_property_is_enumerable<H: VmHost>(vm: &mut H, args: &[u8]) -
let Some(pos) = vm.get_own_property_slot(obj, key_si) else {
return NativeResult::Ok(JsValue::bool(false));
};
// PropertyIsEnumerable 同样先经 `? [[GetOwnProperty]]` 再取 enumerable 位。
if let Err(msg) = namespace_export_get(obj, key_si) {
return NativeResult::Err(crate::error::create_reference_error(vm, msg));
}
let enumerable = obj
.prop_meta_at(pos)
.map(|meta| meta.attributes.enumerable())
@@ -1453,7 +1495,12 @@ pub fn object_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
));
for (i, (si, offset)) in owned_keys.iter().enumerate() {
let key_val = key_si_to_js_value(vm, *si);
let val = obj.get_prop_at(*offset);
// EnumerableOwnProperties 的 "key+value":值经条目活值查询,未初始化抛错。
let val = match namespace_export_get(obj, *si) {
Ok(Some(value)) => value,
Ok(None) => obj.get_prop_at(*offset),
Err(msg) => return NativeResult::Err(crate::error::create_reference_error(vm, msg)),
};
let pair = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
@@ -1636,7 +1683,12 @@ pub fn object_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
vm.epoch().bump(),
));
for (i, (_si, offset)) in owned_keys.iter().enumerate() {
let val = obj.get_prop_at(*offset);
// EnumerableOwnProperties 的 "value":值经条目活值查询,未初始化抛错。
let val = match namespace_export_get(obj, *_si) {
Ok(Some(value)) => value,
Ok(None) => obj.get_prop_at(*offset),
Err(msg) => return NativeResult::Err(crate::error::create_reference_error(vm, msg)),
};
unsafe {
(*arr).set_prop_at(i, val);
}