diff --git a/crates/objects/src/lib.rs b/crates/objects/src/lib.rs index 270ab2e..124f6f7 100644 --- a/crates/objects/src/lib.rs +++ b/crates/objects/src/lib.rs @@ -1,6 +1,6 @@ -//! Core Endor objects, values, validated executable data and object storage. -//! This crate has no VM evaluator or external dependencies. Handles, reference -//! accounting and mutable payload replacement remain encapsulated here. +//! Endor 对象、数值、已验证代码和存储的公共 API;compiler/core 和宿主都从这里导入。 +//! 不包含 VM 指令求值器;数值精确运算使用 num_* 依赖,句柄/计数/载荷修改仍封装在本 crate。 +//! 下方示例由文档测试调用:创建拥有根、读写属性,最后 release,不把复制句柄误当新增根。 //! //! ``` //! use objects::{ObjectStore, Value}; @@ -13,41 +13,64 @@ //! # Ok::<(), objects::VmError>(()) //! ``` +// 内部内置载荷:字符串、字典、函数、闭包槽和异常。 mod builtins; +// 内部变更通知结构,消费者据此失效缓存。 mod changes; +// 代码布局和构造校验,不执行代码。 mod code; +// 各层共享错误,避免对象层依赖 VM。 mod error; +// 私有堆:代际身份、根/边计数、只读追踪和回收。 mod heap; +// 指令协议及字节编解码,供编译器和执行器共用。 mod instruction; +// 宿主回调契约、上下文和结果根移交。 mod native; +// 无损数值表示和受预算约束的精确运算。 mod number; +// 直接值、载荷协议及公共对象实体。 mod object; +// 受控对象操作门面,宿主可独立于 VM 使用。 mod store; +// 字典/Cell/函数等内置载荷的 Store 扩展。 mod store_builtins; +// 名义类型、能力、字段注解及子模块中的契约/效果分析。 mod types; +// 向宿主公开内置载荷,分配和修改仍经 Store。 pub use builtins::{ EndorBaseExceptionObject, EndorCellObject, EndorDictObject, EndorFunctionObject, EndorStringObject, EndorTracebackEntry, }; +// 向缓存消费者公开借用变更通知,不提供对象拥有根。 pub use changes::{ChangeBatch, ChangeKey, ObjectChange}; +// 编译器构造与 VM 共享执行数据需要的代码 API。 pub use code::{CodeLayout, EndorCode, EndorCodeObject, SourceSignature}; +// 调用者结构化匹配的统一错误。 pub use error::VmError; +// 只公开句柄和读取观察结果,不导出可变 Heap。 pub use heap::{ObjectRef, ReadAccess, ReadDependency, ReadSet, TrackedReads, WeakObjectRef}; +// 编译器/宿主组装指令,执行器读取解码结果。 pub use instruction::{DecodedInstruction, Instruction, Opcode}; +// 宿主实现原生函数并声明参数、返回值和效果。 pub use native::{ CallArity, CallEffect, CallSignature, CallType, ContextNativeFunction, NativeCallable, NativeContext, NativeResult, }; +// 编译器字面量、VM 算术和宿主显式转换共用数值 API。 pub use number::{ ArithmeticOp, EndorDecimal, EndorInteger, EndorNumber, EndorNumberObject, EndorRational, EndorReal, }; +// 宿主实现载荷及等待协议,保留旧原生函数别名兼容。 pub use object::{ AwaitPoll, CompareOp, EndorNativeCall, EndorNativeFunctionObject, EndorObject, NativeCall, NativeFunction, ObjectHeader, ObjectPayload, Record, Value, }; +// 独立存储的主要入口,VM 也持有同一门面。 pub use store::ObjectStore; +// 宿主注册类型/能力,执行器检查构造和调用契约。 pub use types::{ AnnotationType, ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes, TypeConstructor, TypedMethodRequirement, diff --git a/crates/objects/tests/arithmetic.rs b/crates/objects/tests/arithmetic.rs index 3fe55f9..91db0c3 100644 --- a/crates/objects/tests/arithmetic.rs +++ b/crates/objects/tests/arithmetic.rs @@ -1,20 +1,27 @@ +//! number.rs 的公开 API 回归:比较数学值与存储表示分开,且构造/运算使用不同预算。 use objects::{ArithmeticOp, CompareOp, EndorNumber, EndorReal}; +/// 测试辅助:用 numeric_compare 断言数学相等,不能用 Rust Eq 比较不同表示。 fn equal(a: &EndorNumber, b: &EndorNumber) { assert!(a.numeric_compare(b, CompareOp::Eq).unwrap()); } #[test] +/// 测试框架调用:验证精确跨表示求和、整数溢出提升、有理数等价和大整数混合浮点比较。 fn exact_real_arithmetic_keeps_large_values_and_cross_representation_equivalence() { + // 十进制保留尾零,后续与分数求和应保持精确数学值。 let tenth = EndorNumber::decimal("0.10").unwrap(); + // 分数构造不要求约分,算术结果仍应等价于精确 0.3。 let fifth = EndorNumber::rational("2", "10").unwrap(); equal( &tenth.arithmetic(&fifth, ArithmeticOp::Add).unwrap(), &EndorNumber::decimal("0.3").unwrap(), ); + // 机器最大整数加一必须提升到大整数,不能溢出或丢精度。 let large = EndorNumber::integer("9223372036854775807").unwrap(); equal( &large.arithmetic(&1_i64.into(), ArithmeticOp::Add).unwrap(), &EndorNumber::integer("9223372036854775808").unwrap(), ); + // 最小负整数转换需单独处理其超出正 i64 范围的幅值。 let min = EndorNumber::integer("-9223372036854775808").unwrap(); assert_eq!(min.as_i64(), Some(i64::MIN)); equal( @@ -33,12 +40,15 @@ fn exact_real_arithmetic_keeps_large_values_and_cross_representation_equivalence ); } #[test] +/// 测试框架调用:验证 IEEE 符号零/NaN、零除/非有限结果拒绝,以及巨大指数可存不可运算。 fn ieee_signed_zero_nan_and_operation_limits_are_explicit() { + // 浮点取负翻符号位,+0 应变 -0 而不是规范成整数零。 let negative = EndorNumber::binary_float(0.).negated().unwrap(); assert_eq!( negative.as_real(), Some(&EndorReal::BinaryFloat((-0.0_f64).to_bits())) ); + // 同一 NaN 数学比较也不等于自身,Ne 必须为真。 let nan = EndorNumber::binary_float(f64::NAN); assert!(!nan.numeric_compare(&nan, CompareOp::Eq).unwrap()); assert!(nan.numeric_compare(&nan, CompareOp::Ne).unwrap()); @@ -52,6 +62,7 @@ fn ieee_signed_zero_nan_and_operation_limits_are_explicit() { .arithmetic(&2_i64.into(), ArithmeticOp::Multiply) .is_err() ); + // 极长指数语法合法且可原样显示,但实际运算要触发预算保护。 let huge = EndorNumber::decimal("1e99999999999999999").unwrap(); assert_eq!(huge.to_string(), "1e99999999999999999"); assert!(huge.arithmetic(&1_i64.into(), ArithmeticOp::Add).is_err()); diff --git a/crates/objects/tests/store.rs b/crates/objects/tests/store.rs index 7486d00..7b57fda 100644 --- a/crates/objects/tests/store.rs +++ b/crates/objects/tests/store.rs @@ -1,3 +1,5 @@ +//! 独立 ObjectStore 回归场景,由 Rust 测试框架调用;不需要 VM 执行指令。 +//! 场景覆盖根/边所有权、失败原子性与读取作用域。 use std::sync::Arc; use objects::{ @@ -6,13 +8,21 @@ use objects::{ }; #[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 @@ -37,10 +47,15 @@ fn standalone_storage_preserves_identity_validates_edges_and_collects_cycles() { } #[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), @@ -50,7 +65,9 @@ fn standalone_function_factories_validate_and_own_shared_executable_data() { 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![]), @@ -60,50 +77,66 @@ fn standalone_function_factories_validate_and_own_shared_executable_data() { ); 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(); let cell = store .new_cell(Some(objects::Value::Number(1_i64.into()))) .unwrap(); + // 进入时快照已有对象,之后分配的 fresh 不在本层保护集合。 let scope = store.begin_read_only(); assert!( store @@ -129,57 +162,76 @@ fn read_only_scopes_block_existing_container_and_type_writes_but_allow_fresh_res 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()); + // 嵌套层快照/区间只影响自身,结束必须按 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()); + // 进入时快照已有对象,之后分配的 fresh 不在本层保护集合。 let scope = store.begin_read_only(); + // 外层区间开始后,其内所有读取都要包含在最终结果中。 let outer = store.begin_read_tracking().unwrap(); store.object(first).unwrap(); + // 嵌套层快照/区间只影响自身,结束必须按 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]); - // Ending the call discards an unfinished region, as on callback failure. + // 模拟回调失败:结束整次调用时丢弃尚未正常结束的区间。 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()); + // 移除一函数仅减一条代码边,另一函数仍保留代码和常量。 store.release(first).unwrap(); + // 最后一个函数释放后,代码/全局/常量无根链一起回收。 store.release(second).unwrap(); }