docs: 补全 Aster 中文调用链、控制流与生命周期注释 #3
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user