//! 独立 ObjectStore 回归场景,由 Rust 测试框架调用;不需要 VM 执行指令。 //! 场景覆盖根/边所有权、失败原子性与读取作用域。 use std::sync::Arc; use objects::{ CodeLayout, EndorCode, EndorCodeObject, EndorFunctionObject, Instruction, ObjectStore, Opcode, Value, VmError, }; #[test] /// 验证容器替换保持对象地址、跨堆边被拒绝且失去外部根的互引环可回收。 fn standalone_storage_preserves_identity_validates_edges_and_collects_cycles() { // 每个测试建立独立存储,避免对象/作用域状态跨场景污染。 let mut store = ObjectStore::new().unwrap(); // 独立堆用于证明句柄归属不能仅以槽号判断。 let mut other = ObjectStore::new().unwrap(); // 字典与下面 Cell 各自有一份外部根,Cell 另外持有字典强边。 let dictionary = store.new_dict().unwrap(); // Cell 初始拥有字典内部边,后续反向写入形成环。 let cell = store.new_cell(Some(Value::Object(dictionary))).unwrap(); // 异堆对象保持自身外部根,不应被本堆写入接管。 let foreign = other.new_object().unwrap(); // 保存对象实体地址,验证替换载荷不会移动外层稳定 Box。 let address = store.object(dictionary).unwrap() as *const _; // 字典键使用直接文本,不产生托管强边。 let key = Value::Text("cell".into()); store .dict_set(dictionary, key.clone(), Value::Object(cell)) .unwrap(); assert_eq!(store.object(dictionary).unwrap() as *const _, address); assert_eq!( store.dict_set(dictionary, key.clone(), Value::Object(foreign)), Err(VmError::ForeignHandle) ); assert_eq!( store.dict_get(dictionary, &key).unwrap(), Some(Value::Object(cell)) ); assert_eq!(store.ref_count(cell).unwrap(), 2); store.release(dictionary).unwrap(); store.release(cell).unwrap(); assert_eq!(store.collect_cycles(), 2); assert_eq!(store.object_count(), 0); // 独立 Store 释放完拥有图后,原 Cell 借用句柄也应失效。 assert!(matches!(store.object(cell), Err(VmError::InvalidHandle))); other.release(foreign).unwrap(); } #[test] /// 验证无效函数构造不分配对象,两个函数共享同一执行数据并通过代码边保留常量。 fn standalone_function_factories_validate_and_own_shared_executable_data() { // 每个测试建立独立存储,避免对象/作用域状态跨场景污染。 let mut store = ObjectStore::new().unwrap(); // 字符串最初由测试根持有,随后代码常量也建立内部边。 let constant = store.new_string("kept by code").unwrap(); // 函数全局空间在两个函数载荷中各持有一条内部边。 let globals = store.new_dict().unwrap(); // 最小已验证代码只装载常量再返回,不在本测试执行。 let code = EndorCode::from_instructions( &[ Instruction::new(Opcode::LoadConst, 0), Instruction::simple(Opcode::ReturnValue), ], vec![Value::Object(constant)], CodeLayout::default(), ) .unwrap(); // 分配代码载荷后常量由其 trace 强边持有。 let code = store.new_code(code).unwrap(); // 记录失败构造前对象数,断言失败不得泄漏半成品。 let before = store.object_count(); assert_eq!( store.new_function("bad", code, globals, vec![Value::Nil], vec![]), Err(VmError::InvalidArgument( "function defaults or closure do not match code" )) ); assert_eq!(store.object_count(), before); assert_eq!(store.ref_count(code).unwrap(), 1); // 第一个函数持有代码和全局字典,不复制代码对象。 let first = store .new_function("first", code, globals, vec![], vec![]) .unwrap(); // 第二个函数独立持有相同代码/全局边,供释放顺序验证。 let second = store .new_function("second", code, globals, vec![], vec![]) .unwrap(); // 借用第一个函数记录的代码句柄,不新增根。 let first_code = store .payload::(first, "function") .unwrap() .code(); // 借用第二个函数代码句柄,与第一函数应指向相同执行数据。 let second_code = store .payload::(second, "function") .unwrap() .code(); // 取得 Rust Arc 只用于指针共享验证,它不替代托管代码根。 let first_executable = store .payload::(first_code, "code") .unwrap() .shared_code(); // 第二份 Arc 与第一份应共享同一 EndorCode 分配。 let second_executable = store .payload::(second_code, "code") .unwrap() .shared_code(); assert!(Arc::ptr_eq(&first_executable, &second_executable)); // 先释放 Rust 共享引用,再测试托管函数/代码边生命周期。 drop(first_executable); drop(second_executable); // 归还测试拥有根后,常量仍应通过两个函数到代码的图存活。 store.release(constant).unwrap(); store.release(code).unwrap(); store.release(globals).unwrap(); assert_eq!(store.str(constant).unwrap(), "kept by code"); // 移除一函数仅减一条代码边,另一函数仍保留代码和常量。 store.release(first).unwrap(); assert_eq!(store.ref_count(code).unwrap(), 1); // 最后一个函数释放后,代码/全局/常量无根链一起回收。 store.release(second).unwrap(); assert_eq!(store.object_count(), 0); } #[test] /// 验证已有对象受保护、新结果可写、嵌套层会保护外层新对象及退出后恢复权限。 fn read_only_scopes_block_existing_container_and_type_writes_but_allow_fresh_results() { // 每个测试建立独立存储,避免对象/作用域状态跨场景污染。 let mut store = objects::ObjectStore::new().unwrap(); // 显式自举两个类型根,清理时需归还它们并收集类型图环。 let roots = store.bootstrap_types().unwrap(); // 此记录在进入只读前分配,属性和类型附着都应被写屏障保护。 let record = store.new_object().unwrap(); // 已有字典受同一只读屏障保护,不只保护普通属性。 let dict = store.new_dict().unwrap(); // 进入只读前创建的 Cell 同样属于受保护堆对象,不能靠内部值更新绕过写屏障。 let cell = store .new_cell(Some(objects::Value::Number(1_i64.into()))) .unwrap(); // 进入时快照已有对象,之后分配的 fresh 不在本层保护集合。 let scope = store.begin_read_only(); assert!( store .set_attr(record, "value", objects::Value::Nil) .is_err() ); assert!(store.attach_type(record, roots.object).is_err()); assert!( store .dict_set( dict, objects::Value::Text("key".into()), objects::Value::Nil ) .is_err() ); assert!( store .cell_set(cell, objects::Value::Number(2_i64.into())) .is_err() ); assert_eq!( store.cell_get(cell).unwrap(), objects::Value::Number(1_i64.into()) ); // 在只读调用内分配的新结果可初始化属性。 let fresh = store.new_object().unwrap(); store.set_attr(fresh, "value", objects::Value::Nil).unwrap(); // Record 不原地赋值,只读引用重绑定可以返回 false 而不修改旧对象。 assert!(!store.assign_from(record, fresh).unwrap()); // 内层重新快照所有存活对象,fresh 此时也受保护;结束必须按 LIFO。 let nested = store.begin_read_only(); assert!( store .set_attr(fresh, "value", objects::Value::Bool(true)) .is_err() ); // 退出内层后 fresh 又不受外层快照保护。 store.end_read_only(nested).unwrap(); store .set_attr(fresh, "value", objects::Value::Bool(true)) .unwrap(); // 结束外层只读同时取得借用读取集合,Cell 读取应被记录。 let reads = store.end_read_only(scope).unwrap(); assert!(reads.contains(&cell)); store .set_attr(record, "value", objects::Value::Nil) .unwrap(); // 逐项归还测试拥有根;类型/实例内部边由 Heap 清理,不手动归还。 for object in [record, dict, cell, fresh, roots.object, roots.type_type] { store.release(object).unwrap(); } // 显式回收自举类型图中的环,最后验证堆为空。 store.collect_cycles(); assert_eq!(store.object_count(), 0); } #[test] /// 验证子区间重复读取独立记录、观察传播到外层,错误结束顺序不损坏状态。 fn read_tracking_regions_repeat_shared_reads_and_preserve_enclosing_dependencies() { // 每个测试建立独立存储,避免对象/作用域状态跨场景污染。 let mut store = ObjectStore::new().unwrap(); // 两个独立对象用于验证读取传播,不建立彼此引用。 let first = store.new_object().unwrap(); // 第二对象仅在内层首次读取,外层集合也必须包含它。 let second = store.new_object().unwrap(); assert!(store.begin_read_tracking().is_err()); // 进入时把 first、second 列为已有对象,后面的读取才会成为本层依赖。 let scope = store.begin_read_only(); // 外层区间开始后,其内所有读取都要包含在最终结果中。 let outer = store.begin_read_tracking().unwrap(); store.object(first).unwrap(); // 子区间嵌套不另建对象保护快照;其读取同时传播给 outer,结束仍须按 LIFO。 let nested = store.begin_read_tracking().unwrap(); store.object(first).unwrap(); store.object(second).unwrap(); // 内层未结束时不能弹出外层,失败后应仍能正确结束两层。 assert!(store.end_read_tracking(outer).is_err()); assert_eq!( store.end_read_tracking(nested).unwrap(), vec![first, second] ); assert_eq!(store.end_read_tracking(outer).unwrap(), vec![first, second]); // 新子区间重新读取同一对象,也必须得到自己的观察记录。 let repeated = store.begin_read_tracking().unwrap(); store.object(first).unwrap(); assert_eq!(store.end_read_tracking(repeated).unwrap(), vec![first]); // 模拟回调失败:结束整次调用时丢弃尚未正常结束的区间。 store.begin_read_tracking().unwrap(); assert_eq!(store.end_read_only(scope).unwrap(), vec![first, second]); // 整个调用结束后未完成区间已丢弃,不能再拿旧令牌结束。 assert!(store.end_read_tracking(1).is_err()); assert!(!store.is_read_only()); // first 没有内部拥有边,归还唯一外部根后可立即回收;借用读取记录不会保活它。 store.release(first).unwrap(); // second 同样独立归还唯一根;本场景没有函数、代码对象或对象互引环。 store.release(second).unwrap(); }