//! Rust 测试框架调用的 VM/Store 基础协议集成场景,不依赖源码编译器。 //! Resource 用 Rust Drop 计数观察实际析构,Number/ReflectedOrdering 验证宿主载荷协议覆盖和反向分派。 //! 每个测试独立建 VM;未显式归还的根及未收集环最终仍须由整个 VM 的销毁清理。 use std::any::Any; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use endor_core::{CompareOp, NativeFunction, ObjectPayload, ObjectRef, Record, Value, Vm, VmError}; #[derive(Debug)] /// 生命周期场景的宿主资源:以 Drop 计数观测真正销毁,edges 同时让载荷参与托管图。 struct Resource { /// VM 外观察实际析构次数的共享计数器。 drops: Arc, /// 分配时由 Heap 快照持有的内部强边,含重复项。 edges: Vec, } impl Drop for Resource { /// Resource 真正销毁时累加共享计数,区分句柄复制/归还与实际 Rust 资源释放。 fn drop(&mut self) { self.drops.fetch_add(1, Ordering::SeqCst); } } impl ObjectPayload for Resource { /// Store/比较协议的具体载荷下转型入口,不依赖语言名义类型。 fn as_any(&self) -> &dyn Any { self } /// 错误与诊断显示的类别标签。 fn kind_name(&self) -> &'static str { "resource" } /// Heap 分配时枚举载荷拥有边;重复项必须逐项计数,不能去重。 fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) { // 每个槽都是独立拥有边,不能因为身份相同而合并计数。 for reference in &self.edges { visit(*reference); } } /// Resource 允许公共属性表,使测试能组合载荷边和属性边成环。 fn allows_attributes(&self) -> bool { true } } #[derive(Debug)] /// 协议覆盖场景的自定义整数载荷,与内置 EndorNumberObject 分开,专门验证 Store 的动态分派。 struct Number(i64); impl ObjectPayload for Number { /// Store/比较协议的具体载荷下转型入口,不依赖语言名义类型。 fn as_any(&self) -> &dyn Any { self } /// 错误与诊断显示的类别标签。 fn kind_name(&self) -> &'static str { "number" } /// 测试 Number 用值覆盖默认身份表示,供 str 默认回退调用。 fn repr(&self, _identity: ObjectRef) -> Result { Ok(self.0.to_string()) } /// 测试 Number 仅零为假,不使用默认对象恒真规则。 fn is_true(&self) -> Result { Ok(self.0 != 0) } /// Store::hash 调用此载荷协议,按整数值生成哈希;负数按 Rust as 转为 u64。 fn hash(&self, _identity: ObjectRef) -> Result { Ok(self.0 as u64) } /// Store 先正向后反向调用;返回 None 是未实现,Some(false) 则不再回退。 fn compare( &self, other: &dyn ObjectPayload, operation: CompareOp, ) -> Result, VmError> { // Number 只处理同类,其他载荷返回 None 允许 Store 继续分派。 let Some(other) = other.as_any().downcast_ref::() else { return Ok(None); }; // 同类 Number 每个关系都返回明确结果,false 也不是未实现。 Ok(Some(match operation { CompareOp::Eq => self.0 == other.0, CompareOp::Ne => self.0 != other.0, CompareOp::Lt => self.0 < other.0, CompareOp::Le => self.0 <= other.0, CompareOp::Gt => self.0 > other.0, CompareOp::Ge => self.0 >= other.0, })) } } /// 各生命周期测试共用分配器,保存计数器 Arc 与给定不可变边,返回一份拥有根。 fn resource(vm: &mut Vm, drops: &Arc, edges: Vec) -> ObjectRef { vm.allocate(Resource { drops: drops.clone(), edges, }) .unwrap() } #[test] /// 验证新 VM 不隐式自举名义类型或内置对象;不代表 VM 没有显式类型 API。 fn starts_empty_without_language_classes_or_a_type_system() { // 只观察初始空堆,不因查询自动初始化对象。 let vm = Vm::new().unwrap(); assert_eq!(vm.object_count(), 0); } #[test] /// 验证普通 Record 的身份表示、恒真和身份相等回退,无序比较及不可调用的错误。 fn defaults_provide_identity_representation_hash_and_truth() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 第一个独立对象/资源保留一份根,与 second/equal 比较身份或引用关系。 let first = vm.new_object().unwrap(); // 另一个分配结果有独立身份,即使内容相同也不是同对象。 let second = vm.new_object().unwrap(); assert_eq!(vm.object(first).unwrap().header().identity(), first); assert!(vm.is_same(first, first).unwrap()); assert!(!vm.is_same(first, second).unwrap()); assert!(vm.repr(first).unwrap().contains(&first.to_string())); assert_eq!(vm.repr(first).unwrap(), vm.str(first).unwrap()); assert_eq!(vm.hash(first).unwrap(), vm.hash(first).unwrap()); assert!(vm.is_true(first).unwrap()); assert!(vm.compare(first, first, CompareOp::Eq).unwrap()); assert!(vm.compare(first, second, CompareOp::Ne).unwrap()); assert_eq!( vm.compare(first, second, CompareOp::Lt), Err(VmError::UnsupportedOperation("ordering")) ); assert_eq!( vm.call(first, &[]), Err(VmError::UnsupportedOperation("call")) ); } #[test] /// 验证复制句柄不保留对象,显式 retain/release 控制资源销毁与过期访问。 fn explicit_references_control_lifetime_and_copies_are_borrowed() { // 共享 Rust 原子计数器独立于 VM 生命周期,测试可在对象销毁后读取实际析构次数。 let drops = Arc::new(AtomicUsize::new(0)); // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // resource 返回一份外部根,后续复制不新增根。 let owned = resource(&mut vm, &drops, vec![]); // 直接复制 ObjectRef,ref_count 仍为一,不能额外 release。 let borrowed = owned; assert_eq!(vm.ref_count(borrowed).unwrap(), 1); // 显式多取得一份外部根,需两次归还后才实际析构。 vm.retain(owned).unwrap(); assert_eq!(vm.ref_count(owned).unwrap(), 2); vm.release(owned).unwrap(); assert_eq!(drops.load(Ordering::SeqCst), 0); vm.release(owned).unwrap(); assert_eq!(drops.load(Ordering::SeqCst), 1); assert_eq!(vm.object_count(), 0); // 最后拥有根释放后,复制来的借用句柄必须失效,不能当作额外拥有根。 assert!(matches!(vm.object(borrowed), Err(VmError::InvalidHandle))); assert_eq!(vm.release(borrowed), Err(VmError::InvalidHandle)); } #[test] /// 验证字段独立持有内部边,不能用外部 release 归还仅借用的属性边。 fn attributes_keep_objects_alive_and_delete_releases_them() { // 共享 Rust 原子计数器独立于 VM 生命周期,测试可在对象销毁后读取实际析构次数。 let drops = Arc::new(AtomicUsize::new(0)); // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 普通 Record 字段作为子对象强边来源。 let owner = vm.new_object().unwrap(); // 子对象先有分配根,写入属性后另有内部边。 let child = resource(&mut vm, &drops, vec![]); vm.set_attr(owner, "child", Value::Object(child)).unwrap(); assert_eq!(vm.ref_count(child).unwrap(), 2); // 归还分配根后属性边继续保活,不能再当作拥有根重复 release。 vm.release(child).unwrap(); assert_eq!(vm.ref_count(child).unwrap(), 1); assert_eq!(vm.release(child), Err(VmError::NoOwnedReference)); assert_eq!(vm.get_attr(owner, "child").unwrap(), Value::Object(child)); vm.del_attr(owner, "child").unwrap(); assert_eq!(drops.load(Ordering::SeqCst), 1); assert_eq!( vm.get_attr(owner, "child"), Err(VmError::AttributeNotFound("child".into())) ); } #[test] /// 验证属性同对象重写先保留新边再归还旧边,随后改标量才释放最后强边。 fn replacing_the_same_edge_does_not_destroy_the_target() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 普通 Record 字段作为子对象强边来源。 let owner = vm.new_object().unwrap(); // 独立目标用于属性替换或多重载荷边计数。 let target = vm.new_object().unwrap(); vm.set_attr(owner, "child", Value::Object(target)).unwrap(); // 归还原根,后续存活完全依赖已建立的内部边。 vm.release(target).unwrap(); vm.set_attr(owner, "child", Value::Object(target)).unwrap(); assert_eq!(vm.ref_count(target).unwrap(), 1); vm.set_attr(owner, "child", Value::Number(7_i64.into())) .unwrap(); // 属性从对象改为数值会归还原目标最后的边,因此旧 target 句柄应失效。 assert!(matches!(vm.object(target), Err(VmError::InvalidHandle))); assert_eq!( vm.get_attr(owner, "child").unwrap(), Value::Number(7_i64.into()) ); } #[test] /// 验证宿主主动 retain 属性借用结果后,可在原父对象释放后继续使用。 fn borrowed_attribute_results_can_be_retained_by_the_host() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 父对象拥有 child 字段边,释放父根会移除该边。 let parent = vm.new_object().unwrap(); // 子对象先有分配根,写入属性后另有内部边。 let child = vm.new_object().unwrap(); vm.set_attr(parent, "child", Value::Object(child)).unwrap(); // 归还分配根后属性边继续保活,不能再当作拥有根重复 release。 vm.release(child).unwrap(); // get_attr 返回借用值,宿主若要跨父对象寿命使用须手动 retain。 let Value::Object(borrowed) = vm.get_attr(parent, "child").unwrap() else { panic!("expected an object"); }; // 取得独立宿主根后,原持有字段消失也不会使结果失效。 vm.retain(borrowed).unwrap(); vm.release(parent).unwrap(); assert_eq!(vm.ref_count(borrowed).unwrap(), 1); vm.release(borrowed).unwrap(); assert_eq!(vm.object_count(), 0); } #[test] /// 验证异堆和过期值写入失败保持旧字段,retain/release/身份比较也验证堆归属。 fn failed_foreign_and_stale_writes_preserve_the_old_attribute() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 第二个 VM 用于构造确实属于异堆的有效身份。 let mut other = Vm::new().unwrap(); // 普通 Record 字段作为子对象强边来源。 let owner = vm.new_object().unwrap(); // 句柄在所属 VM 中有效,但本 VM 不能 retain、存储或当作结果返回。 let foreign = other.new_object().unwrap(); // 制造旧身份;释放后写入必须失败而非覆盖旧值。 let stale = vm.new_object().unwrap(); // 销毁对象使句柄过期,后续不再拥有任何可归还的根。 vm.release(stale).unwrap(); vm.set_attr(owner, "value", Value::Number(1_i64.into())) .unwrap(); assert_eq!( vm.set_attr(owner, "value", Value::Object(foreign)), Err(VmError::ForeignHandle) ); assert_eq!( vm.set_attr(owner, "value", Value::Object(stale)), Err(VmError::InvalidHandle) ); assert_eq!( vm.get_attr(owner, "value").unwrap(), Value::Number(1_i64.into()) ); assert_eq!(vm.retain(foreign), Err(VmError::ForeignHandle)); assert_eq!(vm.release(foreign), Err(VmError::ForeignHandle)); assert_eq!(vm.is_same(owner, foreign), Err(VmError::ForeignHandle)); } #[test] /// 验证槽代际与堆身份共同防止旧句柄别名新对象。 fn released_handles_cannot_alias_new_objects_or_new_vms() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 释放后保留旧句柄表示,检查槽复用不会让它复活。 let old = vm.new_object().unwrap(); vm.release(old).unwrap(); // 新分配可复用槽号,但代际必须不同。 let new = vm.new_object().unwrap(); assert_ne!(old, new); assert_eq!(vm.repr(old), Err(VmError::InvalidHandle)); // 同号槽在另一堆也不是有效身份,必须报 ForeignHandle。 let other = Vm::new().unwrap(); assert!(matches!(other.object(new), Err(VmError::ForeignHandle))); } #[test] /// 验证槽向量扩容不移动独立装箱对象实体。 fn boxed_objects_keep_their_addresses_when_the_slot_vector_grows() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 保持首个对象存活,扩容前后比较其独立 Box 地址而非槽向量中的位置。 let first = vm.new_object().unwrap(); // 保存对象实体地址,不保存可能因向量扩容变化的槽地址。 let address = std::ptr::from_ref(vm.object(first).unwrap()); // 额外分配四千零九十六个对象,迫使 slots 向量扩容;所有对象在 VM 结束时清理。 for _ in 0..4096 { vm.new_object().unwrap(); } assert_eq!(std::ptr::from_ref(vm.object(first).unwrap()), address); } #[test] /// 构造长单向拥有链,释放唯一头根应通过显式工作栈完成级联回收。 fn zero_reference_chains_are_released_without_recursive_destructors() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 长链唯一保留外部根的起点。 let head = vm.new_object().unwrap(); // tail 只是借用游标,后续节点仅由前节点 next 属性持有。 let mut tail = head; // 压力设置:足够多次分配或链节点,检验稳定盒与非递归清理。 for _ in 0..4096 { // 新节点先分配根,接上链后归还该根,仅留下内部边。 let next = vm.new_object().unwrap(); vm.set_attr(tail, "next", Value::Object(next)).unwrap(); vm.release(next).unwrap(); tail = next; } // 唯一头根移除应触发整条无环链的级联释放。 vm.release(head).unwrap(); assert_eq!(vm.object_count(), 0); } #[test] /// 验证有外部根的互引环保留,最后根归还后 collect_cycles 才整环回收。 fn cycles_remain_while_rooted_and_are_collected_when_unreachable() { // 共享 Rust 原子计数器独立于 VM 生命周期,测试可在对象销毁后读取实际析构次数。 let drops = Arc::new(AtomicUsize::new(0)); // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 第一个独立对象/资源保留一份根,与 second/equal 比较身份或引用关系。 let first = resource(&mut vm, &drops, vec![]); // 另一个分配结果有独立身份,即使内容相同也不是同对象。 let second = resource(&mut vm, &drops, vec![]); vm.set_attr(first, "next", Value::Object(second)).unwrap(); vm.set_attr(second, "next", Value::Object(first)).unwrap(); vm.release(second).unwrap(); assert_eq!(vm.collect_cycles(), 0); vm.release(first).unwrap(); assert_eq!(vm.object_count(), 2); assert_eq!(vm.collect_cycles(), 2); assert_eq!(vm.collect_cycles(), 0); assert_eq!(drops.load(Ordering::SeqCst), 2); // 无根环回收后第一个旧句柄失效,不能只依赖析构计数判断槽是否仍可访问。 assert!(matches!(vm.object(first), Err(VmError::InvalidHandle))); // 同一垃圾环中的第二个对象也必须失效,避免只回收部分环。 assert!(matches!(vm.object(second), Err(VmError::InvalidHandle))); } #[test] /// 验证回收垃圾环时只扣除其指向幸存者的边,不销毁仍有根的目标。 fn cycle_collection_preserves_rooted_targets_and_updates_counts() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 幸存者保留外部根,即使垃圾环另持有它也不能被 GC 误删。 let live = vm.new_object().unwrap(); // 自环使普通强计数无法归零;无外部根后交给显式环回收或 VM Drop。 let cycle = vm.new_object().unwrap(); vm.set_attr(cycle, "self", Value::Object(cycle)).unwrap(); vm.set_attr(cycle, "live", Value::Object(live)).unwrap(); vm.release(cycle).unwrap(); assert_eq!(vm.ref_count(live).unwrap(), 2); assert_eq!(vm.collect_cycles(), 1); assert_eq!(vm.ref_count(live).unwrap(), 1); vm.release(live).unwrap(); assert_eq!(vm.object_count(), 0); } #[test] /// 验证 trace 两次报告同一目标就持有两条强边,释放载荷逐边归还。 fn payload_edges_are_owned_and_duplicate_edges_are_counted() { // 共享 Rust 原子计数器独立于 VM 生命周期,测试可在对象销毁后读取实际析构次数。 let drops = Arc::new(AtomicUsize::new(0)); // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 独立目标用于属性替换或多重载荷边计数。 let target = vm.new_object().unwrap(); // 载荷同时拥有同一目标两次,总强计数应包含两条内部边。 let holder = resource(&mut vm, &drops, vec![target, target]); assert_eq!(vm.ref_count(target).unwrap(), 3); // 归还原根,后续存活完全依赖已建立的内部边。 vm.release(target).unwrap(); vm.release(holder).unwrap(); assert_eq!(vm.object_count(), 0); assert_eq!(drops.load(Ordering::SeqCst), 1); } #[test] /// 验证混合载荷边与属性边的环能被同一次图遍历回收。 fn payload_edges_participate_in_cycle_tracing() { // 共享 Rust 原子计数器独立于 VM 生命周期,测试可在对象销毁后读取实际析构次数。 let drops = Arc::new(AtomicUsize::new(0)); // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 普通属性对象作为混合环的一端。 let record = vm.new_object().unwrap(); // Resource 载荷拥有 record,record 的字段再反向拥有 Resource。 let native = resource(&mut vm, &drops, vec![record]); vm.set_attr(record, "native", Value::Object(native)) .unwrap(); vm.release(record).unwrap(); vm.release(native).unwrap(); assert_eq!(vm.collect_cycles(), 2); assert_eq!(drops.load(Ordering::SeqCst), 1); } #[test] /// 验证分配前完整边预检,后部异堆边失败不能部分保留前部有效边。 fn invalid_payload_edges_abort_allocation_without_retaining_valid_edges() { // 共享 Rust 原子计数器独立于 VM 生命周期,测试可在对象销毁后读取实际析构次数。 let drops = Arc::new(AtomicUsize::new(0)); // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 第二个 VM 用于构造确实属于异堆的有效身份。 let mut other = Vm::new().unwrap(); // 合法本堆边放在错误边前面,验证失败不部分提交其计数。 let local = vm.new_object().unwrap(); // 句柄在所属 VM 中有效,但本 VM 不能 retain、存储或当作结果返回。 let foreign = other.new_object().unwrap(); assert_eq!( vm.allocate(Resource { drops: drops.clone(), edges: vec![local, foreign], }), Err(VmError::ForeignHandle) ); assert_eq!(vm.ref_count(local).unwrap(), 1); assert_eq!(vm.object_count(), 1); assert_eq!(drops.load(Ordering::SeqCst), 1); } #[test] /// 验证 Rust 载荷可覆盖显示、真值、哈希和比较,无需附着语言类型。 fn rust_payload_operations_override_defaults_without_language_types() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 测试用 Number(0) 自定义真值为假,不是内置 EndorNumberObject。 let zero = vm.allocate(Number(0)).unwrap(); // 第一个独立对象/资源保留一份根,与 second/equal 比较身份或引用关系。 let first = vm.allocate(Number(42)).unwrap(); // 同值不同身份的 Number,必须内容相等且哈希一致。 let equal = vm.allocate(Number(42)).unwrap(); assert!(!vm.is_same(first, equal).unwrap()); assert!(vm.compare(first, equal, CompareOp::Eq).unwrap()); assert!(vm.compare(zero, first, CompareOp::Lt).unwrap()); assert_eq!(vm.hash(first).unwrap(), vm.hash(equal).unwrap()); assert_eq!(vm.str(first).unwrap(), "42"); assert!(!vm.is_true(zero).unwrap()); assert!(vm.is_true(first).unwrap()); } #[derive(Debug)] /// 反向比较场景的右操作数:只对 Gt 返回 true,用于识别左 Lt 是否正确交换方向。 struct ReflectedOrdering; impl ObjectPayload for ReflectedOrdering { /// Store/比较协议的具体载荷下转型入口,不依赖语言名义类型。 fn as_any(&self) -> &dyn Any { self } /// 错误与诊断显示的类别标签。 fn kind_name(&self) -> &'static str { "reflected-ordering" } /// Store 先正向后反向调用;返回 None 是未实现,Some(false) 则不再回退。 fn compare( &self, _other: &dyn ObjectPayload, operation: CompareOp, ) -> Result, VmError> { Ok(Some(operation == CompareOp::Gt)) } /// Store::hash 调用时明确返回不支持,不能退回默认对象身份哈希。 fn hash(&self, _identity: ObjectRef) -> Result { Err(VmError::UnsupportedOperation("hash")) } } #[test] /// 验证左侧未实现时把 Lt 反向为右侧 Gt,显式不支持 hash 原样传播。 fn reflected_operations_and_explicitly_unsupported_hashing_are_respected() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 左侧 Record 不实现有序比较,Store 应尝试右侧反向协议。 let left = vm.allocate(Record).unwrap(); // 右侧专门只对 Gt 返回真,证明左 Lt 正确反射。 let right = vm.allocate(ReflectedOrdering).unwrap(); assert!(vm.compare(left, right, CompareOp::Lt).unwrap()); assert_eq!(vm.hash(right), Err(VmError::UnsupportedOperation("hash"))); } /// 简单旧式原生回调,仅接受两个直接整数并检查 i64 加法溢出;不是 Endor 通用算术实现。 fn add(arguments: &[Value]) -> Result { // 先按参数形状匹配两个直接 Number,其他输入走回调参数错误。 match arguments { [Value::Number(left), Value::Number(right)] => { // as_i64 同时成功才允许此测试回调运算,不接受大整数或小数隐式转换。 let (left, right) = left .as_i64() .zip(right.as_i64()) .ok_or(VmError::InvalidArgument("expected two integers"))?; left.checked_add(right) .map(|value| Value::Number(value.into())) .ok_or(VmError::InvalidArgument("integer overflow")) } _ => Err(VmError::InvalidArgument("expected two integers")), } } #[test] /// 验证原生分派、回调错误、禁止动态属性,以及异堆参数在回调前拒绝。 fn native_calls_dispatch_validate_arguments_and_propagate_errors() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 把 Rust add 包装为旧式原生函数,由 VM 负责参数/结果身份验证。 let function = vm.allocate(NativeFunction::new("add", add)).unwrap(); assert_eq!(vm.repr(function).unwrap(), ""); assert_eq!( vm.call( function, &[Value::Number(2_i64.into()), Value::Number(3_i64.into())] ) .unwrap(), Value::Number(5_i64.into()) ); assert_eq!( vm.call(function, &[]), Err(VmError::InvalidArgument("expected two integers")) ); assert_eq!( vm.call( function, &[Value::Number(i64::MAX.into()), Value::Number(1_i64.into())] ), Err(VmError::InvalidArgument("integer overflow")) ); assert_eq!( vm.set_attr(function, "x", Value::Nil), Err(VmError::UnsupportedOperation("attributes")) ); // 第二个 VM 用于构造确实属于异堆的有效身份。 let mut other = Vm::new().unwrap(); // 句柄在所属 VM 中有效,但本 VM 不能 retain、存储或当作结果返回。 let foreign = other.new_object().unwrap(); assert_eq!( vm.call(function, &[Value::Object(foreign)]), Err(VmError::ForeignHandle) ); } #[derive(Debug)] /// 非法结果场景的故障注入载荷:只保存未 trace 的借用身份,调用时故意返还它让 VM 拒绝。 struct ReturnsObject(ObjectRef); impl ObjectPayload for ReturnsObject { /// Store/比较协议的具体载荷下转型入口,不依赖语言名义类型。 fn as_any(&self) -> &dyn Any { self } /// 错误与诊断显示的类别标签。 fn kind_name(&self) -> &'static str { "invalid-return" } /// 故意返回未经验证的原始对象句柄,测试 VM 能否在结果移交前阻止非法身份。 fn call(&self, _arguments: &[Value]) -> Result { Ok(Value::Object(self.0)) } } #[test] /// 验证原生结果仍需堆归属/存活检查,宿主回调不能借返回通道塞入非法句柄。 fn native_results_cannot_smuggle_foreign_or_dead_handles_into_the_vm() { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 第二个 VM 用于构造确实属于异堆的有效身份。 let mut other = Vm::new().unwrap(); // 句柄在所属 VM 中有效,但本 VM 不能 retain、存储或当作结果返回。 let foreign = other.new_object().unwrap(); // 故意不 trace 持有的句柄,因为这里测试的是非法借用结果而非合法拥有边。 let invalid_return = vm.allocate(ReturnsObject(foreign)).unwrap(); assert_eq!(vm.call(invalid_return, &[]), Err(VmError::ForeignHandle)); // 本堆对象释放后制造过期返回值,不与异堆错误混淆。 let dead = vm.new_object().unwrap(); // 之后只剩失效的借用身份,回调返回不能把它重新保活。 vm.release(dead).unwrap(); // 故意不 trace 持有的句柄,因为这里测试的是非法借用结果而非合法拥有边。 let invalid_return = vm.allocate(ReturnsObject(dead)).unwrap(); assert_eq!(vm.call(invalid_return, &[]), Err(VmError::InvalidHandle)); } #[test] /// 验证离开 VM 作用域最终销毁仍有根对象及尚未显式 GC 的自环。 fn dropping_the_vm_cleans_up_rooted_objects_and_uncollected_cycles() { // 共享 Rust 原子计数器独立于 VM 生命周期,测试可在对象销毁后读取实际析构次数。 let drops = Arc::new(AtomicUsize::new(0)); { // 建立独立堆,句柄不能跨测试/VM 使用。 let mut vm = Vm::new().unwrap(); // 离开局部 VM 作用域时仍有一份根,VM Drop 也必须释放宿主资源。 let rooted = resource(&mut vm, &drops, vec![]); // 自环使普通强计数无法归零;无外部根后交给显式环回收或 VM Drop。 let cycle = resource(&mut vm, &drops, vec![]); vm.set_attr(cycle, "self", Value::Object(cycle)).unwrap(); vm.release(cycle).unwrap(); assert_eq!(vm.ref_count(rooted).unwrap(), 1); } assert_eq!(drops.load(Ordering::SeqCst), 2); }