docs: 补全 Aster 中文调用链、控制流与生命周期注释 #3

Merged
Zayden-Jung merged 62 commits from codex/detailed-chinese-comments into main 2026-11-15 12:13:30 +00:00
Showing only changes of commit 2ca839c0d8 - Show all commits
+67
View File
@@ -1986,46 +1986,66 @@ fn managed_coroutine_hard_budget_accumulates_and_error_releases_frames() {
assert_eq!(vm.object_count(), 0);
}
/// await 测试专用宿主载荷,由测试分配到 VM;eval 的 Await 指令通过 ObjectPayload 轮询它。
/// 只切换宿主就绪标志,不改变托管引用边;结果对象始终由 trace 报告给 Heap。
#[derive(Debug)]
struct AwaitGate {
// 测试与载荷共享就绪标志;测试 Release 写入,poll_await Acquire 读取。
ready: Arc<std::sync::atomic::AtomicBool>,
// 就绪后返回的借用值;若为对象,分配时由 trace 建立内部拥有边。
value: Value,
}
// 实现宿主对象协议,接入 Store 的种类检查、Heap 的引用遍历和 eval 的等待操作。
impl ObjectPayload for AwaitGate {
/// Store/宿主载荷检查调用,保留具体 Rust 类型以支持 Any 下转。
fn as_any(&self) -> &dyn Any {
self
}
/// 对象种类诊断调用,提供稳定的测试载荷名称。
fn kind_name(&self) -> &'static str {
"await-gate"
}
/// Heap 分配、回收时遍历内部对象边;立即值没有托管引用,无需报告。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
// 只有对象值携带堆引用;必须报告这一边,使测试释放原根后结果仍存活。
if let Value::Object(reference) = self.value {
visit(reference);
}
}
/// eval 每次进入 Await 时调用;非阻塞返回 Pending 或借用 Ready 值,不自行增加结果根。
fn poll_await(&self) -> Result<endor_core::AwaitPoll, VmError> {
// 与测试的 Release 写入配对;标志可变而 value 的托管引用关系保持不变。
Ok(if self.ready.load(Ordering::Acquire) {
// clone 只复制 Value 表示;eval 在丢弃等待操作数前为结果取得独立根。
endor_core::AwaitPoll::Ready(self.value.clone())
} else {
// 尚未就绪时帧保留原操作数与 Await 位置,下次只重新轮询该指令。
endor_core::AwaitPoll::Pending
})
}
}
#[test]
/// 由 Rust 测试框架调用,验证 Await 停放不重放前缀,等待图保活结果,完成后结果根独立于协程存活。
fn await_keeps_operand_and_owned_object_result_without_replaying_prefix() {
use endor_core::CoroutineProgress;
// 本测试独享堆和协程执行状态,最后检查结果归还后没有残留对象。
let mut vm = Vm::new().unwrap();
// 宿主持一个共享标志,初始未就绪;停放后通过它唤醒下一次轮询。
let ready = Arc::new(std::sync::atomic::AtomicBool::new(false));
// 分配最终结果并写入 marker,稍后释放原根以排除宿主根掩盖保活错误。
let value = vm.new_object().unwrap();
vm.set_attr(value, "marker", text("kept")).unwrap();
// 分配等待载荷,Heap 根据 trace 保留 value;宿主标志使用 Arc 共享。
let gate = vm
.allocate(AwaitGate {
ready: ready.clone(),
value: Value::Object(value),
})
.unwrap();
// 环境保存可观察的前缀副作用,等待后宿主将改写它以检测指令重放。
let globals = vm.new_dict().unwrap();
// 先写 step,再载入 gate 并 Await,最后返回等待结果;两次恢复应只重试 Await。
let code = make_code(
&mut vm,
&[
@@ -2041,21 +2061,30 @@ fn await_keeps_operand_and_owned_object_result_without_replaying_prefix() {
..CodeLayout::default()
},
);
// 函数拥有代码与环境,代码常量表另持 gate。
let function = vm
.new_function("awaited", code, globals, vec![], vec![])
.unwrap();
// 创建可变托管协程;普通同步入口不支持停放的 Pending 等待。
let coroutine = vm
.new_coroutine(function, &[], ExecutionLimits::default())
.unwrap();
// 首轮应返回 Waiting,携带的借用等待对象必须正是 gate。
assert!(
matches!(vm.resume_coroutine(coroutine, 64).unwrap(), CoroutineProgress::Waiting { awaitable, .. } if awaitable == gate)
);
// 归还结果原根,接下来靠 gate 的内部边保活。
vm.release(value).unwrap();
// 归还等待对象原根,代码常量及停放帧操作数仍持有它。
vm.release(gate).unwrap();
// 等待期间宿主改写前缀结果;恢复后必须保留该写入。
vm.dict_set(globals, text("step"), text("interleaved"))
.unwrap();
// 强制回收检查等待图仍可达,不能把帧上的 await 操作数误判为垃圾。
vm.collect_cycles();
// 发布就绪状态;下一次恢复应从保存的 Await 指令重新轮询。
ready.store(true, Ordering::Release);
// 就绪后应取得拥有结果;不应再次返回 Waiting 或重跑整段代码。
let CoroutineProgress::Complete { value: result, .. } =
vm.resume_coroutine(coroutine, 64).unwrap()
else {
@@ -2065,26 +2094,36 @@ fn await_keeps_operand_and_owned_object_result_without_replaying_prefix() {
vm.dict_get(globals, &text("step")).unwrap(),
Some(text("interleaved"))
);
// 清除协程、函数、代码与环境的测试根,验证结果根本身足以保持对象。
for object in [coroutine, function, code, globals] {
vm.release(object).unwrap();
}
// 结果必须仍是有效对象,marker 检查不能依赖已释放的等待图。
let Value::Object(reference) = result else {
panic!("owned object result")
};
assert_eq!(vm.get_attr(reference, "marker").unwrap(), text("kept"));
// 最后归还调用结果拥有根,此时堆应为空。
vm.release_value(result).unwrap();
assert_eq!(vm.object_count(), 0);
}
#[test]
/// 由 Rust 测试框架调用,比较切片、同步精确及兼容读取集合;验证无关名称不使名称依赖失效,而遮蔽、命中值变化及对象销毁会失效。
fn named_reads_ignore_unrelated_globals_but_track_missing_shadow_and_payload_kind() {
// 隔离读取作用域与对象生命周期,后续销毁测试不会受其他用例影响。
let mut vm = Vm::new().unwrap();
// 全局字典起初缺 name;这个缺失也是必须记录的观察。
let globals = vm.new_dict().unwrap();
// 回退字典提供 name=builtin,全局缺失后才读取它。
let builtins = vm.new_dict().unwrap();
// 初始化回退值,后续全局缺失时应读取这个内置名称。
vm.dict_set(builtins, text("name"), text("builtin"))
.unwrap();
// 在函数创建前绑定内置字典,函数保存该内置环境引用。
vm.dict_set(globals, text("__builtins__"), Value::Object(builtins))
.unwrap();
// 只读取名称 name 后返回,避免其他内容读取把依赖扩大为整个字典版本。
let code = make_code(
&mut vm,
&[op(Opcode::LoadGlobal, 0), simple(Opcode::ReturnValue)],
@@ -2094,9 +2133,11 @@ fn named_reads_ignore_unrelated_globals_but_track_missing_shadow_and_payload_kin
..CodeLayout::default()
},
);
// 构造具有独立代码/环境内部边的被测函数。
let function = vm
.new_function("read-name", code, globals, vec![], vec![])
.unwrap();
// 量子足够一次完成;分别取得拥有结果、兼容对象集合和精确依赖快照。
let endor_core::ExecutionProgress::Complete {
value,
reads,
@@ -2109,38 +2150,48 @@ fn named_reads_ignore_unrelated_globals_but_track_missing_shadow_and_payload_kin
panic!("complete")
};
assert_eq!(value, text("builtin"));
// 全局缺失观察与内置命中观察都必须出现在兼容读取集合里。
assert!(reads.contains(&globals) && reads.contains(&builtins));
// 归还本次调用结果;读取集合和依赖本身只是借用记录,不新增对象根。
vm.release_value(value).unwrap();
// 增加无关名称,不能影响此前 name 缺失与内置命中的精确观察。
vm.dict_set(globals, text("_"), text("unrelated")).unwrap();
// 同步精确入口应产生与切片入口相同的对象集合及依赖。
let (value, synchronous) = vm
.call_read_only_precise_with_limits(function, &[], ExecutionLimits::default())
.unwrap();
assert_eq!(value, text("builtin"));
assert_eq!(synchronous.dependencies, dependencies);
assert_eq!(synchronous.objects, reads);
// 归还本次调用结果;读取集合和依赖本身只是借用记录,不新增对象根。
vm.release_value(value).unwrap();
// 兼容入口仍返回对象集合,验证旧接口与精确接口的覆盖一致。
let (value, conservative) = vm
.call_read_only_with_limits(function, &[], ExecutionLimits::default())
.unwrap();
assert_eq!(conservative, reads);
// 归还本次调用结果;读取集合和依赖本身只是借用记录,不新增对象根。
vm.release_value(value).unwrap();
assert!(
dependencies
.iter()
.all(|dependency| vm.object_store().read_dependency_valid(dependency))
);
// 新增同名全局遮蔽内置值,必须使原来的全局缺失依赖失效。
vm.dict_set(globals, text("name"), text("shadow")).unwrap();
assert!(
dependencies
.iter()
.any(|dependency| !vm.object_store().read_dependency_valid(dependency))
);
// 删除遮蔽后,名称观察重新与原快照一致;依赖不是永久失效标记。
vm.dict_delete(globals, &text("name")).unwrap();
assert!(
dependencies
.iter()
.all(|dependency| vm.object_store().read_dependency_valid(dependency))
);
// 实际命中值变化使内置名称快照失效。
vm.dict_set(builtins, text("name"), text("changed"))
.unwrap();
assert!(
@@ -2148,6 +2199,7 @@ fn named_reads_ignore_unrelated_globals_but_track_missing_shadow_and_payload_kin
.iter()
.any(|dependency| !vm.object_store().read_dependency_valid(dependency))
);
// 归还全部测试根,环境和代码图释放后原依赖中的句柄不再有效。
for object in [function, code, globals, builtins] {
vm.release(object).unwrap();
}
@@ -2160,13 +2212,20 @@ fn named_reads_ignore_unrelated_globals_but_track_missing_shadow_and_payload_kin
}
#[test]
/// 由 Rust 测试框架调用,直接在 ObjectStore 检查子区间读取及重验均登记到外层;按名称依赖保持精确,整字典读取仍保守失效。
fn precise_nested_regions_replay_borrowed_dependencies_without_hiding_full_dict_reads() {
// 直接使用 ObjectStore,只验证读取追踪,无需创建可执行函数或运行解释器。
let mut vm = endor_core::ObjectStore::new().unwrap();
// 准备只含 name 的字典,之后添加其他名称来区分精确与整对象观察。
let globals = vm.new_dict().unwrap();
vm.dict_set(globals, text("name"), text("value")).unwrap();
// 开启只读外层作用域,收集兼容对象集合及精确观察,退出后才允许写入旧对象。
let scope = vm.begin_read_only();
// 在现有只读作用域内标记子区间,结束子区间不会清除外层已登记的依赖。
let token = vm.begin_read_tracking().unwrap();
// 记录字典种类与指定名称命中值,不记录无关名称或整个内容版本。
vm.dict_get_name(globals, "name").unwrap();
// 结束子区间,取得借用的访问分类与依赖;这些记录不会自动持有对象根。
let tracked = vm.end_read_tracking_precise(token).unwrap();
assert!(
tracked
@@ -2174,10 +2233,13 @@ fn precise_nested_regions_replay_borrowed_dependencies_without_hiding_full_dict_
.iter()
.any(|access| access.object == globals)
);
// 在外层作用域仍开启时重验子区间依赖;正常读取入口会再次登记观察。
for dependency in &tracked.dependencies {
assert!(vm.read_dependency_valid(dependency));
}
// 结束外层只读作用域取得累计观察,再进行写入以检验失效范围。
let reads = vm.end_read_only_precise(scope).unwrap();
// 首次只读仅观察 name,新增 other 后所有原依赖应仍然有效。
vm.dict_set(globals, text("other"), Value::Nil).unwrap();
assert!(
reads
@@ -2185,9 +2247,13 @@ fn precise_nested_regions_replay_borrowed_dependencies_without_hiding_full_dict_
.iter()
.all(|dependency| vm.read_dependency_valid(dependency))
);
// 开启只读外层作用域,收集兼容对象集合及精确观察,退出后才允许写入旧对象。
let scope = vm.begin_read_only();
// 第二轮读取全部条目,必须记录整字典版本,不能被之前的精确读取策略掩盖。
vm.dict_items(globals).unwrap();
// 结束外层只读作用域取得累计观察,再进行写入以检验失效范围。
let reads = vm.end_read_only_precise(scope).unwrap();
// 整字典内容变化后,应至少有一项整对象依赖失效。
vm.dict_set(globals, text("third"), Value::Nil).unwrap();
assert!(
reads
@@ -2195,6 +2261,7 @@ fn precise_nested_regions_replay_borrowed_dependencies_without_hiding_full_dict_
.iter()
.any(|dependency| !vm.read_dependency_valid(dependency))
);
// 归还字典唯一外部根;读取记录不拥有根,因此堆应清空。
vm.release(globals).unwrap();
assert_eq!(vm.object_count(), 0);
}