From 43f15ad2ee752649dd15310dcfca87864ab098b5 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 14 Oct 2026 05:16:34 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20import=20defer=20=E7=9A=84=20TLA=20?= =?UTF-8?q?=E4=BC=A0=E9=80=92=E4=BE=9D=E8=B5=96=E6=8C=89=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E5=BA=8F=E6=8F=90=E5=8D=87=E6=B1=82=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 42 +-- Cargo.toml | 2 +- crates/oxide_builtins/src/module.rs | 104 +++++- crates/oxide_emit/src/module.rs | 354 +++++++++++++++---- crates/oxide_emit/tests/module_link_tests.rs | 51 +++ crates/oxide_runtime_api/src/host.rs | 34 ++ crates/oxide_runtime_api/src/lib.rs | 2 +- crates/oxide_vm/src/vm/mod.rs | 6 + crates/oxide_vm/src/vm/vmhost.rs | 60 +++- crates/oxide_vm/src/vm_props.rs | 12 +- crates/oxide_vm/src/vm_support.rs | 5 + 11 files changed, 555 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b80525f..40f6f223 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.18.9" +version = "0.19.1" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.18.9" +version = "0.19.1" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.18.9" +version = "0.19.1" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.18.9" +version = "0.19.1" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1272,7 +1272,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.18.9" +version = "0.19.1" dependencies = [ "num-bigint", "oxc_allocator", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "oxide_fold" -version = "0.18.9" +version = "0.19.1" dependencies = [ "num-bigint", "oxide_bytecode", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.18.9" +version = "0.19.1" dependencies = [ "bumpalo", "dashmap", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.18.9" +version = "0.19.1" dependencies = [ "tracing", "tracing-appender", @@ -1356,11 +1356,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.18.9" +version = "0.19.1" [[package]] name = "oxide_parser" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.18.9" +version = "0.19.1" dependencies = [ "num-bigint", "num-traits", @@ -1404,7 +1404,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.18.9" +version = "0.19.1" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.18.9" +version = "0.19.1" dependencies = [ "bumpalo", "dashmap", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.18.9" +version = "0.19.1" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index d27d9507..33f456a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.19.0" +version = "0.19.1" 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 765e2fca..36782c86 100644 --- a/crates/oxide_builtins/src/module.rs +++ b/crates/oxide_builtins/src/module.rs @@ -7,7 +7,7 @@ //! source-phase / defer 语义留待后续轮次。 use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; -use oxide_runtime_api::{NativeResult, VmHost}; +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; @@ -78,6 +78,9 @@ pub struct DeferredNsState { /// 求值失败时缓存的原始错误值(再读再抛同一错误对象,保身份); /// 成功求值与求值前恒 `None`。 pub error: Option, + /// 依赖模块规范路径:求值触发点据此查 per-module 状态(提升求值过的 TLA + /// 模块已结算,直接取缓存命名空间,防双求值)。 + pub path: String, } /// 模块命名空间条目表:挂在 ns 对象 `native_data` 上,是 exotic [[Get]] 的权威 @@ -897,16 +900,26 @@ pub fn module_seal(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(ns_val) } -/// `__moduleEval(fn)`:同步执行依赖模块(fn 为编译期 CREATE_CLOSURE 的函数对象),返回其命名空间。 +/// `__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() < 2 { - return type_error(vm, "__moduleEval: 1 argument required"); + 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"); } @@ -914,18 +927,50 @@ pub fn module_eval(vm: &mut H, args: &[u8]) -> NativeResult { 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) => NativeResult::Ok(v), + 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)`:创建 deferred namespace 对象(`import defer * as` 产物)。 +/// `__moduleDeferObject(fn, real_ns, path)`:创建 deferred namespace 对象 +/// (`import defer * as` 产物)。 /// /// # 步骤 /// 1. 校验 `fn` 为函数对象(依赖模块函数,编译期 CREATE_CLOSURE 的闭包对象) @@ -934,17 +979,19 @@ pub fn module_eval(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)、失败错误缓存恒 `None`。 +/// `[[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() < 3 { - return type_error(vm, "__moduleDeferObject: 2 arguments required"); + 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(); @@ -975,6 +1022,7 @@ pub fn module_defer_object(vm: &mut H, args: &[u8]) -> NativeResult { evaluated: ns_val.is_object(), namespace: ns_val, error: None, + path, }); } NativeResult::Ok(JsValue::from_js_object(obj)) @@ -1044,7 +1092,9 @@ mod tests { let (mut vm, fn_val) = vm_with_function(); vm.set_reg(1, fn_val); vm.set_reg(2, JsValue::undefined()); - let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); + 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()); @@ -1068,7 +1118,9 @@ mod tests { 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 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]] 应存依赖模块函数"); @@ -1082,7 +1134,9 @@ mod tests { // 哨兵形态: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 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"); @@ -1095,7 +1149,9 @@ mod tests { let (mut vm, _) = vm_with_function(); vm.set_reg(1, JsValue::int(42)); vm.set_reg(2, JsValue::undefined()); - let result = module_defer_object(&mut vm, &[0, 1, 2]); + 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"); } @@ -1104,7 +1160,9 @@ mod tests { 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]); + 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"); } @@ -1116,7 +1174,9 @@ mod tests { // 创建 deferred ns(持有 fn);fn + ns 两个 session 对象。 vm.set_reg(1, fn_val); vm.set_reg(2, JsValue::undefined()); - let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); + 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]] 边。 @@ -1143,7 +1203,9 @@ mod tests { // 创建 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(); + 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 字段(模拟失败缓存),置已求值。 { @@ -1177,7 +1239,9 @@ mod tests { // 模块函数返回非对象(42):求值成功后 [[Namespace]] 为非对象值。 vm.set_reg(1, fn_val); vm.set_reg(2, JsValue::undefined()); - let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); + 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。 @@ -1205,7 +1269,9 @@ mod tests { let real_ns_val = JsValue::from_js_object(real_ns); vm.set_reg(1, fn_val); vm.set_reg(2, real_ns_val); - let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); + 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 条目表)。 diff --git a/crates/oxide_emit/src/module.rs b/crates/oxide_emit/src/module.rs index 6f9ff1cd..1f276b2d 100644 --- a/crates/oxide_emit/src/module.rs +++ b/crates/oxide_emit/src/module.rs @@ -59,6 +59,74 @@ struct DepPhase { attrs: Vec<(String, String)>, } +/// 依赖图节点:模块的依赖路径集(源序)、自身 TLA 标志、提升求值用 IR 副本。 +/// +/// `ir` 仅对含顶层 `await` 的模块(TLA 边界)回填其完整 `IRFunction`:提升求值时 +/// 入口模块须在自己的 `ctx.nested` 持有该模块 IR 才能发 `CREATE_CLOSURE`——传递 +/// TLA 依赖编译在中间模块的 `ctx.nested`,入口侧无直接引用,故按 path 键缓存 IR。 +struct DepNode { + deps: Vec, + has_tla: bool, + ir: Option, +} + +/// 编译期依赖图:`GatherAsynchronousTransitiveDependencies` 的 DFS 游走载体。 +/// +/// 键 = 模块规范路径(与加载器 `resolve` 回填口径一致),值 = 依赖路径集 +/// (源序)+ 自身 TLA 标志 + 嵌套 IR 下标。递归编译期间逐模块登记,defer 依赖 +/// 的 TLA 传递依赖据此按发现序收集并提升求值。 +struct ModuleDepGraph { + nodes: HashMap, +} + +impl ModuleDepGraph { + fn new() -> Self { + Self { nodes: HashMap::new() } + } + + /// 登记模块节点:依赖路径集按源序、`has_tla` 为模块自身顶层 `await` 标志。 + fn record_module(&mut self, path: &str, deps: Vec, has_tla: bool) { + self.nodes.insert(path.to_string(), DepNode { deps, has_tla, ir: None }); + } + + /// 回填 TLA 模块的完整 IR(`compile_js_dep` 组装 IR 后调用,仅 `has_tla` 模块)。 + fn set_ir(&mut self, path: &str, ir: IRFunction) { + if let Some(node) = self.nodes.get_mut(path) { + node.ir = Some(ir); + } + } + + /// 取 TLA 模块的完整 IR(非 TLA 或未登记时为 `None`)。 + fn ir(&self, path: &str) -> Option { + self.nodes.get(path).and_then(|n| n.ir.clone()) + } + + /// `GatherAsynchronousTransitiveDependencies` 编译期等价:DFS 游走依赖图, + /// 按发现序收集 TLA 边界模块(自身含顶层 `await` 的模块;命中即停,不递归)。 + fn gather_tla(&self, root: &str) -> Vec { + let mut seen = HashSet::new(); + let mut result = Vec::new(); + self.gather_dfs(root, &mut seen, &mut result); + result + } + + fn gather_dfs(&self, path: &str, seen: &mut HashSet, result: &mut Vec) { + if !seen.insert(path.to_string()) { + return; + } + let Some(node) = self.nodes.get(path) else { + return; + }; + if node.has_tla { + result.push(path.to_string()); + return; + } + for dep in &node.deps { + self.gather_dfs(dep, seen, result); + } + } +} + /// 模块加载器:由调用方(test262 runner / CLI)提供文件解析。 pub trait ModuleSourceLoader { /// 解析依赖模块:按 `base_dir` 与 `specifier` 定位模块源码,`attributes` 为 @@ -716,7 +784,8 @@ impl Emitter { // ES module 顶层恒严格模式(模块代码是严格模式代码,嵌套函数经父 ctx 继承)。 ctx.is_strict = true; let mut path_stack = vec![module_path.to_string()]; - self.emit_module_into_ctx(program, module_path, loader, &mut path_stack, &mut ctx, true)?; + let mut graph = ModuleDepGraph::new(); + self.emit_module_into_ctx(program, module_path, loader, &mut path_stack, &mut ctx, true, &mut graph)?; Ok(ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None)) } @@ -727,7 +796,7 @@ impl Emitter { #[allow(clippy::too_many_arguments)] fn emit_module_into_ctx( &self, program: &oxide_parser::Program, module_path: &str, loader: &mut dyn ModuleSourceLoader, - path_stack: &mut Vec, ctx: &mut CompileCtx, top_level: bool, + path_stack: &mut Vec, ctx: &mut CompileCtx, top_level: bool, graph: &mut ModuleDepGraph, ) -> Result<(), String> { let body = &program.body; @@ -755,9 +824,9 @@ impl Emitter { ctx.module_ns_reg = Some(ns_reg); // —— 依赖收集(import + re-export/star 的 source,按首次出现去重,记 phase)—— - // eager_order 为各 spec 首个 eager 语句的出现序(求值序);defer_order 为各 spec - // 首次出现序(defer-only 组发射序)。无 defer 的模块两序与首次出现序恒等, - // 发射产物与现态逐字节一致。 + // eager_order 为各 spec 首个 eager 语句的出现序(编译序);defer_order 为各 + // spec 首次出现序(求值发射序,evaluationList 序)。无 defer 的模块两序与 + // 首次出现序恒等,发射产物与现态逐字节一致。 let base_dir = Path::new(module_path) .parent() .map(|p| p.to_string_lossy().to_string()) @@ -765,6 +834,10 @@ impl Emitter { let mut phase_map: HashMap = HashMap::new(); let mut eager_order: Vec = Vec::new(); let mut defer_order: Vec = Vec::new(); + // 语句源序(spec, 是否 defer):evaluationList 求值发射序的权威口径—— + // 每条语句按源位贡献(defer 语句贡献其 TLA 传递依赖、eager 语句贡献模块 + // 本体),mixed 依赖的求值点落在其 eager 语句位(非首次出现位)。 + let mut stmt_order: Vec<(String, bool)> = Vec::new(); for stmt in body { let (spec, attrs, is_defer) = match stmt { Statement::ImportDeclaration(imp) => { @@ -783,6 +856,7 @@ impl Emitter { } _ => continue, }; + stmt_order.push((spec.clone(), is_defer)); let is_new = !phase_map.contains_key(&spec); let entry = phase_map.entry(spec.clone()).or_insert_with(|| DepPhase { eager: false, @@ -811,23 +885,39 @@ impl Emitter { // 异步 eager 依赖的 (spec, `__moduleEval` 返回的 promise 寄存器):prelude // 聚合 gate 消费。defer-only 依赖不求值、不产生 promise,不入此集。 let mut async_dep_promises: Vec<(String, u32)> = Vec::new(); + let mut hoisted_tla_promises: Vec = Vec::new(); - // —— 编译并求值依赖:eager 组先行(按 eager 语句序),defer-only 组随后 —— + // 逐依赖发射数据:编译期(旧序)记录闭包寄存器与求值信息,求值期 + // (evaluationList 序)消费。 + #[derive(Clone)] + struct DepEmitData { + fn_reg: Option, + own_names: HashSet, + var_scoped: HashSet, + eval_async: bool, + path: String, + is_self: bool, + is_defer: bool, + } + let mut dep_data: HashMap = HashMap::new(); + + // —— 阶段一:编译依赖(旧序:eager 组按 eager 语句序、defer-only 组随后), + // 发 CREATE_CLOSURE 并登记子模块树;求值发射推迟到阶段二(evaluationList 序)。—— 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 is_self = resolved.path == module_path; - let (dep_ns_reg, fn_reg, dep_own_names, dep_var_scoped) = if is_self { + let (fn_reg, dep_eval_async, dep_own_names, dep_var_scoped) = if is_self { // 自导入:读自身命名空间(依赖即本模块,勿递归)。 - (ns_reg, None, own_export_names.clone(), var_scoped_export_names.clone()) + (None, false, 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, dep_own_names, dep_var_scoped, dep_eval_async) = match resolved.kind { - ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack)?, + ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack, graph)?, ModuleKind::Json => ( self.compile_data_dep("json", &resolved.source)?, false, @@ -849,65 +939,51 @@ 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)); - let real_ns = self.emit_module_call(ctx, "__moduleEval", &[fn_reg])?; - // 异步依赖:`__moduleEval` 返回 capability promise(非命名空间), - // 登记进聚合 gate;命名空间在 gate 后从 promise 结算值提取。 - if dep_eval_async { - async_dep_promises.push((spec.clone(), real_ns)); - } - (real_ns, Some(fn_reg), dep_own_names, dep_var_scoped) + (Some(fn_reg), dep_eval_async, 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 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)?; - } + dep_data.insert( + spec.clone(), + DepEmitData { + fn_reg, + own_names: dep_own_names, + var_scoped: dep_var_scoped, + eval_async: dep_eval_async, + path: resolved.path, + is_self, + is_defer: phase.defer, + }, + ); } - // 本模块异步求值标志:自身含顶层 `await`,或任一 eager 依赖按异步求值 - // (传递异步经 `dep_eval_async` 递归回传),模块体按合成异步函数编译 - // (`assemble_ir` 据此回写 IR 的 `is_async`)。非 TLA 模块恒 false,零 diff。 - ctx.is_async = own_tla || !async_dep_promises.is_empty(); - - // 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]; + // defer-only 组(eager == false 的 spec,按 defer_order):编译入子模块树、 + // 发 CREATE_CLOSURE,求值发射推迟到阶段二。 + 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 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()); + dep_data.insert( + spec.clone(), + DepEmitData { + fn_reg: None, + own_names: HashSet::new(), + var_scoped: HashSet::new(), + eval_async: false, + path: resolved.path, + is_self: true, + is_defer: true, + }, + ); } else { path_stack.push(resolved.path.clone()); let (dep_ir, _dep_reassignable, dep_own_names, dep_var_scoped, _dep_eval_async) = match resolved.kind { - ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack)?, + ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack, graph)?, ModuleKind::Json => ( self.compile_data_dep("json", &resolved.source)?, false, @@ -928,26 +1004,159 @@ impl Emitter { 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)?; + dep_data.insert( + spec.clone(), + DepEmitData { + fn_reg: Some(fn_reg), + own_names: dep_own_names, + var_scoped: dep_var_scoped, + eval_async: false, + path: resolved.path, + is_self: false, + is_defer: true, + }, + ); } } - // —— 聚合 gate:异步依赖的 `__moduleEval` 返回 capability promise(非命名空间), - // 其命名空间在 gate 后从结算值提取。单 AWAIT 挂起模块体直至全部异步依赖结算 - // (由 drain 微任务释放),非 TLA 模块此集为空、零 IR 变化。—— - if !async_dep_promises.is_empty() { - let promise_regs: Vec = async_dep_promises.iter().map(|(_, r)| *r).collect(); - let gate_reg = self.emit_module_call(ctx, "__moduleWaitAll", &promise_regs)?; + // —— 阶段二:按 evaluationList 序(语句源位交错)发射求值——每条语句按源位 + // 贡献:defer 语句提升其 TLA 传递依赖、eager 语句求值模块本体(去重,mixed + // 依赖的求值点落在其 eager 语句位而非首次出现位)。—— + let mut evaluated_specs: HashSet = HashSet::new(); + let mut hoisted_tla: HashSet = HashSet::new(); + for (spec, is_defer) in &stmt_order { + let phase = &phase_map[spec]; + let data = dep_data.get(spec).cloned().unwrap_or_else(|| { + DepEmitData { + fn_reg: None, + own_names: HashSet::new(), + var_scoped: HashSet::new(), + eval_async: false, + path: String::new(), + is_self: false, + is_defer: false, + } + }); + if *is_defer { + // defer 语句:提升本模块的 TLA 传递依赖(去重,已提升者不重发)。 + // 提升模块在导入方 prelude 期启动(TLA 模块挂起返 promise),其 + // promise 登记进聚合 gate;deferred 模块 prelude 再求值时经 + // per-module 状态取缓存命名空间,防双求值。 + // + // 传递 TLA 依赖编译在中间模块的 `ctx.nested`,入口侧无直接引用: + // 按 path 取图缓存的完整 IR,push 进入口自己的 `ctx.nested` 再发 + // `CREATE_CLOSURE`(下标为 push 后的 `ctx.nested.len()`)。 + let tla_modules = graph.gather_tla(&data.path); + for tla_path in tla_modules { + if !hoisted_tla.insert(tla_path.clone()) { + continue; + } + let Some(tla_ir) = graph.ir(&tla_path) else { + continue; + }; + ctx.nested.push(tla_ir); + let tla_fn_reg = ctx.alloc_reg(); + ctx.inst(Inst::create_closure(Operand::Reg(tla_fn_reg), ctx.nested.len() as u16)); + let tla_path_reg = self.load_string_const(&tla_path, ctx); + let tla_promise = self.emit_module_call(ctx, "__moduleEval", &[tla_fn_reg, tla_path_reg])?; + hoisted_tla_promises.push(tla_promise); + } + if !phase.eager { + // defer-only:deferred 模块本身不求值,发 deferred 对象。 + // path 槽供求值触发点查 per-module 状态(防提升求值后的双求值)。 + if data.is_self { + // 哨兵:模块函数在导入方 prelude 期不可得。 + let undef = self.emit_undefined(ctx); + let path_reg = self.load_string_const(&data.path, ctx); + let defer_ns = + self.emit_module_call(ctx, "__moduleDeferObject", &[undef, undef, path_reg])?; + 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(), data.path.clone()); + } else { + let fn_reg = data.fn_reg.expect("非祖先 defer-only 依赖必有闭包寄存器"); + let undef = self.emit_undefined(ctx); + let path_reg = self.load_string_const(&data.path, ctx); + let defer_ns = + self.emit_module_call(ctx, "__moduleDeferObject", &[fn_reg, undef, path_reg])?; + 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(), data.path.clone()); + self.emit_defer_pre_register(ctx, defer_ns, &data.own_names, &data.var_scoped)?; + } + } + // mixed defer 语句:模块本体在其 eager 语句位求值、deferred 对象亦在 + // eager 语句位发(承载已求值态),此处仅提升 TLA 传递依赖。 + } else { + // eager 语句:求值模块本体(去重,mixed 依赖仅在其 eager 语句位求值一次)。 + if !evaluated_specs.contains(spec) { + let (dep_ns_reg, fn_reg) = if data.is_self { + // 自导入:读自身命名空间(依赖即本模块,勿递归)。 + (ns_reg, None) + } else { + let fn_reg = data.fn_reg.expect("非自导入 eager 依赖必有闭包寄存器"); + let path_reg = self.load_string_const(&data.path, ctx); + let real_ns = self.emit_module_call(ctx, "__moduleEval", &[fn_reg, path_reg])?; + // 异步依赖:`__moduleEval` 返回 capability promise(非命名空间), + // 登记进聚合 gate;命名空间在 gate 后从 promise 结算值提取。 + if data.eval_async { + async_dep_promises.push((spec.clone(), real_ns)); + } + (real_ns, Some(fn_reg)) + }; + ctx.module_dep_ns_regs.insert(spec.clone(), dep_ns_reg); + ctx.module_dep_paths.insert(spec.clone(), data.path.clone()); + if data.is_self { + // 自导入:绑定走别名语义(源导出在 body 执行中才就绪), + // 不能像普通依赖那样链接期快照。 + ctx.module_self_import_specs.insert(spec.clone()); + } + evaluated_specs.insert(spec.clone()); + if data.is_defer { + // mixed 依赖:模块已急切求值,deferred 对象承载已求值态 + // ([[Evaluated]]=true、[[Namespace]]=真实 ns)。 + let fn_or_sentinel = if data.is_self { + // 自导入 mixed:模块函数在导入方 prelude 期不可得,发哨兵形态。 + self.emit_undefined(ctx) + } else { + fn_reg.expect("非自导入 eager 依赖必有闭包寄存器") + }; + let path_reg = self.load_string_const(&data.path, ctx); + let defer_ns = + self.emit_module_call(ctx, "__moduleDeferObject", &[fn_or_sentinel, dep_ns_reg, path_reg])?; + ctx.module_defer_ns_regs.insert(spec.clone(), defer_ns); + self.emit_defer_pre_register(ctx, defer_ns, &data.own_names, &data.var_scoped)?; + } + } + } + } + + // 登记本模块图节点:依赖路径集按源序(defer_order)、has_tla 为自身顶层 + // `await` 标志。 + let dep_paths_in_source_order: Vec = + defer_order.iter().filter_map(|s| dep_data.get(s).map(|d| d.path.clone())).collect(); + graph.record_module(module_path, dep_paths_in_source_order, own_tla); + + // 本模块异步求值标志:自身含顶层 `await`,或任一 eager 依赖按异步求值 + // (传递异步经 `dep_eval_async` 递归回传),或存在提升的 TLA 传递依赖 + // (其 promise 须由聚合 gate 等待)。模块体按合成异步函数编译 + // (`assemble_ir` 据此回写 IR 的 `is_async`)。非 TLA 模块恒 false,零 diff。 + ctx.is_async = own_tla || !async_dep_promises.is_empty() || !hoisted_tla_promises.is_empty(); + + // —— 聚合 gate:异步依赖与提升 TLA 模块的 `__moduleEval` 返回 capability + // promise(非命名空间),其命名空间在 gate 后从结算值提取(仅直接依赖)。 + // 单 AWAIT 挂起模块体直至全部 promise 结算(由 drain 微任务释放), + // 非 TLA 模块此集为空、零 IR 变化。—— + let all_promises: Vec = + async_dep_promises.iter().map(|(_, r)| *r).chain(hoisted_tla_promises.iter().copied()).collect(); + if !all_promises.is_empty() { + let gate_reg = self.emit_module_call(ctx, "__moduleWaitAll", &all_promises)?; ctx.inst(Inst::await_expr(Operand::Reg(gate_reg))); // promise settle 后恢复值(结算值数组)经 reg 0 交付(与 YIELD/CALL 同协议)。 let settled_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(settled_reg), Operand::None, Operand::None)); - // 逐依赖从结算数组提取命名空间,覆盖链接寄存器(原为 promise)。 + // 逐直接依赖从结算数组提取命名空间,覆盖链接寄存器(原为 promise); + // 提升 TLA 模块非直接依赖,不提取(其命名空间由 per-module 状态承载)。 for (i, (spec, _)) in async_dep_promises.iter().enumerate() { let idx_reg = self.load_string_const(&i.to_string(), ctx); let ns_reg = ctx.alloc_reg(); @@ -1289,6 +1498,7 @@ impl Emitter { #[allow(clippy::type_complexity)] fn compile_js_dep( &self, resolved: &ResolvedModule, loader: &mut dyn ModuleSourceLoader, path_stack: &mut Vec, + graph: &mut ModuleDepGraph, ) -> Result<(IRFunction, bool, HashSet, HashSet, bool), String> { // 线程本地分配器:依赖模块递归解析共享同一分配器, // 重入深度守卫防止嵌套 reset 销毁外层 AST。 @@ -1303,13 +1513,19 @@ impl Emitter { ctx.is_global_scope = false; // 依赖模块经 parse_module 解析,顶层恒严格模式。 ctx.is_strict = true; - self.emit_module_into_ctx(&program, &resolved.path, loader, path_stack, &mut ctx, false)?; + self.emit_module_into_ctx(&program, &resolved.path, loader, path_stack, &mut ctx, false, graph)?; let reassignable = ctx.module_live_dep; let eval_async = ctx.is_async; let own_export_names = module_own_export_names(&program.body); let var_scoped = module_var_scoped_export_names(&program.body); + let ir = ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None); + // TLA 模块:缓存完整 IR 供导入方提升求值(入口侧 `ctx.nested` 无直接 + // 引用,须按 path 键取副本)。非 TLA 模块不缓存,零额外开销。 + if module_has_top_level_await(&program.body) { + graph.set_ir(&resolved.path, ir.clone()); + } Ok(( - ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None), + ir, reassignable, own_export_names, var_scoped, diff --git a/crates/oxide_emit/tests/module_link_tests.rs b/crates/oxide_emit/tests/module_link_tests.rs index 0c18bf66..5595f558 100644 --- a/crates/oxide_emit/tests/module_link_tests.rs +++ b/crates/oxide_emit/tests/module_link_tests.rs @@ -612,3 +612,54 @@ fn extends_await_makes_module_async() { "顶层 await 应编译为一条 AWAIT" ); } + +/// 面 30:defer-only 依赖含 TLA——TLA 传递依赖提升求值(导入方 prelude 发 +/// `__moduleEval`),deferred 模块本身不求值;导入方标记异步、发聚合 gate。 +#[test] +fn defer_only_dep_with_tla_hoists_tla_module() { + let (ir, _) = emit_module( + "import defer * as ns from \"./dep.js\"; ns;", + "./entry.js", + &[ + ("./dep.js", ModuleKind::Js, "import \"./tla.js\"; export const x = 1;"), + ("./tla.js", ModuleKind::Js, "const v = await Promise.resolve(1); export { v };"), + ], + ) + .expect("defer-only 依赖含 TLA 应编译成功"); + // 入口:1 个提升 TLA 模块(tla)求值一次;deferred 模块(dep)不求值。 + assert_eq!(native_calls_to(&ir, "__moduleEval"), 1, "提升 TLA 模块应求值一次"); + assert_eq!(native_calls_to(&ir, "__moduleDeferObject"), 1, "deferred 对象应构造一次"); + assert!(ir.is_async, "含提升 TLA 模块的导入方应标记异步求值"); + assert_eq!(native_calls_to(&ir, "__moduleWaitAll"), 1, "聚合 gate 应发一次"); +} + +/// 面 31:defer-only 依赖的传递 TLA——DFS 序收集 TLA 边界模块,提升求值一次。 +#[test] +fn defer_only_dep_with_transitive_tla_hoists_in_dfs_order() { + let (ir, _) = emit_module( + "import defer * as ns from \"./dep.js\"; ns;", + "./entry.js", + &[ + ("./dep.js", ModuleKind::Js, "import \"./mid.js\"; export const x = 1;"), + ("./mid.js", ModuleKind::Js, "import \"./tla.js\"; export const y = 2;"), + ("./tla.js", ModuleKind::Js, "const v = await Promise.resolve(1); export { v };"), + ], + ) + .expect("defer-only 依赖含传递 TLA 应编译成功"); + assert_eq!(native_calls_to(&ir, "__moduleEval"), 1, "传递 TLA 模块应提升求值一次"); + assert!(ir.is_async, "含传递 TLA 模块的导入方应标记异步求值"); +} + +/// 面 32:defer-only 依赖无 TLA——零提升、零 gate、导入方保持同步(零 diff 守卫)。 +#[test] +fn defer_only_dep_without_tla_no_hoisting() { + let (ir, _) = emit_module( + "import defer * as ns from \"./dep.js\"; ns;", + "./entry.js", + &[("./dep.js", ModuleKind::Js, "export const x = 1;")], + ) + .expect("defer-only 依赖无 TLA 应编译成功"); + assert_eq!(native_calls_to(&ir, "__moduleEval"), 0, "无 TLA 的 defer-only 依赖不得求值"); + assert_eq!(native_calls_to(&ir, "__moduleWaitAll"), 0, "无 TLA 模块不得发聚合 gate"); + assert!(!ir.is_async, "无 TLA 模块的导入方不得标记异步"); +} diff --git a/crates/oxide_runtime_api/src/host.rs b/crates/oxide_runtime_api/src/host.rs index 6792f334..7b92195e 100644 --- a/crates/oxide_runtime_api/src/host.rs +++ b/crates/oxide_runtime_api/src/host.rs @@ -23,6 +23,16 @@ pub struct ShapeNode { pub depth: u32, } +/// 模块求值状态(per-module 防双求值):键 = 模块规范路径。 +/// +/// `evaluated` 为真时 `namespace` 为已求值命名空间;为假时 `namespace` 承载 +/// 启动中的 capability promise(TLA 模块挂起未结算),调用方据结算值取命名空间。 +#[derive(Clone, Debug)] +pub struct ModuleEvalState { + pub evaluated: bool, + pub namespace: JsValue, +} + /// 内建对象种类:session builtin world 中 builtins 读取的原型与构造器槽位。 /// /// 变体与 builtin world 字段一一对应,仅覆盖 builtins 实际读取的槽位; @@ -306,6 +316,30 @@ pub trait VmHost { // 调用基础设施 fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result; + /// 异步模块求值驱动:同步执行模块函数到静止并取结算命名空间。 + /// + /// # 步骤 + /// 1. `call_function_sync(callee)`:TLA 模块返 capability promise(body 同步 + /// 执行到首个 AWAIT),同步模块返命名空间。 + /// 2. 返值为 promise 时单次 `drain_microtasks` 排空到静止,取结算值; + /// 返值为命名空间时原样返回。 + /// + /// # 边界与前提 + /// - 返值为 promise 且 drain 后仍 pending(宏任务挂起)时返该 promise + /// (不阻塞);rejected 时取拒绝原因经 `Err` 上交。 + /// + /// # 副作用 + /// - 可能执行用户模块代码(含分配与 GC);drain 排空微任务队列。 + fn evaluate_module_async(&mut self, callee: JsValue) -> Result; + /// 是否原生 Promise 对象(capability promise 判定)。 + fn is_promise_value(&self, v: JsValue) -> bool; + /// 取 promise 结算值:`Some((is_fulfilled, value))`,pending 返 `None`。 + fn promise_settled_value(&self, promise: JsValue) -> Option<(bool, JsValue)>; + /// 读 per-module 求值状态(键 = 模块规范路径):`None` 未求值。 + fn get_module_eval_state(&self, path: &str) -> Option; + /// 置 per-module 求值状态(键 = 模块规范路径):`evaluated` 为真时 + /// `namespace` 为命名空间,为假时 `namespace` 承载 capability promise。 + fn set_module_eval_state(&mut self, path: &str, evaluated: bool, namespace: JsValue); /// deferred namespace 求值触发(`EnsureDeferredNamespaceEvaluation`): /// 对 deferred 且未求值的 module namespace 同步执行依赖模块函数,缓存结果 /// 命名空间或失败错误对象。 diff --git a/crates/oxide_runtime_api/src/lib.rs b/crates/oxide_runtime_api/src/lib.rs index 654034e4..a4372586 100644 --- a/crates/oxide_runtime_api/src/lib.rs +++ b/crates/oxide_runtime_api/src/lib.rs @@ -32,5 +32,5 @@ pub use coercion::{ }; pub use compiler_service::CompilerService; pub use event_target::{EventTargetState, ListenerEntry}; -pub use host::{ProtoKind, ShapeNode, VmHost}; +pub use host::{ModuleEvalState, ProtoKind, ShapeNode, VmHost}; pub use native_result::NativeResult; diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index 899fd3b4..f154d056 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -20,6 +20,7 @@ use crate::realm::Realm; use crate::vm_debug; use crate::vm_state::{IterState, ProfilingState, SampleState}; use oxide_kernel::kernel::{KernelCore, KernelSession}; +use oxide_runtime_api::ModuleEvalState; use oxide_types::error::JsErrorKind; use oxide_types::object::{Cell, JsObject, JsString, NativeFnPtr}; use oxide_types::private_key::INT_KEY_COUNT; @@ -314,6 +315,11 @@ pub struct Vm { /// 在 `call_function_sync` 前置 `Some(fn)`,两者均 save/restore(嵌套安全)。 /// 触发点据此判自求值/祖先求值中的 cyclic 并抛 TypeError。 pub(crate) evaluating_module: Option, + /// per-module 求值状态(键 = 模块规范路径):防 hoisted TLA 模块与 + /// deferred 模块 prelude 的双求值。`evaluated` 为真时 `namespace` 为 + /// 已求值命名空间,为假时 `namespace` 承载启动中的 capability promise。 + /// 生命周期与 Vm 同消亡:`full_reset` 清空。 + pub(crate) module_eval_states: std::collections::HashMap, /// 数组 length define 强转期用户代码抛出的原始异常:与 `last_uncaught_value` /// 分离,避免入口误取其它操作忽略调用残留的值。仅 `define_array_length` 写入, /// 由 Object/Reflect define 入口消费。 diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index 02a910b3..cbba128d 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -4,7 +4,7 @@ use std::ffi::c_void; use std::sync::Arc; use oxide_kernel::KernelCore; -use oxide_runtime_api::{ListenerEntry, ProtoKind, ShapeNode}; +use oxide_runtime_api::{ListenerEntry, ModuleEvalState, ProtoKind, ShapeNode}; use oxide_types::mem::P; use oxide_types::object::{Cell, JsObject, PropAttributes}; use oxide_types::value::JsValue; @@ -262,6 +262,44 @@ impl oxide_runtime_api::VmHost for Vm { fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result { self.call_function_sync(callee, receiver, args) } + fn evaluate_module_async(&mut self, callee: JsValue) -> Result { + // 同步执行模块函数到首个挂起点:TLA 模块返 capability promise,同步模块返命名空间。 + let result = self.call_function_sync(callee, JsValue::undefined(), &[])?; + // 返值为 promise(TLA 模块):单次 drain 排空到静止,取结算值。 + if self.is_promise_value(result) { + self.drain_microtasks(); + let obj = unsafe { &*result.as_js_object_ptr() }; + return match crate::promise::promise_settled_value(obj) { + Some((true, value)) => Ok(value), + Some((false, reason)) => Err(self.error_text(reason)), + // 宏任务挂起(drain 后仍 pending):返 capability promise,不阻塞。 + None => Ok(result), + }; + } + // 同步模块:命名空间原样返回。 + Ok(result) + } + fn is_promise_value(&self, v: JsValue) -> bool { + self.is_promise_value(v) + } + fn promise_settled_value(&self, promise: JsValue) -> Option<(bool, JsValue)> { + if !promise.is_object() { + return None; + } + let ptr = promise.as_js_object_ptr(); + if ptr.is_null() { + return None; + } + let obj = unsafe { &*ptr }; + crate::promise::promise_settled_value(obj) + } + fn get_module_eval_state(&self, path: &str) -> Option { + self.module_eval_states.get(path).cloned() + } + fn set_module_eval_state(&mut self, path: &str, evaluated: bool, namespace: JsValue) { + self.module_eval_states + .insert(path.to_string(), ModuleEvalState { evaluated, namespace }); + } fn ensure_deferred_ns_evaluation(&mut self, obj: &JsObject, key_si: Option) -> Result<(), String> { // 非 deferred module namespace 直接 no-op(热路径零成本)。 if !obj.is_module_namespace() || !obj.is_deferred() { @@ -303,8 +341,24 @@ impl oxide_runtime_api::VmHost for Vm { if self.evaluating_module == Some(state.module) { return Err(self.error_message_text("TypeError", "cyclic module evaluation")); } - // EvaluateSync:调依赖模块函数,缓存结果命名空间或失败错误。 - match self.call_function_sync(state.module, JsValue::undefined(), &[]) { + // 提升求值过的 TLA 模块:per-module 状态已结算(或结算中),直接取缓存 + // 命名空间,防 `evaluate_module_async` 重跑模块函数(双求值)。 + if let Some(ms) = self.get_module_eval_state(&state.path) { + if ms.evaluated { + state.namespace = ms.namespace; + state.evaluated = true; + return Ok(()); + } + if let Some((true, value)) = self.promise_settled_value(ms.namespace) { + self.set_module_eval_state(&state.path, true, value); + state.namespace = value; + state.evaluated = true; + return Ok(()); + } + // pending 且未结算:落入下方正常求值(聚合 gate 后不应发生,兜底)。 + } + // EvaluateSync:调依赖模块函数(异步驱动到静止),缓存结果命名空间或失败错误。 + match self.evaluate_module_async(state.module) { Ok(ns) => { state.namespace = ns; state.evaluated = true; diff --git a/crates/oxide_vm/src/vm_props.rs b/crates/oxide_vm/src/vm_props.rs index 407e2f85..19bb3fd5 100644 --- a/crates/oxide_vm/src/vm_props.rs +++ b/crates/oxide_vm/src/vm_props.rs @@ -2049,7 +2049,9 @@ mod tests { fn make_deferred_ns(vm: &mut Vm, fn_val: JsValue) -> JsValue { vm.set_reg(1, fn_val); vm.set_reg(2, JsValue::undefined()); - oxide_builtins::module::module_defer_object(vm, &[0, 1, 2]).unwrap() + let path_str = vm.new_string("p"); + vm.set_reg(3, path_str); + oxide_builtins::module::module_defer_object(vm, &[0, 1, 2, 3]).unwrap() } #[test] @@ -2077,7 +2079,9 @@ mod tests { // 哨兵形态:[[Module]] = undefined(自导入/祖先 defer)。 vm.set_reg(1, JsValue::undefined()); vm.set_reg(2, JsValue::undefined()); - let ns_val = oxide_builtins::module::module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); + let path_str = vm.new_string("p"); + vm.set_reg(3, path_str); + let ns_val = oxide_builtins::module::module_defer_object(&mut vm, &[0, 1, 2, 3]).unwrap(); let ns_ptr = ns_val.as_js_object_ptr(); // 模拟「正在求值模块」:置 cyclic 守卫标记。 @@ -2099,7 +2103,9 @@ mod tests { .expect("create function"); vm.set_reg(1, throwing_fn); vm.set_reg(2, JsValue::undefined()); - let ns_val = oxide_builtins::module::module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); + let path_str = vm.new_string("p"); + vm.set_reg(3, path_str); + let ns_val = oxide_builtins::module::module_defer_object(&mut vm, &[0, 1, 2, 3]).unwrap(); let ns_ptr = ns_val.as_js_object_ptr(); let x_si = vm.kernel_core.perm_interner().intern("x").0; diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 08ba614b..bd479cec 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -195,6 +195,7 @@ impl Vm { exception_value: None, last_uncaught_value: None, evaluating_module: None, + module_eval_states: std::collections::HashMap::new(), pending_length_exception: None, pending_exception: None, pending_error_kind: None, @@ -363,6 +364,7 @@ impl Vm { exception_value: None, last_uncaught_value: None, evaluating_module: None, + module_eval_states: std::collections::HashMap::new(), pending_length_exception: None, pending_exception: None, pending_error_kind: None, @@ -605,6 +607,9 @@ impl Vm { // 池回收后残留将悬垂。 self.last_uncaught_value = None; self.pending_length_exception = None; + // per-module 求值状态持 session 对象指针(命名空间/promise):执行期状态, + // 跨 run/reset 不保留,残留将悬垂。 + self.module_eval_states.clear(); self.pending_exception = None; self.pending_error_kind = None; self.pending_completion = None;