docs(objects): explain exports and storage arithmetic regressions
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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::<EndorFunctionObject>(first, "function")
|
||||
.unwrap()
|
||||
.code();
|
||||
// 借用第二个函数代码句柄,与第一函数应指向相同执行数据。
|
||||
let second_code = store
|
||||
.payload::<EndorFunctionObject>(second, "function")
|
||||
.unwrap()
|
||||
.code();
|
||||
// 取得 Rust Arc 只用于指针共享验证,它不替代托管代码根。
|
||||
let first_executable = store
|
||||
.payload::<EndorCodeObject>(first_code, "code")
|
||||
.unwrap()
|
||||
.shared_code();
|
||||
// 第二份 Arc 与第一份应共享同一 EndorCode 分配。
|
||||
let second_executable = store
|
||||
.payload::<EndorCodeObject>(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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user