//! 模块命名空间与求值辅助(import 实现内部用,非 JS 可见标准 API)。 //! //! 命名约定 `__module*`:由编译器模块 prelude 发出(`lookup_or_builtin` 解析为全局槽, //! VM 在 frame push 时从 global 对象取回 native 函数)。依赖模块先整体求值并返回 //! 命名空间对象,导入方从中读取导出值;依赖含可重赋导出时命名/默认导入经 //! `__moduleGet` 活读,被捕获导出由 `__moduleSetCell` 挂共享 cell。 //! source-phase / defer 语义留待后续轮次。 use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_runtime_api::{to_string, NativeResult, VmHost}; use oxide_types::object::{Cell, JsObject, PropAttributes, PropMetaEntry}; use oxide_types::private_key::encode_symbol_key; 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}`; /// 写保护由命名空间的 `[[Set]]`/`[[DefineOwnProperty]]` 专属语义承担,不靠描述符位。 fn ns_attrs() -> PropAttributes { PropAttributes::new(true, true, false) } fn type_error(vm: &mut H, msg: &str) -> NativeResult { NativeResult::Err(crate::error::create_type_error(vm, msg)) } /// 命名空间条目表的来源身份(provenance)。 /// /// `Local` 为本模块直接声明的导出;`Reexport` 描述跨模块再导出的 `(模块, 绑定)` /// 身份,`direct` 区分显式再导出(`export { x } from`)与 star 复制的间接导出; /// `Ambiguous` 是 star 冲突后的省略哨兵,该名不再出现在命名空间且一旦歧义恒歧义。 #[derive(Clone, Copy)] pub enum ModuleNsOrigin { Local, Reexport { path_id: u32, binding_id: u32, direct: bool }, Ambiguous, } /// 单个导出名的活值状态。 pub enum ModuleNsState { /// 预注册后未初始化的一般导出;`initialized` 为 true 时 `value` 是当前活值。 Value { value: JsValue, initialized: bool }, /// 被捕获导出挂的共享 cell,活值由 cell 承载。 Cell(*mut Cell), } /// 命名空间条目:导出名 interned 键、活值状态与来源身份一一对应。 pub struct ModuleNsEntry { pub name_si: u32, pub origin: ModuleNsOrigin, pub state: ModuleNsState, } /// deferred namespace 状态盒:`[[Module]]`/`[[Evaluated]]`/`[[Namespace]]` 三内部槽。 /// /// 以 `Option` 内嵌进 `ModuleNsTable`(`native_data` 的唯一分配),不新建 JsObject /// 字段、不建第二个 Box——GC 边、字节核算、释放全部复用 `ModuleNamespace` 家族链, /// 零新增注册点。 pub struct DeferredNsState { /// `[[Module]]`:依赖模块函数(编译期 CREATE_CLOSURE 的闭包对象),或 /// undefined(哨兵:自导入/祖先引用,模块函数在导入方 prelude 期不可得)。 pub module: JsValue, /// `[[Evaluated]]`:依赖模块是否已求值(mixed 依赖构造时即为真; /// 求值完成(成功或失败)后置真,不回退)。 pub evaluated: bool, /// `[[Namespace]]`:求值后返回的命名空间对象(mixed 依赖构造时即为真实 ns; /// 求值前为 undefined)。 pub namespace: JsValue, /// 求值失败时缓存的原始错误值(再读再抛同一错误对象,保身份); /// 成功求值与求值前恒 `None`。 pub error: Option, /// 依赖模块规范路径:求值触发点据此查 per-module 状态(提升求值过的 TLA /// 模块已结算,直接取缓存命名空间,防双求值)。 pub path: String, } /// 模块命名空间条目表:挂在 ns 对象 `native_data` 上,是 exotic [[Get]] 的权威 /// 状态。键为 `PermInterner` interned 键,线性查找(导出名通常少于 50)。 /// /// `deferred` 仅对 deferred namespace 对象为 `Some`(`__moduleDeferObject` 安装), /// 其余 ns 对象恒 `None`;求值触发点(后续轮次)经 `deferred_state` 只读消费。 pub struct ModuleNsTable { entries: Vec, deferred: Option, } /// 导出名状态查询结果。 pub enum ModuleNsQuery { /// 未初始化(预注册未赋值,或未初始化的共享 cell)。 Uninitialized, /// 已初始化活值(`Value` 条目或已初始化 cell)。 Initialized(JsValue), } /// 取 ns 对象的条目表指针;非 module namespace 或无表返回空指针。 /// /// # 注意事项 /// - 返回指针由 ns 对象 `native_data` 持有,在该对象存活期内有效;消费方须在 /// 同一 VM 世代内使用。 fn ns_table_ptr(obj: &JsObject) -> *mut ModuleNsTable { if !obj.is_module_namespace() { return std::ptr::null_mut(); } obj.native_data() as *mut ModuleNsTable } /// 只读访问 deferred namespace 状态盒(`[[Module]]`/`[[Evaluated]]`/`[[Namespace]]`)。 /// /// # 边界与前提 /// - 返回 `None`:非 module namespace / 无条目表 / 非 deferred 对象(`deferred` 为 `None`)。 /// - 返回 `Some`:deferred 对象且已安装状态盒,供求值触发点消费三内部槽。 /// /// # 注意事项 /// - 写入点唯一(`module_defer_object` 安装),本访问器只读,不改状态盒。 pub fn deferred_state(obj: &JsObject) -> Option<&DeferredNsState> { let table = ns_table_ptr(obj); if table.is_null() { return None; } // SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。 let table_ref = unsafe { &*table }; 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 { table.entries.iter().position(|e| e.name_si == key_si) } /// 分配空条目表并挂到 ns 对象的 `native_data`。 fn install_ns_table(obj: &mut JsObject) -> *mut ModuleNsTable { let table = Box::into_raw(Box::new(ModuleNsTable { entries: Vec::new(), deferred: None, })); obj.set_native_data(table as *mut u8); table } /// 追加一条未初始化的 `Value` 条目(来源为本地导出)。 /// /// # 注意事项 /// - 调用方保证 `name_si` 尚未入表(入口均为先查后插)。 fn push_uninitialized_entry(table: *mut ModuleNsTable, name_si: u32) { push_entry( table, name_si, ModuleNsOrigin::Local, ModuleNsState::Value { value: JsValue::undefined(), initialized: false, }, ); } /// 追加一条已初始化为 undefined 的 `Value` 条目(来源为本地导出)。 /// /// # 边界与前提 /// - 供 var 作用域导出(`VarScopedDeclarations`)在实例化期预初始化:读时为 /// undefined 而非 TDZ,写点由 `__moduleSet` 就地覆盖。 fn push_var_initialized_entry(table: *mut ModuleNsTable, name_si: u32) { push_entry( table, name_si, ModuleNsOrigin::Local, ModuleNsState::Value { value: JsValue::undefined(), initialized: true, }, ); } /// 向条目表追加一条带来源身份的条目。 /// /// # 注意事项 /// - table 归调用方持有的 ns 对象所有,在同一 VM 世代内有效。 fn push_entry(table: *mut ModuleNsTable, name_si: u32, origin: ModuleNsOrigin, state: ModuleNsState) { let entry = ModuleNsEntry { name_si, origin, state }; // SAFETY: table 归调用方持有的 ns 对象所有,在同一 VM 世代内有效。 unsafe { (*table).entries.push(entry); } } /// 模块命名空间字符串导出名的状态查询(不抛错、不分配、不写状态)。 /// /// # 边界与前提 /// - 返回 `None`:非 module namespace / 无条目表 / 键非条目(含 symbol 键)。 /// - 返回 `Uninitialized`:预注册未赋值,或条目挂在未初始化的共享 cell 上。 /// - 返回 `Initialized(v)`:`Value` 已初始化,或 cell 已初始化。 /// /// # 注意事项 /// - `Cell` 读依赖「cell 由 `alloc_cell` 分配并登记,参与 mark-sweep 回收、 /// `full_reset` 为收尾兜底」的生命周期契约;同一 VM 世代内指针有效。 pub fn module_ns_export(obj: &JsObject, key_si: u32) -> Option { let table = ns_table_ptr(obj); if table.is_null() { return None; } // SAFETY: table 指向本 ns 对象持有的 Box,随对象存活。 let table_ref = unsafe { &*table }; let entry = table_ref.entries.get(entry_index(table_ref, key_si)?)?; // 歧义哨兵条目:该名已从命名空间省略,查询一律视为非导出(落普通路径)。 if matches!(entry.origin, ModuleNsOrigin::Ambiguous) { return None; } match &entry.state { ModuleNsState::Value { value, initialized } => { if *initialized { Some(ModuleNsQuery::Initialized(*value)) } else { Some(ModuleNsQuery::Uninitialized) } } ModuleNsState::Cell(cell) => { if cell.is_null() { return Some(ModuleNsQuery::Uninitialized); } // SAFETY: cell 由 alloc_cell 分配并登记,参与 mark-sweep、full_reset 收尾兜底。 let cell_ref = unsafe { &**cell }; if cell_ref.is_initialized() { Some(ModuleNsQuery::Initialized(cell_ref.value)) } else { Some(ModuleNsQuery::Uninitialized) } } } } /// 收集条目表持有的 JsValue 边(已初始化 `Value` 与全部 `Cell` 内值),供 GC mark。 /// /// deferred namespace 对象额外持有 `[[Module]]`、`[[Namespace]]` 与失败缓存 /// `error`(Some 时)三条边:依赖模块函数、求值后命名空间对象与失败错误对象 /// 须经本单一注册点入 mark,漏登则对应值在 deferred ns 存活期间被回收, /// 触发点解引用悬垂。 pub fn module_ns_native_edges(obj: &JsObject) -> Vec { let table = ns_table_ptr(obj); if table.is_null() { return Vec::new(); } // SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。 let table_ref = unsafe { &*table }; let mut edges = Vec::new(); for entry in &table_ref.entries { match &entry.state { ModuleNsState::Value { value, initialized } => { if *initialized { edges.push(*value); } } ModuleNsState::Cell(cell) => { if !cell.is_null() { // SAFETY: cell 生命周期见 `module_ns_export`。 edges.push(unsafe { (**cell).value }); } } } } 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 } /// 收集条目表持有的共享 cell 指针(`Cell` 条目),供 GC mark cell 边。 /// cell 指针不经对象图、须独立入存活集(与 `module_ns_native_edges` 的值边互补)。 pub fn module_ns_cell_edges(obj: &JsObject) -> Vec<*mut Cell> { let table = ns_table_ptr(obj); if table.is_null() { return Vec::new(); } // SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。 let table_ref = unsafe { &*table }; let mut out = Vec::new(); for entry in &table_ref.entries { if let ModuleNsState::Cell(cell) = &entry.state { if !cell.is_null() { out.push(*cell); } } } out } /// 只读核算条目表的 native 数据字节(不释放),与 `drop_module_ns_native` 的 /// capacity 口径一致,供 GC 账目核算。 pub fn module_ns_native_size(obj: &JsObject) -> u64 { let table = ns_table_ptr(obj); if table.is_null() { return 0; } // SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。 let table_ref = unsafe { &*table }; (std::mem::size_of::() + table_ref.entries.capacity() * std::mem::size_of::()) as u64 } /// 释放条目表(`Box`),返回释放字节数供泄漏统计。 /// /// # 注意事项 /// - 每对象的表至多释放一次;释放后 `native_data` 置空保证幂等。 pub fn drop_module_ns_native(obj: &mut JsObject) -> u64 { let table = ns_table_ptr(obj); if table.is_null() { return 0; } let bytes = module_ns_native_size(obj); // SAFETY: table 由 install_ns_table 的 Box::into_raw 分配,本处恰好释放一次。 unsafe { drop(Box::from_raw(table)); } obj.set_native_data(std::ptr::null_mut()); bytes } /// `__modulePreRegister(ns, name, var_like)`:live 模块在 body 求值前预注册静态导出名。 /// /// # 步骤 /// 1. 校验 ns 为 module namespace 对象,取导出名的 interned 键。 /// 2. 无表时分配;键已有条目时 no-op(幂等)。 /// 3. 经瞬态可扩展窗口在 ns 上定义真实数据属性 `undefined`(可写可枚举不可配置)。 /// 4. 追加条目:`var_like` 为真(var/函数类导出,属 `VarScopedDeclarations`)时 /// 初始化为 undefined;否则保持未初始化(lexical/class 的 TDZ)。随后按规范顺序 /// 重排导出键。 /// /// # 副作用 /// - 在 ns 上定义真实属性槽、挂载/扩展条目表,并重排 `[[OwnPropertyKeys]]` 顺序。 pub fn module_pre_register(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 4 { return type_error(vm, "__modulePreRegister: 3 arguments required"); } let ns_val = vm.reg(args[1]); let ns_ptr = match vm.checked_object_ptr(ns_val, "__modulePreRegister: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__modulePreRegister: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let name_val = vm.reg(args[2]); let name_si = vm.property_key_si(name_val); let var_like = oxide_runtime_api::to_boolean(vm.reg(args[3])); let obj = unsafe { &mut *ns_ptr }; if !obj.is_module_namespace() { return type_error(vm, "__modulePreRegister: target is not a module namespace"); } // 幂等:同名条目已存在时不再定义真实槽或重复入表。 let existing = ns_table_ptr(obj); if !existing.is_null() && unsafe { entry_index(&*existing, name_si) }.is_some() { return NativeResult::Ok(JsValue::undefined()); } // 导出定义窗口:同 module_set,窗口内不运行 JS,错误路径先复位扩展性。 obj.set_extensible(true); let result = vm.define_data_property(obj, name_si, JsValue::undefined(), ns_attrs()); obj.set_extensible(false); if let Err(e) = result { return NativeResult::Err(crate::error::create_error(vm, &e)); } let table = if existing.is_null() { install_ns_table(obj) } else { existing }; if var_like { push_var_initialized_entry(table, name_si); } else { push_uninitialized_entry(table, name_si); } crate::object::sort_namespace_exports(vm, obj); NativeResult::Ok(JsValue::undefined()) } /// `__moduleObject()`:创建模块命名空间对象(null 原型,带 @@toStringTag Symbol 键)。 /// /// # 副作用 /// - 对象创建起即标记 module namespace exotic 并置 non-extensible:导出定义由 /// `module_set`/`module_star` 的瞬态可扩展窗口旁路,其余写/定义一律拒绝。 pub fn module_object(vm: &mut H, _args: &[u8]) -> NativeResult { let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let tag_si = encode_symbol_key(vm.realm_id(), TO_STRING_TAG_SYMBOL_ID); let tag_val = vm.new_string("Module"); let obj_ref = unsafe { &mut *obj }; if let Err(e) = vm.define_data_property(obj_ref, tag_si, tag_val, PropAttributes::new(false, false, false)) { return NativeResult::Err(crate::error::create_error(vm, &e)); } obj_ref.set_module_namespace(true); obj_ref.set_extensible(false); NativeResult::Ok(JsValue::from_js_object(obj)) } /// 写入命名空间导出:命中条目则更新活值、真实属性槽与来源身份;否则经瞬态可扩展 /// 窗口定义真实属性槽,并在 ns 有条目表时追加条目。 /// /// # 边界与前提 /// - `Cell` 条目 no-op:被捕获导出的活值由共享 cell 承载,直接写槽会与 cell 分裂。 /// - 条目存在但真实槽缺失(歧义省略后显式导出重新定义):落新键定义路径补回槽。 /// /// # 副作用 /// - 修改条目表状态/来源身份与 ns 属性槽;新键或删槽重定义时重排 /// `[[OwnPropertyKeys]]` 顺序。 fn set_export( vm: &mut H, obj: &mut JsObject, name_si: u32, value: JsValue, origin: ModuleNsOrigin, ) -> Result<(), String> { let table = ns_table_ptr(obj); let existing = if table.is_null() { None } else { // SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。 unsafe { entry_index(&*table, name_si) } }; // Cell 条目 no-op:被捕获导出的活值由共享 cell 承载,写槽会与 cell 分裂。 if let Some(idx) = existing { // SAFETY: idx 来自刚完成的表内查找,仍在界内。 if matches!(unsafe { &(&(*table).entries)[idx].state }, ModuleNsState::Cell(_)) { return Ok(()); } } // 条目已存在且真实槽仍在:原地更新活值/来源,键集不变无需重排。 if let Some(idx) = existing { if let Some(pos) = vm.get_own_property_slot(obj, name_si) { obj.set_prop_at(pos, value); // SAFETY: idx 来自刚完成的表内查找,仍在界内。 unsafe { (&mut (*table).entries)[idx].origin = origin; (&mut (*table).entries)[idx].state = ModuleNsState::Value { value, initialized: true }; } return Ok(()); } // 歧义省略删槽后显式导出重新定义:落下方定义路径补回真实槽。 } // 导出定义窗口:命名空间创建起不可扩展,定义新导出须瞬态放开扩展性。窗口内 // 不运行 JS,无可观察副作用;错误路径先复位以维持 non-extensible 不变式。 obj.set_extensible(true); let result = vm.define_data_property(obj, name_si, value, ns_attrs()); obj.set_extensible(false); result?; if !table.is_null() { if let Some(idx) = existing { // SAFETY: idx 来自刚完成的表内查找;重定义后同步条目,勿重复入表。 unsafe { (&mut (*table).entries)[idx].origin = origin; (&mut (*table).entries)[idx].state = ModuleNsState::Value { value, initialized: true }; } } else { push_entry(table, name_si, origin, ModuleNsState::Value { value, initialized: true }); } } // 每次键集变化后重排:自导入在 body 内即可见有序的 [[OwnPropertyKeys]]。 crate::object::sort_namespace_exports(vm, obj); Ok(()) } /// 从命名空间属性表移除一个导出槽(歧义省略用),保留其余字符串与 Symbol 键及其 /// 描述符。 /// /// # 注意事项 /// - 命名空间导出属性为 non-configurable,本函数不经 `[[Delete]]`,直接重建属性表 /// 以同时保住 `@@toStringTag` Symbol 键。 fn remove_export_slot(vm: &mut H, obj: &mut JsObject, name_si: u32) { let mut retained: Vec<(u32, JsValue, Option)> = crate::object::walk_own_keys(vm, obj) .into_iter() .filter(|(si, _)| *si != name_si) .map(|(si, pos)| (si, obj.get_prop_at(pos), obj.prop_meta_at(pos))) .collect(); let symbols: Vec<(u32, JsValue, Option)> = crate::object::walk_own_symbol_keys(vm, obj) .into_iter() .map(|(si, pos)| (si, obj.get_prop_at(pos), obj.prop_meta_at(pos))) .collect(); obj.set_shape_id(EMPTY_SHAPE_ID); obj.clear_props(); for (si, value, meta) in retained.drain(..).chain(symbols) { let shape = vm.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(); } /// `__moduleSet(ns, name, value)`:在命名空间上定义或更新本地导出属性。 /// /// # 副作用 /// - 同 `set_export`;本地导出来源身份恒为 `Local`,覆盖先前 star 条目时复位来源。 pub fn module_set(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 4 { return type_error(vm, "__moduleSet: 3 arguments required"); } let ns_val = vm.reg(args[1]); let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleSet: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleSet: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let name_val = vm.reg(args[2]); let value = vm.reg(args[3]); let name_si = vm.property_key_si(name_val); let obj = unsafe { &mut *ns_ptr }; if let Err(e) = set_export(vm, obj, name_si, value, ModuleNsOrigin::Local) { return NativeResult::Err(crate::error::create_error(vm, &e)); } NativeResult::Ok(JsValue::undefined()) } /// `__moduleSetCell(ns, name, cell_idx)`:把导出条目挂到模块顶层帧的共享 cell。 /// /// # 步骤 /// 1. 校验 ns 为 module namespace 且已有条目表与同名条目(预注册保证存在)。 /// 2. 取当前字节码帧第 `cell_idx` 个 cell(`VmHost::module_frame_cell`)。 /// 3. 条目状态置 `Cell`,并把真实槽值同步为 cell 当前值一次。 /// /// # 边界与前提 /// - 条目必须已预注册;无表、条目未命中或 cell 缺失均抛 TypeError,不静默退化。 /// - `cell_idx` 来自 emit 期捕获索引,同一模块顶层帧内稳定。 /// /// # 副作用 /// - 修改条目表状态与 ns 真实槽值;后续活值由共享 cell 承载。 pub fn module_set_cell(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 4 { return type_error(vm, "__moduleSetCell: 3 arguments required"); } let ns_val = vm.reg(args[1]); let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleSetCell: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleSetCell: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let name_val = vm.reg(args[2]); let name_si = vm.property_key_si(name_val); let cell_idx = oxide_runtime_api::to_int32(vm.reg(args[3])) as u32; let obj = unsafe { &mut *ns_ptr }; if !obj.is_module_namespace() { return type_error(vm, "__moduleSetCell: target is not a module namespace"); } let table = ns_table_ptr(obj); if table.is_null() { return type_error(vm, "__moduleSetCell: export is not registered"); } // SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。 let Some(idx) = (unsafe { entry_index(&*table, name_si) }) else { return type_error(vm, "__moduleSetCell: export is not registered"); }; let Some(cell) = vm.module_frame_cell(cell_idx) else { return type_error(vm, "__moduleSetCell: cell is not available"); }; // SAFETY: cell 由 alloc_cell 分配并登记,参与 mark-sweep、full_reset 收尾兜底。 let value = unsafe { (*cell).value }; // SAFETY: idx 来自刚完成的表内查找,仍在界内。 unsafe { (&mut (*table).entries)[idx].state = ModuleNsState::Cell(cell); } // 真实槽同步一次 cell 当前值:条目表是活值权威,槽仅保留存在性与占位。 if let Some(pos) = vm.get_own_property_slot(obj, name_si) { obj.set_prop_at(pos, value); } NativeResult::Ok(JsValue::undefined()) } /// `__moduleSetReexport(ns, name, value, dep_path, imported)`:注册显式再导出 /// (`export { x } from` / `export * as ns from` / 导入绑定再导出),记录跨模块 /// 来源身份 `(dep_path, imported)`,使同一绑定经不同路径转发时来源 token 相等。 /// /// # 副作用 /// - 同 `module_set`;ns 无条目表时惰性安装,来源标为 `direct` 再导出。 pub fn module_set_reexport(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 6 { return type_error(vm, "__moduleSetReexport: 5 arguments required"); } let ns_val = vm.reg(args[1]); let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleSetReexport: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleSetReexport: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let name_val = vm.reg(args[2]); let value = vm.reg(args[3]); let path_val = vm.reg(args[4]); let imported_val = vm.reg(args[5]); let name_si = vm.property_key_si(name_val); let Some(dep_path) = vm.lookup_str(path_val) else { return type_error(vm, "__moduleSetReexport: module path is not a string"); }; let Some(imported) = vm.lookup_str(imported_val) else { return type_error(vm, "__moduleSetReexport: imported name is not a string"); }; let obj = unsafe { &mut *ns_ptr }; if !obj.is_module_namespace() { return type_error(vm, "__moduleSetReexport: target is not a module namespace"); } if ns_table_ptr(obj).is_null() { install_ns_table(obj); } let path_id = vm.perm_intern(&dep_path); let binding_id = vm.perm_intern(&imported); let origin = ModuleNsOrigin::Reexport { path_id, binding_id, direct: true, }; if let Err(e) = set_export(vm, obj, name_si, value, origin) { return NativeResult::Err(crate::error::create_error(vm, &e)); } NativeResult::Ok(JsValue::undefined()) } /// `__moduleAlias(ns, name, fallback)`:自别名收尾刷新用。命名空间存在该导出时 /// 返回其值,否则返回传入的 `fallback`(不抛错):star 未提供该名时占位值原样 /// 保留,与现行「星号未提供该名时静默 undefined」行为一致。 /// /// # 边界与前提 /// - 命名空间无该导出时不抛错(区别于 `__moduleGet` 的 TypeError 与 /// `__moduleLinkGet` 的 SyntaxError),缺失臂返回 `fallback` 原值。 /// /// # 副作用 /// - 无:只读命名空间属性,不修改条目表。 pub fn module_alias(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 4 { return type_error(vm, "__moduleAlias: 3 arguments required"); } let ns_val = vm.reg(args[1]); let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleAlias: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleAlias: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let name_val = vm.reg(args[2]); let name_si = vm.property_key_si(name_val); let fallback = vm.reg(args[3]); let obj = unsafe { &*ns_ptr }; if vm.get_own_property_slot(obj, name_si).is_some() { match vm.ordinary_get(obj, name_si, ns_val) { Ok(v) => NativeResult::Ok(v), Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)), } } else { NativeResult::Ok(fallback) } } /// `__moduleGet(ns, name)`:读取命名空间导出属性。 pub fn module_get(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return type_error(vm, "__moduleGet: 2 arguments required"); } let ns_val = vm.reg(args[1]); let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleGet: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleGet: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let name_val = vm.reg(args[2]); let name_si = vm.property_key_si(name_val); let obj = unsafe { &*ns_ptr }; // 模块命名空间 exotic Get:未导出属性抛 TypeError(而非 undefined)。 if vm.get_own_property_slot(obj, name_si).is_none() { return type_error(vm, "__moduleGet: requested export is not exported"); } match vm.ordinary_get(obj, name_si, ns_val) { Ok(v) => NativeResult::Ok(v), Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)), } } /// `__moduleLinkGet(ns, name)`:import 绑定初始化(链接期语义)。 /// 与 `__moduleGet` 的区别:缺失导出抛 SyntaxError(模块声明实例化期的 /// 绑定解析错误),而非命名空间访问的 TypeError。 pub fn module_link_get(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return type_error(vm, "__moduleLinkGet: 2 arguments required"); } let ns_val = vm.reg(args[1]); let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleLinkGet: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleLinkGet: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let name_val = vm.reg(args[2]); let name_si = vm.property_key_si(name_val); let obj = unsafe { &*ns_ptr }; if vm.get_own_property_slot(obj, name_si).is_none() { return NativeResult::Err(crate::error::create_syntax_error(vm, "requested module export is not exported")); } match vm.ordinary_get(obj, name_si, ns_val) { Ok(v) => NativeResult::Ok(v), Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)), } } /// 取 src 命名空间某导出名的来源身份。 /// /// # 边界与前提 /// - src 条目表命中且来源为 `Reexport`:沿用其 `(模块, 绑定)`,`direct` 归一为 /// false(经 star 复制到 dst 的是间接提供)。 /// - 其余(无表 / `Local` / 条目缺失):回退 `(src_path_id, name_si)`。只声明本地 /// 导出的源不建条目表,回退 token 使不同源的同名本地导出身份不同。 fn star_source_origin(src: &JsObject, name_si: u32, src_path_id: u32) -> ModuleNsOrigin { let table = ns_table_ptr(src); if !table.is_null() { // SAFETY: table 归 src 持有,生命周期见 `module_ns_export`。 if let Some(idx) = unsafe { entry_index(&*table, name_si) } { // SAFETY: idx 来自刚完成的表内查找,仍在界内。 if let ModuleNsOrigin::Reexport { path_id, binding_id, .. } = unsafe { (&(*table).entries)[idx].origin } { return ModuleNsOrigin::Reexport { path_id, binding_id, direct: false, }; } } } ModuleNsOrigin::Reexport { path_id: src_path_id, binding_id: name_si, direct: false, } } /// `__moduleStar(dst, src, src_path)`:把 src 命名空间的可枚举导出(除 default) /// 复制到 dst,并按来源身份解析同名冲突。 /// /// # 步骤 /// 1. 每个 src 导出名取来源 `(模块, 绑定)`;`default` 跳过。 /// 2. dst 条目为 `Ambiguous`:恒跳过(一旦歧义恒省略)。 /// 3. dst 条目为显式导出(`Local` / direct `Reexport`):显式恒胜,跳过。 /// 4. dst 条目为间接 `Reexport`:`(模块, 绑定)` 相同则保留;不同则删槽并记 /// `Ambiguous`。 /// 5. dst 无条目:真实槽已存在视为显式导出跳过;否则复制槽并记间接来源。 /// /// # 副作用 /// - 在 dst 上定义/删除真实属性槽,惰性安装条目表,并按规范顺序重排导出键。 pub fn module_star(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 4 { return type_error(vm, "__moduleStar: 3 arguments required"); } let dst_val = vm.reg(args[1]); let src_val = vm.reg(args[2]); let src_path_val = vm.reg(args[3]); let dst_ptr = match vm.checked_object_ptr(dst_val, "__moduleStar: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleStar: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let src_ptr = match vm.checked_object_ptr(src_val, "__moduleStar: source is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleStar: source is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let Some(src_path) = vm.lookup_str(src_path_val) else { return type_error(vm, "__moduleStar: source path is not a string"); }; let src_path_id = vm.perm_intern(&src_path); let src = unsafe { &*src_ptr }; let default_si = vm.perm_intern("default"); let keys = crate::object::walk_own_keys(vm, src); let dst = unsafe { &mut *dst_ptr }; for (name_si, pos) in keys { if name_si == default_si { continue; } let src_origin = star_source_origin(src, name_si, src_path_id); // live dst:star 复制的新导出名须一并入表,否则活值读/枚举看不到条目。 let dst_table = ns_table_ptr(dst); let dst_idx = if dst_table.is_null() { None } else { unsafe { entry_index(&*dst_table, name_si) } }; match dst_idx { Some(idx) => { // SAFETY: idx 来自刚完成的表内查找,仍在界内。 let dst_origin = unsafe { (&(*dst_table).entries)[idx].origin }; match dst_origin { ModuleNsOrigin::Ambiguous => continue, ModuleNsOrigin::Local | ModuleNsOrigin::Reexport { direct: true, .. } => continue, ModuleNsOrigin::Reexport { path_id, binding_id, .. } => { let ModuleNsOrigin::Reexport { path_id: sp, binding_id: sb, .. } = src_origin else { continue; }; if path_id == sp && binding_id == sb { continue; } // 不同绑定同名:删除导出槽并记歧义哨兵,清空可回收的活值边。 remove_export_slot(vm, dst, name_si); // SAFETY: idx 仍指向本表条目,删除槽不改动条目表。 unsafe { (&mut (*dst_table).entries)[idx].origin = ModuleNsOrigin::Ambiguous; (&mut (*dst_table).entries)[idx].state = ModuleNsState::Value { value: JsValue::undefined(), initialized: false, }; } } } } None => { // 真实槽已存在但无条目:视为先前显式导出(本地导出不建表),显式恒胜。 if vm.get_own_property_slot(dst, name_si).is_some() { continue; } let value = src.get_prop_at(pos); let table = if dst_table.is_null() { install_ns_table(dst) } else { dst_table }; // star 导出同 module_set:定义新导出走瞬态可扩展窗口,错误路径先复位。 dst.set_extensible(true); let result = vm.define_data_property(dst, name_si, value, ns_attrs()); dst.set_extensible(false); if let Err(e) = result { return NativeResult::Err(crate::error::create_error(vm, &e)); } // 快照语义:star 名立即视为已初始化。 push_entry(table, name_si, src_origin, ModuleNsState::Value { value, initialized: true }); } } } // star 复制后重排:直接导出与 star 导出的合并键序保持规范顺序。 crate::object::sort_namespace_exports(vm, dst); NativeResult::Ok(JsValue::undefined()) } /// `__moduleSeal(ns)`:封冻命名空间(不可扩展)。 pub fn module_seal(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return type_error(vm, "__moduleSeal: 1 argument required"); } let ns_val = vm.reg(args[1]); let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleSeal: target is not an object") { Ok(Some(p)) => p, Ok(None) => return type_error(vm, "__moduleSeal: target is not an object"), Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; let obj = unsafe { &mut *ns_ptr }; obj.set_extensible(false); NativeResult::Ok(ns_val) } /// `__moduleEval(fn, path)`:求值依赖模块(fn 为编译期 CREATE_CLOSURE 的函数对象, /// path 为模块规范路径),返回其命名空间。有状态:per-module 状态按 path 键防双求值。 /// /// # 步骤 /// 1. 查 per-module 状态(按 path 键):已求值返缓存命名空间(no-op); /// 启动中(capability promise)已结算取结算值返命名空间,未结算返 promise /// 本体(调用方聚合 gate 据 async 标志 await 之,不重跑、不抛错)。 /// 2. 未求值:启动模块(`call_function_sync`,TLA 模块挂起返 promise、同步模块 /// 返命名空间),置状态(pending 载 promise / evaluated 载命名空间),返返值。 /// /// # 副作用 /// - 调用前置「正在求值模块」标记为 `Some(fn)`、返回后还原(save/restore,嵌套 /// `__moduleEval` 安全):deferred namespace 求值触发的 cyclic 守卫据此判自求值。 /// - 写 per-module 状态(键 = path)。 pub fn module_eval(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return type_error(vm, "__moduleEval: 2 arguments required"); } let fn_val = vm.reg(args[1]); let path_val = vm.reg(args[2]); if !fn_val.is_object() { return type_error(vm, "__moduleEval: target is not a function"); } let ptr = fn_val.as_js_object_ptr(); if ptr.is_null() || !unsafe { (*ptr).is_function() } { return type_error(vm, "__moduleEval: target is not a function"); } let path = to_string(path_val); // 已求值:直接返缓存命名空间(no-op,防 hoisted TLA 模块与 deferred 模块 // prelude 的双求值)。 if let Some(state) = vm.get_module_eval_state(&path) { if state.evaluated { return NativeResult::Ok(state.namespace); } // 启动中:capability promise 已结算(调用方在聚合 gate 之后),取结算值; // 未结算(提升 TLA 模块互为依赖,先启动者尚未被外层 gate 排空)返 promise // 本体,调用方聚合 gate 据 async 标志 await 之——不重跑、不抛错。 let promise = state.namespace; return match vm.promise_settled_value(promise) { Some((true, value)) => { vm.set_module_eval_state(&path, true, value); NativeResult::Ok(value) } Some((false, reason)) => { let text = to_string(reason); NativeResult::Err(crate::error::create_from_text(vm, &text)) } None => NativeResult::Ok(promise), }; } // 置位标记,调用返回后无条件还原(异常展开亦不残留)。 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) => { // TLA 模块返 capability promise(挂起未结算),同步模块返命名空间。 if vm.is_promise_value(v) { vm.set_module_eval_state(&path, false, v); } else { vm.set_module_eval_state(&path, true, v); } NativeResult::Ok(v) } Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)), }; vm.set_evaluating_module(saved); result } /// `__moduleDeferObject(fn, real_ns, path)`:创建 deferred namespace 对象 /// (`import defer * as` 产物)。 /// /// # 步骤 /// 1. 校验 `fn` 为函数对象(依赖模块函数,编译期 CREATE_CLOSURE 的闭包对象) /// 或 undefined(哨兵:自导入/祖先引用,模块函数在导入方 prelude 期不可得); /// 校验 `real_ns` 为对象(mixed:模块已急切求值)或 undefined。 /// 2. 建 null 原型对象,定义 `@@toStringTag` = "Deferred Module"(不可写不可枚举不可配置)。 /// 3. 置 module namespace + deferred 双标志,置 non-extensible。 /// 4. 安装条目表并写状态盒:`[[Module]]` = fn、`[[Evaluated]]` = `real_ns` 为对象、 /// `[[Namespace]]` = `real_ns`(或 undefined)、失败错误缓存恒 `None`、 /// `path` = 依赖模块规范路径(求值触发点查 per-module 状态防双求值)。 /// /// # 副作用 /// - 新对象创建起即标记 module namespace exotic 并置 non-extensible(同 `__moduleObject`); /// 状态盒内部槽由本入口唯一安装。 pub fn module_defer_object(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 4 { return type_error(vm, "__moduleDeferObject: 3 arguments required"); } let fn_val = vm.reg(args[1]); let ns_val = vm.reg(args[2]); let path = to_string(vm.reg(args[3])); // fn 为函数或 undefined(哨兵);其余抛 TypeError。 let fn_is_function = fn_val.is_object() && { let ptr = fn_val.as_js_object_ptr(); !ptr.is_null() && unsafe { (*ptr).is_function() } }; if !fn_is_function && !fn_val.is_undefined() { return type_error(vm, "__moduleDeferObject: first argument must be a function or undefined"); } // real_ns 为对象(mixed 已求值态)或 undefined;其余抛 TypeError。 if !ns_val.is_object() && !ns_val.is_undefined() { return type_error(vm, "__moduleDeferObject: second argument must be an object or undefined"); } let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let tag_si = encode_symbol_key(vm.realm_id(), TO_STRING_TAG_SYMBOL_ID); let tag_val = vm.new_string("Deferred Module"); let obj_ref = unsafe { &mut *obj }; if let Err(e) = vm.define_data_property(obj_ref, tag_si, tag_val, PropAttributes::new(false, false, false)) { return NativeResult::Err(crate::error::create_error(vm, &e)); } obj_ref.set_module_namespace(true); obj_ref.set_deferred(true); obj_ref.set_extensible(false); let table = install_ns_table(obj_ref); // SAFETY: table 由 install_ns_table 的 Box::into_raw 分配,本处恰好写入一次。 unsafe { (*table).deferred = Some(DeferredNsState { module: fn_val, evaluated: ns_val.is_object(), namespace: ns_val, error: None, path, }); } NativeResult::Ok(JsValue::from_js_object(obj)) } /// `__moduleData(kind, content)`:构造数据模块命名空间(json/text;bytes 未支持)。 /// /// # 副作用 /// - 同 `__moduleObject`:标记 module namespace exotic 并置 non-extensible。 pub fn module_data(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return type_error(vm, "__moduleData: 2 arguments required"); } let kind_val = vm.reg(args[1]); let content_val = vm.reg(args[2]); if !kind_val.is_string() || !content_val.is_string() { return type_error(vm, "__moduleData: kind/content must be strings"); } let kind = oxide_runtime_api::to_string(kind_val); let default_val = match kind.as_str() { "json" => { // 复用 JSON.parse 的 serde 管线:args[1] 即内容字符串。 let parse_result = crate::json::json_parse(vm, &[args[0], args[2]]); match parse_result { NativeResult::Ok(v) => v, NativeResult::Err(e) => return NativeResult::Err(e), NativeResult::TailCall { .. } => return type_error(vm, "__moduleData: unexpected tail call"), } } "text" => { // lossy 文本拷贝出 owned String 再入堆(模块数据文本桥接,131.2 边界)。 let text = vm.lookup_str(content_val).unwrap_or_default(); vm.new_string_owned(text) } _ => return type_error(vm, "__moduleData: unsupported data kind"), }; let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let default_si = vm.perm_intern("default"); let obj_ref = unsafe { &mut *obj }; if let Err(e) = vm.define_data_property(obj_ref, default_si, default_val, ns_attrs()) { return NativeResult::Err(crate::error::create_error(vm, &e)); } obj_ref.set_module_namespace(true); obj_ref.set_extensible(false); NativeResult::Ok(JsValue::from_js_object(obj)) } /// `%AbstractModuleSource%` 构造器入口:被调用(含 `new`)恒抛 TypeError。 /// /// 规范 28.1.1.1:该内建不可被构造,实例只能由宿主(source-phase 导入的 /// ModuleSource 提供)产生。 pub fn abstract_module_source_ctor(vm: &mut H, _args: &[u8]) -> NativeResult { NativeResult::Err(crate::error::create_type_error(vm, "AbstractModuleSource is not a constructor")) } /// `%AbstractModuleSource%.prototype` 上 `Symbol.toStringTag` 访问器的 getter: /// 返回实例的模块源类名,非实例(非对象 this 或无类名标记)返回 undefined。 /// /// # 步骤 /// 1. `this` 非对象 → undefined(规范 28.3.3.2 第 2 步)。 /// 2. 查全局符号注册表取类名符号键;未登记或 `this` 无该 own 属性 → undefined。 /// 3. 有类名标记 → 返回其值(宿主模块源类名,如 `AbstractModuleSource`)。 /// /// # 边界与前提 /// - 类名标记是 `__moduleSourceObject` 在实例上定义的符号键 own 属性(引擎内 /// 表示宿主 `[[ModuleSourceClassName]]` 内部槽),非实例对象无此属性。 pub fn abstract_module_source_to_string_tag(vm: &mut H, args: &[u8]) -> NativeResult { // 访问器 getter 的 `this` 在 args[0](receiver 槽 253),实参区从 args[1] 起。 let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if !this_val.is_object() { return NativeResult::Ok(JsValue::undefined()); } let Some(idx) = vm.symbol_lookup_global(MODULE_SOURCE_CLASS_NAME_SYMBOL_KEY) else { return NativeResult::Ok(JsValue::undefined()); }; let key = encode_symbol_key(vm.realm_id(), idx); let obj = unsafe { &*this_val.as_js_object_ptr() }; if vm.get_own_property_slot(obj, key).is_none() { return NativeResult::Ok(JsValue::undefined()); } match vm.ordinary_get(obj, key, this_val) { Ok(v) => NativeResult::Ok(v), Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)), } } /// ModuleSource 类名符号的宿主注册表键(`Symbol.for` 全局注册表口径)。 const MODULE_SOURCE_CLASS_NAME_SYMBOL_KEY: &str = "oxide.moduleSourceClassName"; /// `__moduleSourceObject()`:返回一个 ModuleSource 对象(`%AbstractModuleSource%` /// 实例),供 source-phase 导入的局部名绑定。 /// /// # 步骤 /// 1. 经 `$262.AbstractModuleSource.prototype` 取内建原型(宿主绑定在绑定期 /// 安装);宿主或内建缺失时抛 TypeError。 /// 2. 以该原型分配新对象。 /// 3. 定义类名符号 own 属性(非枚举),供 `@@toStringTag` getter 读回类名。 /// /// # 边界与前提 /// - 每次调用产生新实例:宿主对同一 `` 模块记录的 ModuleSource /// 身份由依赖求值缓存(per-module 状态)承载,对象本体不跨调用共享。 /// /// # 副作用 /// - 分配新对象并定义符号键属性;首次调用时登记类名符号入全局注册表。 pub fn module_source_object(vm: &mut H, _args: &[u8]) -> NativeResult { let global = vm.global_object(); let global_val = JsValue::from_js_object(global.as_ptr() as *mut JsObject); let host_si = vm.perm_intern("$262"); let host = match vm.ordinary_get(&global, host_si, global_val) { Ok(v) => v, Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; if !host.is_object() { return type_error(vm, "__moduleSourceObject: $262 host is not bound"); } let host_obj = unsafe { &*host.as_js_object_ptr() }; let ctor_si = vm.perm_intern("AbstractModuleSource"); let ctor = match vm.ordinary_get(host_obj, ctor_si, host) { Ok(v) => v, Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; if !ctor.is_object() { return type_error(vm, "__moduleSourceObject: AbstractModuleSource is not bound"); } let ctor_obj = unsafe { &*ctor.as_js_object_ptr() }; let proto_si = vm.perm_intern("prototype"); let proto = match vm.ordinary_get(ctor_obj, proto_si, ctor) { Ok(v) => v, Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), }; if !proto.is_object() { return type_error(vm, "__moduleSourceObject: AbstractModuleSource.prototype is not an object"); } let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto)); let obj_ref = unsafe { &mut *obj }; let idx = match vm.symbol_lookup_global(MODULE_SOURCE_CLASS_NAME_SYMBOL_KEY) { Some(idx) => idx, None => { let idx = vm.symbol_intern(Some(MODULE_SOURCE_CLASS_NAME_SYMBOL_KEY.to_string())); vm.symbol_register_global(MODULE_SOURCE_CLASS_NAME_SYMBOL_KEY.to_string(), idx); idx } }; let key = encode_symbol_key(vm.realm_id(), idx); let class_name = vm.new_string("AbstractModuleSource"); if let Err(e) = vm.define_data_property(obj_ref, key, class_name, PropAttributes::new(true, false, true)) { return NativeResult::Err(crate::error::create_error(vm, &e)); } NativeResult::Ok(JsValue::from_js_object(obj)) } #[cfg(test)] mod tests { use super::*; use oxide_compiler::DefaultCompilerService; use oxide_vm::vm::Vm; use std::sync::Arc; /// 建一个带编译服务的 VM 与一个依赖模块函数(deferred ns 的 `[[Module]]` 载荷)。 fn vm_with_function() -> (Vm, JsValue) { let mut vm = Vm::new(); vm.set_compiler_service(Arc::new(DefaultCompilerService)); let fn_val = vm .create_dynamic_function(&[], "return 42;", false, false) .expect("create function"); (vm, fn_val) } #[test] fn module_defer_object_installs_state_box() { let (mut vm, fn_val) = vm_with_function(); vm.set_reg(1, fn_val); vm.set_reg(2, JsValue::undefined()); let path_str = vm.new_string("p"); vm.set_reg(3, path_str); let ns_val = module_defer_object(&mut vm, &[0, 1, 2, 3]).unwrap(); assert!(ns_val.is_object()); let ns_ptr = ns_val.as_js_object_ptr(); assert!(!ns_ptr.is_null()); let ns_obj = unsafe { &*ns_ptr }; assert!(ns_obj.is_module_namespace(), "deferred ns 应标记 module namespace"); assert!(ns_obj.is_deferred(), "deferred ns 应标记 deferred"); assert!(!ns_obj.is_extensible(), "deferred ns 应 non-extensible"); // 状态盒三内部槽安装正确:[[Module]] = fn、[[Evaluated]] = false、[[Namespace]] = undefined。 let state = deferred_state(ns_obj).expect("deferred state 应已安装"); assert_eq!(state.module, fn_val, "[[Module]] 应存依赖模块函数"); assert!(!state.evaluated, "[[Evaluated]] 初始应为 false"); assert_eq!(state.namespace, JsValue::undefined(), "[[Namespace]] 求值前应为 undefined"); } #[test] fn module_defer_object_mixed_carries_evaluated_state() { let (mut vm, fn_val) = vm_with_function(); // 真实 ns 对象(mixed:模块已急切求值)。 let real_ns = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let ns_val = JsValue::from_js_object(real_ns); vm.set_reg(1, fn_val); vm.set_reg(2, ns_val); let path_str = vm.new_string("p"); vm.set_reg(3, path_str); let defer_ns = module_defer_object(&mut vm, &[0, 1, 2, 3]).unwrap(); let ns_obj = unsafe { &*defer_ns.as_js_object_ptr() }; let state = deferred_state(ns_obj).expect("deferred state 应已安装"); assert_eq!(state.module, fn_val, "[[Module]] 应存依赖模块函数"); assert!(state.evaluated, "mixed 依赖 [[Evaluated]] 构造时应为 true"); assert_eq!(state.namespace, ns_val, "[[Namespace]] 应存真实 ns"); } #[test] fn module_defer_object_sentinel_accepts_undefined_module() { let (mut vm, _) = vm_with_function(); // 哨兵形态:fn 与 real_ns 均为 undefined(自导入/祖先 defer)。 vm.set_reg(1, JsValue::undefined()); vm.set_reg(2, JsValue::undefined()); let path_str = vm.new_string("p"); vm.set_reg(3, path_str); let ns_val = module_defer_object(&mut vm, &[0, 1, 2, 3]).unwrap(); let ns_obj = unsafe { &*ns_val.as_js_object_ptr() }; let state = deferred_state(ns_obj).expect("deferred state 应已安装"); assert_eq!(state.module, JsValue::undefined(), "哨兵 [[Module]] 应为 undefined"); assert!(!state.evaluated, "哨兵 [[Evaluated]] 应为 false"); assert_eq!(state.namespace, JsValue::undefined(), "哨兵 [[Namespace]] 应为 undefined"); } #[test] fn module_defer_object_rejects_non_function() { let (mut vm, _) = vm_with_function(); vm.set_reg(1, JsValue::int(42)); vm.set_reg(2, JsValue::undefined()); let path_str = vm.new_string("p"); vm.set_reg(3, path_str); let result = module_defer_object(&mut vm, &[0, 1, 2, 3]); assert!(matches!(result, NativeResult::Err(_)), "非函数应抛 TypeError"); } #[test] fn module_defer_object_rejects_non_object_ns() { let (mut vm, fn_val) = vm_with_function(); vm.set_reg(1, fn_val); vm.set_reg(2, JsValue::int(42)); let path_str = vm.new_string("p"); vm.set_reg(3, path_str); let result = module_defer_object(&mut vm, &[0, 1, 2, 3]); assert!(matches!(result, NativeResult::Err(_)), "非对象 real_ns 应抛 TypeError"); } #[test] fn module_defer_ns_gc_edge_keeps_module_function_alive() { let (mut vm, fn_val) = vm_with_function(); let fn_ptr = fn_val.as_js_object_ptr(); // 创建 deferred ns(持有 fn);fn + ns 两个 session 对象。 vm.set_reg(1, fn_val); vm.set_reg(2, JsValue::undefined()); let path_str = vm.new_string("p"); vm.set_reg(3, path_str); let ns_val = module_defer_object(&mut vm, &[0, 1, 2, 3]).unwrap(); let count_after_ns = vm.session_object_count(); // 仅 ns 为根:清掉持有 fn 的寄存器,fn 唯一可达路径是 ns 的 [[Module]] 边。 vm.set_reg(1, JsValue::undefined()); vm.set_reg(0, ns_val); // 完整收集:若 [[Module]] 边漏登,fn 被回收、计数回落。 vm.collect_session_gc(); assert_eq!(vm.session_object_count(), count_after_ns, "依赖模块函数应跨收集存活(GC 边已登记)"); // 原地 sweep 不搬移:ns 与状态盒原地保留,[[Module]] 指针稳定。 let ns_obj = unsafe { &*ns_val.as_js_object_ptr() }; 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 path_str = vm.new_string("p"); vm.set_reg(3, path_str); let ns_val = module_defer_object(&mut vm, &[0, 1, 2, 3]).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 指针应稳定"); } #[test] fn module_eval_state_ns_survives_session_gc() { let (mut vm, fn_val) = vm_with_function(); // 依赖模块函数经寄存器钉为根,首收集达稳态后记录基线。 vm.set_reg(0, fn_val); vm.collect_session_gc(); let baseline = vm.session_object_count(); // 命名空间对象为唯一新增 session 对象:per-module 求值状态表是其唯一根。 let ns = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let ns_val = JsValue::from_js_object(ns); let ns_ptr = ns_val.as_js_object_ptr(); vm.set_module_eval_state("p", true, ns_val); assert_eq!(vm.session_object_count(), baseline + 1, "命名空间对象应已分配"); // 完整收集:若状态表边漏登,命名空间对象被回收、计数回落。 vm.collect_session_gc(); assert_eq!( vm.session_object_count(), baseline + 1, "per-module 状态命名空间对象应跨收集存活(GC 边已登记)" ); // 原地 sweep 不搬移:状态表原地保留,命名空间指针稳定。 let state = vm.get_module_eval_state("p").expect("per-module 状态应存活"); assert!(state.evaluated, "evaluated 标志应稳定"); assert_eq!(state.namespace.as_js_object_ptr(), ns_ptr, "命名空间指针应稳定"); } #[test] fn deferred_ns_read_target_falls_back_for_non_object_namespace() { let (mut vm, fn_val) = vm_with_function(); // 模块函数返回非对象(42):求值成功后 [[Namespace]] 为非对象值。 vm.set_reg(1, fn_val); vm.set_reg(2, JsValue::undefined()); let path_str = vm.new_string("p"); vm.set_reg(3, path_str); let ns_val = module_defer_object(&mut vm, &[0, 1, 2, 3]).unwrap(); let ns_ptr = ns_val.as_js_object_ptr(); // 模拟求值完成:[[Evaluated]] 置真、[[Namespace]] 为非对象值 42。 { let ns_obj = unsafe { &*ns_ptr }; let state = deferred_state_mut(ns_obj).expect("deferred state 应已安装"); state.evaluated = true; state.namespace = JsValue::int(42); } // 权威读目标回落对象自身(预注册占位槽),非对象 [[Namespace]] 不作读目标。 let ns_obj = unsafe { &*ns_ptr }; let target = deferred_ns_read_target(ns_obj); assert!( std::ptr::eq(target as *const JsObject, ns_ptr as *const JsObject), "非对象 [[Namespace]] 应回落对象自身" ); } #[test] fn deferred_ns_read_target_returns_real_ns_for_object_namespace() { let (mut vm, fn_val) = vm_with_function(); // 真实 ns 对象(求值成功且 [[Namespace]] 为对象,mixed 形态)。 let real_ns = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let real_ns_val = JsValue::from_js_object(real_ns); vm.set_reg(1, fn_val); vm.set_reg(2, real_ns_val); let path_str = vm.new_string("p"); vm.set_reg(3, path_str); let ns_val = module_defer_object(&mut vm, &[0, 1, 2, 3]).unwrap(); let ns_ptr = ns_val.as_js_object_ptr(); // 权威读目标返回真实 ns(活值与真实槽位在真实 ns 条目表)。 let ns_obj = unsafe { &*ns_ptr }; let target = deferred_ns_read_target(ns_obj); assert!( std::ptr::eq(target as *const JsObject, real_ns as *const JsObject), "对象 [[Namespace]] 应返回真实 ns" ); } }