feat: import defer 的 TLA 传递依赖按规范序提升求值

This commit is contained in:
2026-10-14 05:16:34 +00:00
parent 2e0197486c
commit 43f15ad2ee
11 changed files with 555 additions and 117 deletions
+6
View File
@@ -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<JsValue>,
/// per-module 求值状态(键 = 模块规范路径):防 hoisted TLA 模块与
/// deferred 模块 prelude 的双求值。`evaluated` 为真时 `namespace` 为
/// 已求值命名空间,为假时 `namespace` 承载启动中的 capability promise。
/// 生命周期与 Vm 同消亡:`full_reset` 清空。
pub(crate) module_eval_states: std::collections::HashMap<String, ModuleEvalState>,
/// 数组 length define 强转期用户代码抛出的原始异常:与 `last_uncaught_value`
/// 分离,避免入口误取其它操作忽略调用残留的值。仅 `define_array_length` 写入,
/// 由 Object/Reflect define 入口消费。
+57 -3
View File
@@ -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<JsValue, String> {
self.call_function_sync(callee, receiver, args)
}
fn evaluate_module_async(&mut self, callee: JsValue) -> Result<JsValue, String> {
// 同步执行模块函数到首个挂起点: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<ModuleEvalState> {
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<u32>) -> 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;
+9 -3
View File
@@ -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;
+5
View File
@@ -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;