diff --git a/Cargo.lock b/Cargo.lock index 7c65796a..d1f5c8de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.104" +version = "0.8.105" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.104" +version = "0.8.105" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.104" +version = "0.8.105" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.104" +version = "0.8.105" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.104" +version = "0.8.105" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.104" +version = "0.8.105" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.104" +version = "0.8.105" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.104" +version = "0.8.105" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.104" +version = "0.8.105" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.104" +version = "0.8.105" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.104" +version = "0.8.105" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index b7f293fb..c17df2b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.8.104" +version = "0.8.105" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/module.rs b/crates/oxide_builtins/src/module.rs index ca48fc96..826d5355 100644 --- a/crates/oxide_builtins/src/module.rs +++ b/crates/oxide_builtins/src/module.rs @@ -66,11 +66,14 @@ pub struct ModuleNsEntry { /// 字段、不建第二个 Box——GC 边、字节核算、释放全部复用 `ModuleNamespace` 家族链, /// 零新增注册点。 pub struct DeferredNsState { - /// `[[Module]]`:依赖模块函数(编译期 CREATE_CLOSURE 的闭包对象)。 + /// `[[Module]]`:依赖模块函数(编译期 CREATE_CLOSURE 的闭包对象),或 + /// undefined(哨兵:自导入/祖先引用,模块函数在导入方 prelude 期不可得)。 pub module: JsValue, - /// `[[Evaluated]]`:依赖模块是否已求值(首次触发成功后置真,不回退)。 + /// `[[Evaluated]]`:依赖模块是否已求值(mixed 依赖构造时即为真; + /// 首次触发成功后置真,不回退)。 pub evaluated: bool, - /// `[[Namespace]]`:求值后返回的命名空间对象(求值前为 undefined)。 + /// `[[Namespace]]`:求值后返回的命名空间对象(mixed 依赖构造时即为真实 ns; + /// 求值前为 undefined)。 pub namespace: JsValue, } @@ -859,29 +862,38 @@ pub fn module_eval(vm: &mut H, args: &[u8]) -> NativeResult { } } -/// `__moduleDeferObject(fn)`:创建 deferred namespace 对象(`import defer * as` 产物)。 +/// `__moduleDeferObject(fn, real_ns)`:创建 deferred namespace 对象(`import defer * as` 产物)。 /// /// # 步骤 -/// 1. 校验 `fn` 为函数对象(依赖模块函数,编译期 CREATE_CLOSURE 的闭包对象)。 +/// 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]]` = false、 -/// `[[Namespace]]` = undefined。 +/// 4. 安装条目表并写状态盒:`[[Module]]` = fn、`[[Evaluated]]` = `real_ns` 为对象、 +/// `[[Namespace]]` = `real_ns`(或 undefined)。 /// /// # 副作用 /// - 新对象创建起即标记 module namespace exotic 并置 non-extensible(同 `__moduleObject`); /// 状态盒三内部槽由本入口唯一安装。 pub fn module_defer_object(vm: &mut H, args: &[u8]) -> NativeResult { - if args.len() < 2 { - return type_error(vm, "__moduleDeferObject: 1 argument required"); + if args.len() < 3 { + return type_error(vm, "__moduleDeferObject: 2 arguments required"); } let fn_val = vm.reg(args[1]); - if !fn_val.is_object() { - return type_error(vm, "__moduleDeferObject: target is not a function"); + let ns_val = vm.reg(args[2]); + // 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"); } - let ptr = fn_val.as_js_object_ptr(); - if ptr.is_null() || !unsafe { (*ptr).is_function() } { - return type_error(vm, "__moduleDeferObject: target is not a function"); + // 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); @@ -898,8 +910,8 @@ pub fn module_defer_object(vm: &mut H, args: &[u8]) -> NativeResult { unsafe { (*table).deferred = Some(DeferredNsState { module: fn_val, - evaluated: false, - namespace: JsValue::undefined(), + evaluated: ns_val.is_object(), + namespace: ns_val, }); } NativeResult::Ok(JsValue::from_js_object(obj)) @@ -968,7 +980,8 @@ mod tests { fn module_defer_object_installs_state_box() { let (mut vm, fn_val) = vm_with_function(); vm.set_reg(1, fn_val); - let ns_val = module_defer_object(&mut vm, &[0, 1]).unwrap(); + vm.set_reg(2, JsValue::undefined()); + let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); assert!(ns_val.is_object()); let ns_ptr = ns_val.as_js_object_ptr(); assert!(!ns_ptr.is_null()); @@ -984,14 +997,54 @@ mod tests { 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 defer_ns = module_defer_object(&mut vm, &[0, 1, 2]).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 ns_val = module_defer_object(&mut vm, &[0, 1, 2]).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)); - let result = module_defer_object(&mut vm, &[0, 1]); + vm.set_reg(2, JsValue::undefined()); + let result = module_defer_object(&mut vm, &[0, 1, 2]); 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 result = module_defer_object(&mut vm, &[0, 1, 2]); + 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(); @@ -999,7 +1052,8 @@ mod tests { // 创建 deferred ns(持有 fn);fn + ns 两个 session 对象。 vm.set_reg(1, fn_val); - let ns_val = module_defer_object(&mut vm, &[0, 1]).unwrap(); + vm.set_reg(2, JsValue::undefined()); + let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); let count_after_ns = vm.session_object_count(); // 仅 ns 为根:清掉持有 fn 的寄存器,fn 唯一可达路径是 ns 的 [[Module]] 边。 diff --git a/crates/oxide_emit/src/compile_ctx.rs b/crates/oxide_emit/src/compile_ctx.rs index 044f8c1d..2fb918c7 100644 --- a/crates/oxide_emit/src/compile_ctx.rs +++ b/crates/oxide_emit/src/compile_ctx.rs @@ -173,6 +173,10 @@ pub struct CompileCtx { pub(crate) module_local_export_regs: HashMap>, /// 已求值依赖模块的命名空间对象寄存器(按 import/export source 字符串索引)。 pub(crate) module_dep_ns_regs: HashMap, + /// defer 依赖的 deferred namespace 对象寄存器(按 import source 字符串索引): + /// `import defer * as ns` 绑定此对象(`__moduleDeferObject` 产物),eager 命名空间 + /// 绑定仍走 `module_dep_ns_regs`。仅含 defer 导入的 spec 登记。 + pub(crate) module_defer_ns_regs: HashMap, /// 依赖模块规范路径(按 import/export source 字符串索引):再导出来源身份用。 pub(crate) module_dep_paths: HashMap, /// 非自导入的导入局部名 → (依赖模块规范路径, 导入名)。命名空间导入的导入名为 @@ -287,6 +291,7 @@ impl CompileCtx { module_ns_reg: None, module_local_export_regs: HashMap::new(), module_dep_ns_regs: HashMap::new(), + module_defer_ns_regs: HashMap::new(), module_dep_paths: HashMap::new(), module_import_origins: HashMap::new(), module_dep_reassignable: HashMap::new(), diff --git a/crates/oxide_emit/src/module.rs b/crates/oxide_emit/src/module.rs index 4835080a..ba695081 100644 --- a/crates/oxide_emit/src/module.rs +++ b/crates/oxide_emit/src/module.rs @@ -4,8 +4,11 @@ //! `__moduleGet` 活读,其余退化为快照;被闭包捕获的导出挂共享 cell,闭包内写 //! 随活读可见。自导入(`import ... from 自身`)的局部名解析为本模块源绑定的活 //! 别名,TDZ/提升/活值/不可变四语义全部委托源绑定。`export` 语句就地注册导出值。 -//! 未支持:嵌套闭包内读 import 名(仍为链接期快照)、defer(延迟求值)、 -//! source-phase;star 转发与经再导出链的活值仍为快照;循环导入在编译期跳过。 +//! defer 导入(`import defer * as ns`)的依赖不求值:编译入子模块树并发 +//! `__moduleDeferObject` 构造 deferred namespace 对象(逐名预注册静态导出名), +//! 绑定该对象;自导入/祖先 defer 回边发哨兵形态(不递归、不编译)。 +//! 未支持:嵌套闭包内读 import 名(仍为链接期快照)、source-phase; +//! star 转发与经再导出链的活值仍为快照;eager 环导入在编译期跳过。 use std::collections::{HashMap, HashSet}; use std::path::Path; @@ -21,7 +24,8 @@ use oxide_ir::operand::Operand; use oxide_ir::{IRFunction, ParamLayout}; use oxide_parser::{ BindingPattern, Declaration, ExportDefaultDeclarationKind, Expression, ImportAttributeKey, - ImportDeclarationSpecifier, ModuleExportName, Statement, VariableDeclarationKind, WithClause, + ImportDeclarationSpecifier, ImportPhase, ModuleExportName, Statement, VariableDeclarationKind, + WithClause, }; use oxide_types::MODULE_NAMESPACE_BINDING; @@ -44,6 +48,16 @@ pub struct ResolvedModule { pub kind: ModuleKind, } +/// 单个依赖 spec 的 phase 跟踪:eager/defer 双标志与首次出现的导入属性。 +/// +/// `eager` 为真表示该 spec 被任一非 defer 语句(导入或再导出)引用; +/// `defer` 为真表示被任一 `import defer * as` 引用。两标志可并存(mixed 依赖)。 +struct DepPhase { + eager: bool, + defer: bool, + attrs: Vec<(String, String)>, +} + /// 模块加载器:由调用方(test262 runner / CLI)提供文件解析。 pub trait ModuleSourceLoader { /// 解析依赖模块:按 `base_dir` 与 `specifier` 定位模块源码,`attributes` 为 @@ -457,50 +471,84 @@ impl Emitter { let ns_reg = self.emit_module_call(ctx, "__moduleObject", &[])?; ctx.module_ns_reg = Some(ns_reg); - // —— 依赖收集(import + re-export/star 的 source,按首次出现去重)—— + // —— 依赖收集(import + re-export/star 的 source,按首次出现去重,记 phase)—— + // eager_order 为各 spec 首个 eager 语句的出现序(求值序);defer_order 为各 spec + // 首次出现序(defer-only 组发射序)。无 defer 的模块两序与首次出现序恒等, + // 发射产物与现态逐字节一致。 let base_dir = Path::new(module_path) .parent() .map(|p| p.to_string_lossy().to_string()) .unwrap_or_default(); - let mut deps: Vec<(String, Vec<(String, String)>)> = Vec::new(); + let mut phase_map: HashMap = HashMap::new(); + let mut eager_order: Vec = Vec::new(); + let mut defer_order: Vec = Vec::new(); for stmt in body { - let (spec, attrs) = match stmt { + let (spec, attrs, is_defer) = match stmt { Statement::ImportDeclaration(imp) => { - (imp.source.value.to_string(), module_attributes(&imp.with_clause)) + let is_defer = imp.phase == Some(ImportPhase::Defer); + (imp.source.value.to_string(), module_attributes(&imp.with_clause), is_defer) } Statement::ExportNamedDeclaration(exp) => { if let Some(src) = &exp.source { - (src.value.to_string(), module_attributes(&exp.with_clause)) + (src.value.to_string(), module_attributes(&exp.with_clause), false) } else { continue; } } Statement::ExportAllDeclaration(exp) => { - (exp.source.value.to_string(), module_attributes(&exp.with_clause)) + (exp.source.value.to_string(), module_attributes(&exp.with_clause), false) } _ => continue, }; - if !deps.iter().any(|(s, _)| *s == spec) { - deps.push((spec, attrs)); + let is_new = !phase_map.contains_key(&spec); + let entry = phase_map + .entry(spec.clone()) + .or_insert_with(|| DepPhase { eager: false, defer: false, attrs }); + if is_new { + defer_order.push(spec.clone()); + } + if !is_defer && !entry.eager { + entry.eager = true; + eager_order.push(spec.clone()); + } + if is_defer { + entry.defer = true; } } - // —— 编译并求值依赖 —— - for (spec, attrs) in deps { - let attr_refs: Vec<(&str, &str)> = attrs.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect(); + // 本模块自身导出名与 var 作用域导出名:自导入 mixed 的 defer 预注册复用, + // 亦供下方绑定循环与 live 预注册消费。 + let own_export_names = module_own_export_names(body); + let var_scoped_export_names = module_var_scoped_export_names(body); + + // —— 编译并求值依赖:eager 组先行(按 eager 语句序),defer-only 组随后 —— + for spec in eager_order { + let phase = &phase_map[&spec]; + let attr_refs: Vec<(&str, &str)> = phase.attrs.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect(); let resolved = loader.resolve(&base_dir, &spec, &attr_refs)?; - let dep_ns_reg = if resolved.path == module_path { + let is_self = resolved.path == module_path; + let (dep_ns_reg, fn_reg, dep_own_names, dep_var_scoped) = if is_self { // 自导入:读自身命名空间(依赖即本模块,勿递归)。 - ns_reg + (ns_reg, None, own_export_names.clone(), var_scoped_export_names.clone()) } else { if path_stack.contains(&resolved.path) { return Err(format!("circular module import not supported: {}", resolved.path)); } path_stack.push(resolved.path.clone()); - let (dep_ir, dep_reassignable) = match resolved.kind { + let (dep_ir, dep_reassignable, dep_own_names, dep_var_scoped) = match resolved.kind { ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack)?, - ModuleKind::Json => (self.compile_data_dep("json", &resolved.source)?, false), - ModuleKind::Text => (self.compile_data_dep("text", &resolved.source)?, false), + ModuleKind::Json => ( + self.compile_data_dep("json", &resolved.source)?, + false, + HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + ), + ModuleKind::Text => ( + self.compile_data_dep("text", &resolved.source)?, + false, + HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + ), ModuleKind::Bytes => return Err("bytes module not supported".into()), }; path_stack.pop(); @@ -508,20 +556,84 @@ impl Emitter { ctx.module_dep_reassignable.insert(spec.clone(), dep_reassignable); let fn_reg = ctx.alloc_reg(); ctx.inst(Inst::create_closure(Operand::Reg(fn_reg), ctx.nested.len() as u16)); - self.emit_module_call(ctx, "__moduleEval", &[fn_reg])? + let real_ns = self.emit_module_call(ctx, "__moduleEval", &[fn_reg])?; + (real_ns, Some(fn_reg), dep_own_names, dep_var_scoped) }; ctx.module_dep_ns_regs.insert(spec.clone(), dep_ns_reg); ctx.module_dep_paths.insert(spec.clone(), resolved.path.clone()); - if resolved.path == module_path { + if is_self { // 自导入:绑定走别名语义(源导出在 body 执行中才就绪), // 不能像普通依赖那样链接期快照。 ctx.module_self_import_specs.insert(spec.clone()); } + if phase.defer { + // mixed 依赖:模块已急切求值,deferred 对象承载已求值态 + // ([[Evaluated]]=true、[[Namespace]]=真实 ns)。 + let fn_or_sentinel = if is_self { + // 自导入 mixed:模块函数在导入方 prelude 期不可得,发哨兵形态。 + self.emit_undefined(ctx) + } else { + fn_reg.expect("非自导入 eager 依赖必有闭包寄存器") + }; + let defer_ns = self.emit_module_call(ctx, "__moduleDeferObject", &[fn_or_sentinel, dep_ns_reg])?; + ctx.module_defer_ns_regs.insert(spec.clone(), defer_ns); + self.emit_defer_pre_register(ctx, defer_ns, &dep_own_names, &dep_var_scoped)?; + } + } + + // defer-only 组(eager == false 的 spec,按 defer_order):不求值、不发 + // __moduleEval。祖先判定(自导入或父链回边)命中时发哨兵形态 + // __moduleDeferObject(undefined, undefined)——不递归、不压 path_stack、不编译; + // 否则正常编译并发 __moduleDeferObject(fn, undefined) + 逐名预注册。 + for spec in defer_order { + let phase = &phase_map[&spec]; + if phase.eager { + continue; + } + let attr_refs: Vec<(&str, &str)> = phase.attrs.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect(); + let resolved = loader.resolve(&base_dir, &spec, &attr_refs)?; + let is_ancestor = resolved.path == module_path || path_stack.contains(&resolved.path); + if is_ancestor { + // 哨兵:模块函数在导入方 prelude 期不可得(自导入是自身、祖先是 + // 父链),defer 回边在导入图上成环,编译期不得报循环。 + let undef = self.emit_undefined(ctx); + let defer_ns = self.emit_module_call(ctx, "__moduleDeferObject", &[undef, undef])?; + ctx.module_dep_ns_regs.insert(spec.clone(), defer_ns); + ctx.module_defer_ns_regs.insert(spec.clone(), defer_ns); + ctx.module_dep_paths.insert(spec.clone(), resolved.path.clone()); + } else { + path_stack.push(resolved.path.clone()); + let (dep_ir, _dep_reassignable, dep_own_names, dep_var_scoped) = match resolved.kind { + ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack)?, + ModuleKind::Json => ( + self.compile_data_dep("json", &resolved.source)?, + false, + HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + ), + ModuleKind::Text => ( + self.compile_data_dep("text", &resolved.source)?, + false, + HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + HashSet::from([DEFAULT_EXPORT_NAME.to_string()]), + ), + ModuleKind::Bytes => return Err("bytes module not supported".into()), + }; + path_stack.pop(); + ctx.nested.push(dep_ir); + let fn_reg = ctx.alloc_reg(); + ctx.inst(Inst::create_closure(Operand::Reg(fn_reg), ctx.nested.len() as u16)); + let undef = self.emit_undefined(ctx); + let defer_ns = self.emit_module_call(ctx, "__moduleDeferObject", &[fn_reg, undef])?; + ctx.module_dep_ns_regs.insert(spec.clone(), defer_ns); + ctx.module_defer_ns_regs.insert(spec.clone(), defer_ns); + ctx.module_dep_paths.insert(spec.clone(), resolved.path.clone()); + self.emit_defer_pre_register(ctx, defer_ns, &dep_own_names, &dep_var_scoped)?; + } } // —— 导入绑定初始化(普通依赖为链接期快照;命名空间绑定直接引用 ns 对象; - // 自导入绑定解析为本模块源绑定的活别名)—— - let own_export_names = module_own_export_names(body); + // defer 命名空间绑定引用 deferred 对象;自导入绑定解析为本模块源绑定的活别名)—— // 无命名 `export * from 'x'` 会把依赖的全部导出转发给自身;自导入名可能来自 // star 再导出,编译期无法静态判定该名是否存在于自身导出,故链接校验放宽。 let has_unnamed_star = body @@ -647,19 +759,29 @@ impl Emitter { } } ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => { + let is_defer = imp.phase == Some(ImportPhase::Defer); // 命名空间绑定引用 ns 对象本身:自导入时同一对象, - // body 执行后导出自然可见。 - if is_self { + // body 执行后导出自然可见;defer 绑定 deferred 对象。 + // defer 自导入不得激活 live ns(不置 has_self_ns_import)。 + if is_self && !is_defer { has_self_ns_import = true; - } else { + } else if !is_self { ctx.module_import_origins.insert( s.local.name.to_string(), (dep_path.clone(), MODULE_NAMESPACE_BINDING.to_string()), ); } + let bind_reg = if is_defer { + *ctx + .module_defer_ns_regs + .get(&dep_spec) + .ok_or_else(|| format!("module dependency missing: {dep_spec}"))? + } else { + dep_ns_reg + }; self.emit_bind_target( s.local.name.as_str(), - dep_ns_reg, + bind_reg, VariableDeclarationKind::Const, true, false, @@ -698,12 +820,11 @@ impl Emitter { // var/函数类导出(VarScopedDeclarations)在实例化期即初始化为 undefined, // lexical/class 保持未初始化(TDZ)。 —— if live_active { - let var_scoped = module_var_scoped_export_names(body); let mut export_names: Vec = own_export_names.into_iter().collect(); export_names.sort(); for name in export_names { let name_reg = self.load_string_const(&name, ctx); - let var_like_reg = self.load_bool_const(var_scoped.contains(&name), ctx); + let var_like_reg = self.load_bool_const(var_scoped_export_names.contains(&name), ctx); self.emit_module_call(ctx, "__modulePreRegister", &[ns_reg, name_reg, var_like_reg])?; } } @@ -824,17 +945,19 @@ impl Emitter { Ok(()) } - /// 编译 JS 依赖模块(递归),回传 IR 与「是否含可重赋导出」标志。 + /// 编译 JS 依赖模块(递归),回传 IR、「是否含可重赋导出」标志、 + /// 自身静态导出名集与 var 作用域导出名集。 /// /// # 边界与前提 - /// - 标志取自依赖 ctx 的 `module_live_dep`;入口模块不参与,依赖模块恒按 + /// - 可重赋标志取自依赖 ctx 的 `module_live_dep`;入口模块不参与,依赖模块恒按 /// `!top_level` 判定,故 const-only 依赖回传 false。 + /// - 导出名集在函数内对解析产物就地计算,供导入方 defer 预注册消费。 /// /// # 注意事项 /// - 导入方据标志决定是否登记活读映射;标志不改变本模块 IR。 fn compile_js_dep( &self, resolved: &ResolvedModule, loader: &mut dyn ModuleSourceLoader, path_stack: &mut Vec, - ) -> Result<(IRFunction, bool), String> { + ) -> Result<(IRFunction, bool, HashSet, HashSet), String> { let alloc = oxide_parser::Allocator::default(); let program = oxide_parser::parse_module(&alloc, &resolved.source).map_err(|errs| { format!( @@ -848,7 +971,30 @@ impl Emitter { ctx.is_strict = true; self.emit_module_into_ctx(&program, &resolved.path, loader, path_stack, &mut ctx, false)?; let reassignable = ctx.module_live_dep; - Ok((ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None), reassignable)) + let own_export_names = module_own_export_names(&program.body); + let var_scoped = module_var_scoped_export_names(&program.body); + Ok(( + ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None), + reassignable, + own_export_names, + var_scoped, + )) + } + + /// 发 defer 预注册:对 deferred namespace 对象逐名 `__modulePreRegister`, + /// 名字按字典序排序保证发射序确定。var 作用域导出名(var/function) + /// 预注册为已初始化(undefined),lexical/class 保持未初始化(TDZ)。 + fn emit_defer_pre_register( + &self, ctx: &mut CompileCtx, defer_ns_reg: u32, export_names: &HashSet, var_scoped: &HashSet, + ) -> Result<(), String> { + let mut names: Vec<&String> = export_names.iter().collect(); + names.sort(); + for name in names { + let name_reg = self.load_string_const(name, ctx); + let var_like_reg = self.load_bool_const(var_scoped.contains(name), ctx); + self.emit_module_call(ctx, "__modulePreRegister", &[defer_ns_reg, name_reg, var_like_reg])?; + } + Ok(()) } /// 编译数据模块(json/text):body 仅为 __moduleData 调用 + RETURN。 diff --git a/crates/oxide_emit/tests/module_link_tests.rs b/crates/oxide_emit/tests/module_link_tests.rs index 08c314a6..684e014f 100644 --- a/crates/oxide_emit/tests/module_link_tests.rs +++ b/crates/oxide_emit/tests/module_link_tests.rs @@ -1,6 +1,7 @@ //! 模块链接面的 IR 级断言:内存版 `ModuleSourceLoader` + //! `emit_program_module`,钉死 import prelude / export 就地注册 / star 再导出 / -//! 循环导入编译期报错 / 数据模块的现状行为。 +//! 循环导入编译期报错 / 数据模块 / defer 依赖(不求值、mixed eager 序、 +//! 自导入与祖先回边哨兵)的现状行为。 //! //! 路径口径:加载器 `path` 回填表键原样字符串,与入口 `module_path` 入参逐字符 //! 一致(虚拟路径 canonicalize 失败保留原路径),自导入身份比较与循环检测依赖 @@ -366,3 +367,112 @@ fn entry_module_reassignable_export_not_pre_registered() { assert_eq!(native_calls_to(&ir, "__modulePreRegister"), 0, "入口模块不得预注册"); assert_eq!(native_calls_to(&ir, "__moduleSetCell"), 0, "入口模块不得挂 cell"); } + +/// 面 14:defer-only 依赖不求值——无 `__moduleEval`,发 `__moduleDeferObject` +/// 构造 deferred 对象,逐名 `__modulePreRegister` 预注册静态导出名。 +#[test] +fn defer_only_dep_not_evaluated() { + let (ir, _) = emit_module( + "import defer * as ns from \"./dep.js\"; ns;", + "./entry.js", + &[("./dep.js", ModuleKind::Js, "export var a = 1; export const b = 2;")], + ) + .expect("defer-only 依赖应编译成功"); + assert_eq!(native_calls_to(&ir, "__moduleEval"), 0, "defer-only 依赖不得求值"); + assert_eq!(native_calls_to(&ir, "__moduleDeferObject"), 1, "deferred 对象应构造一次"); + assert_eq!(native_calls_to(&ir, "__modulePreRegister"), 2, "a 与 b 各预注册一次"); + let pool = pool_strings(&ir); + assert!(pool.contains("a") && pool.contains("b"), "导出名应入池: {pool:?}"); + assert_eq!(ir.nested.len(), 1, "依赖模块应编入子模块树"); +} + +/// 面 15:mixed 依赖按 eager 语句序求值,defer 语句不计入求值序。 +/// 入口的 eager 序为 [setup, dep-2, dep-1](dep-1 为 mixed);dep-1 内的 +/// defer-only dep-1.2 不求值。 +#[test] +fn mixed_dep_evaluates_in_eager_order() { + let (ir, _) = emit_module( + "import \"./setup.js\"; import defer * as ns1 from \"./dep-1.js\"; import \"./dep-2.js\"; import \"./dep-1.js\";", + "./entry.js", + &[ + ("./setup.js", ModuleKind::Js, "globalThis.evaluations = [];"), + ( + "./dep-1.js", + ModuleKind::Js, + "import \"./dep-1.1.js\"; import defer * as ns_1_2 from \"./dep-1.2.js\"; globalThis.evaluations.push(1); export { ns_1_2 };", + ), + ("./dep-2.js", ModuleKind::Js, "globalThis.evaluations.push(2);"), + ("./dep-1.1.js", ModuleKind::Js, "globalThis.evaluations.push(1.1);"), + ("./dep-1.2.js", ModuleKind::Js, "globalThis.evaluations.push(1.2);"), + ], + ) + .expect("mixed 依赖应编译成功"); + // 入口:3 个 eager 依赖(setup, dep-2, dep-1)各求值一次。 + assert_eq!(native_calls_to(&ir, "__moduleEval"), 3, "入口应求值 3 个 eager 依赖"); + // 入口:dep-1 为 mixed,deferred 对象构造一次。 + assert_eq!(native_calls_to(&ir, "__moduleDeferObject"), 1, "mixed dep-1 应构造 deferred 对象一次"); + // dep-1 是第 3 个 nested 模块(eager_order = [setup, dep-2, dep-1])。 + let dep1 = &ir.nested[2]; + // dep-1:1 个 eager 依赖(dep-1.1)求值,defer-only 依赖(dep-1.2)不求值。 + assert_eq!(native_calls_to(dep1, "__moduleEval"), 1, "dep-1 应只求值 dep-1.1"); + // dep-1:dep-1.2 为 defer-only,deferred 对象构造一次。 + assert_eq!(native_calls_to(dep1, "__moduleDeferObject"), 1, "dep-1.2 应构造 deferred 对象一次"); + // dep-1.2 是 dep-1 的第 2 个 nested 模块(eager_order = [dep-1.1], defer_order = [dep-1.1, dep-1.2])。 + let dep12 = &dep1.nested[1]; + assert_eq!(native_calls_to(dep12, "__moduleEval"), 0, "dep-1.2 不得求值"); +} + +/// 面 16:自导入 defer 走哨兵分支——不递归、无环检测 Err、发 +/// `__moduleDeferObject(undefined, undefined)`。 +#[test] +fn self_import_defer_sentinel() { + let (ir, _) = emit_module( + "import defer * as self from \"./self.js\"; self;", + "./self.js", + &[("./self.js", ModuleKind::Js, "export var foo = 1;")], + ) + .expect("自导入 defer 应编译成功"); + assert_eq!(native_calls_to(&ir, "__moduleEval"), 0, "自导入 defer 不得求值"); + assert_eq!(native_calls_to(&ir, "__moduleDeferObject"), 1, "哨兵 deferred 对象应构造一次"); + assert_eq!(ir.nested.len(), 0, "自导入 defer 不得递归"); +} + +/// 面 17:祖先 defer 回边(dep → main)走哨兵分支——编译成功、无环检测 Err、 +/// 不递归。main eager 导入 dep,dep defer 导入 main(导入图含环)。 +#[test] +fn ancestor_defer_back_edge_sentinel() { + let main_src = "import \"./dep.js\"; globalThis.result = 1;"; + let dep_src = "import defer * as main from \"./main.js\"; main.foo;"; + let (ir, _) = emit_module( + main_src, + "./main.js", + &[ + ("./main.js", ModuleKind::Js, main_src), + ("./dep.js", ModuleKind::Js, dep_src), + ], + ) + .expect("祖先 defer 回边应编译成功"); + // main:1 个 eager 依赖(dep)求值。 + assert_eq!(native_calls_to(&ir, "__moduleEval"), 1, "main 应求值 dep"); + // main:无 defer,无 deferred 对象。 + assert_eq!(native_calls_to(&ir, "__moduleDeferObject"), 0, "main 不得构造 deferred 对象"); + // dep(nested):1 个 defer-only 依赖(main)哨兵,不求值。 + let dep = &ir.nested[0]; + assert_eq!(native_calls_to(dep, "__moduleEval"), 0, "dep 不得求值 main"); + assert_eq!(native_calls_to(dep, "__moduleDeferObject"), 1, "dep 应为 main 构造哨兵 deferred 对象"); + // dep:不递归(main 未编入 dep 的 nested)。 + assert_eq!(dep.nested.len(), 0, "dep 不得递归进 main"); +} + +/// 面 18:零 diff 守卫——非 defer 模块无 `__moduleDeferObject`、无依赖预注册。 +#[test] +fn no_defer_object_for_non_defer_modules() { + let (ir, _) = emit_module( + "import * as ns from \"./dep.js\"; ns;", + "./entry.js", + &[("./dep.js", ModuleKind::Js, "export var z = 1;")], + ) + .expect("非 defer 模块应编译成功"); + assert_eq!(native_calls_to(&ir, "__moduleDeferObject"), 0, "非 defer 模块不得构造 deferred 对象"); + assert_eq!(native_calls_to(&ir, "__modulePreRegister"), 0, "非 defer 模块不得预注册依赖"); +} diff --git a/crates/oxide_vm/src/bindings/bind_global.rs b/crates/oxide_vm/src/bindings/bind_global.rs index 1beb27bd..db4708f6 100644 --- a/crates/oxide_vm/src/bindings/bind_global.rs +++ b/crates/oxide_vm/src/bindings/bind_global.rs @@ -57,7 +57,7 @@ pub fn bind_global(core: &Arc, session: &KernelSession, global: &mut ( "__moduleDeferObject", oxide_builtins::module::module_defer_object:: as *const (), - 1, + 2, ), ("__moduleData", oxide_builtins::module::module_data:: as *const (), 2), ("eval", oxide_builtins::eval::eval:: as *const (), 1),