Files
endorphin-aster-vm/crates/objects/tests/attribute_storage.rs
T

323 lines
15 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Rust 测试框架调用的属性存储回归,不执行源码指令。
//! Proxy 让字段存储与名义身份分开;ReflectiveType 触发类型查询对自身属性的重入。
//! 断言覆盖托管边生命周期、失败不修改版本、能力方法不得被实例属性遮蔽。
use objects::{ObjectPayload, ObjectRef, ObjectStore, TypeConstructor, Value, VmError};
#[derive(Debug)]
/// 一个拥有后端存储引用的门面,type_target 保持默认 None。
struct Proxy(ObjectRef);
impl ObjectPayload for Proxy {
/// Store 下转型测试载荷,不进行语言类型判定。
fn as_any(&self) -> &dyn std::any::Any {
self
}
/// 测试错误/诊断使用的类别标签。
fn kind_name(&self) -> &'static str {
"proxy"
}
/// Heap 分配/替换时记录一条拥有边,确保被代理存储或自引用 Cell 存活。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
visit(self.0);
}
/// Store::attribute_path 追踪实际字段存储,不改变代理对象头部类型。
fn attribute_target(&self, _: &ObjectStore) -> Result<Option<ObjectRef>, VmError> {
Ok(Some(self.0))
}
}
#[test]
/// 验证代理改指新存储后,名义类型不变、旧值释放、读写删除转到新终点。
fn storage_retargeting_preserves_type_identity_and_releases_old_state() {
// 建立独立堆隔离场景,所有分配根在测试末尾明确归还。
let mut store = ObjectStore::new().unwrap();
// 自举 Object/Type,各有一份外部根;其图内自环需最终 collect_cycles。
let roots = store.bootstrap_types().unwrap();
// 给代理/反射实例附着真实名义类型,字段转发不应替换它。
let class = store
.new_type("Proxy", roots.object, TypeConstructor::Record)
.unwrap();
// 普通 Record 作为无类型后端存储,只有代理持有时也须存活。
let storage = store.new_object().unwrap();
// 旧属性值先由测试根持有,之后转由字段强边持有。
let old_value = store.new_object().unwrap();
// 弱句柄不持有旧值,替换存储后可观测其真正释放。
let weak_value = store.downgrade(old_value).unwrap();
store
.set_attr(storage, "value", Value::Object(old_value))
.unwrap();
// 归还外部根,留下 storage.value 一条强边。
store.release(old_value).unwrap();
// Proxy 的 trace 持有后端,字段实际写入后端但类型留在 Proxy 头部。
let proxy = store.allocate(Proxy(storage)).unwrap();
store.attach_type(proxy, class).unwrap();
// 归还后端外部根后仍由 Proxy 的载荷边维持存活。
store.release(storage).unwrap();
// 确认路径同时包含门面和终点,不只返回头部类型对象。
assert_eq!(store.attribute_path(proxy).unwrap(), vec![proxy, storage]);
// 保留 type 属性查询代理自身名义类型,不跟随后端身份变化。
assert_eq!(store.get_attr(proxy, "type").unwrap(), Value::Object(class));
assert_eq!(
store.get_attr(proxy, "value").unwrap(),
Value::Object(old_value)
);
// 新后端先由根持有,替换成功后 Proxy 自己持有一条边。
let next = store.new_object().unwrap();
// 受控替换重新登记边,旧后端及其唯一持有的属性值应被级联释放。
store.replace_payload(proxy, Proxy(next)).unwrap();
// 归还新后端根,后续访问证明其仍由 Proxy 强边保留。
store.release(next).unwrap();
// 旧值没有残余根,弱升级必须返回 None。
assert!(store.upgrade(weak_value).unwrap().is_none());
store
.set_attr(proxy, "value", Value::Number(9_i64.into()))
.unwrap();
assert_eq!(
store.get_attr(next, "value").unwrap(),
Value::Number(9_i64.into())
);
// 通过代理删除应作用于新后端,不只是门面自己的属性表。
store.del_attr(proxy, "value").unwrap();
assert!(store.get_attr(next, "value").is_err());
// 逐项释放测试拥有根,不手动 release 内部边,避免重复归还。
for object in [proxy, class, roots.object, roots.type_type] {
store.release(object).unwrap();
}
// 回收类型自举环及其他已无外部根的环,最终堆应为空。
store.collect_cycles();
assert_eq!(store.object_count(), 0);
}
#[test]
/// 验证属性转发环和异堆身份被拒绝,失败不产生新的变更版本。
fn cyclic_and_foreign_attribute_storage_fail_without_mutating_fields() {
// 建立独立堆隔离场景,所有分配根在测试末尾明确归还。
let mut store = ObjectStore::new().unwrap();
// 普通 Record 作为无类型后端存储,只有代理持有时也须存活。
let storage = store.new_object().unwrap();
// Proxy 的 trace 持有后端,字段实际写入后端但类型留在 Proxy 头部。
let proxy = store.allocate(Proxy(storage)).unwrap();
// 注入合法托管自环但非法属性转发环;属性路径必须检测,不无限循环。
store.replace_payload(proxy, Proxy(proxy)).unwrap();
// 记录失败读写前版本,路径检查失败不得修改对象。
let version = store.change_version(proxy).unwrap();
assert!(store.get_attr(proxy, "field").is_err());
assert!(store.set_attr(proxy, "field", Value::Nil).is_err());
assert_eq!(store.change_version(proxy).unwrap(), version);
// 异堆即使槽号相同也必须报 ForeignHandle,不当成普通缺字段。
let other = ObjectStore::new().unwrap();
assert_eq!(other.attribute_path(proxy), Err(VmError::ForeignHandle));
// 归还后端外部根后仍由 Proxy 的载荷边维持存活。
store.release(storage).unwrap();
store.release(proxy).unwrap();
// 外部根释放后只有 Proxy 自环残留,显式回收恰好一个对象。
assert_eq!(store.collect_cycles(), 1);
}
#[derive(Debug)]
/// 拥有一个 Cell,type_target 读 Cell 指回自己,测试递归属性查询。
struct ReflectiveType(ObjectRef);
impl ObjectPayload for ReflectiveType {
/// Store 下转型测试载荷,不进行语言类型判定。
fn as_any(&self) -> &dyn std::any::Any {
self
}
/// 测试错误/诊断使用的类别标签。
fn kind_name(&self) -> &'static str {
"reflective-type"
}
/// 反射对象自行存字段,允许 selector 查询从公共属性表读取。
fn allows_attributes(&self) -> bool {
true
}
/// Heap 分配/替换时记录一条拥有边,确保被代理存储或自引用 Cell 存活。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
visit(self.0);
}
/// Store::type_of 调用时通过 Cell 找自己并读 selector,专门制造属性检查重入。
fn type_target(&self, store: &ObjectStore) -> Result<Option<ObjectRef>, VmError> {
// Cell 初始 Nil 时不重入,安装自引用后读自身 selector 触发保护守卫路径。
if let Value::Object(receiver) = store.cell_get(self.0)? {
store.get_attr(receiver, "selector")?;
}
Ok(None)
}
}
#[derive(Debug)]
/// 无捕获且不修改存储的能力方法测试实现;完整签名供声明/调用门禁读取。
struct TypedCheck;
impl objects::NativeCallable for TypedCheck {
/// 为 check 声明显式 self、Number 返回和 ReadOnly 效果,供能力契约核对。
fn signature(&self) -> Option<objects::CallSignature> {
Some(objects::CallSignature::exact(
vec![objects::AnnotationType::Any],
objects::AnnotationType::Number,
objects::CallEffect::ReadOnly,
))
}
/// 原生分派返回直接整数,无对象结果需要额外保留;本文件主要验证契约门禁。
fn invoke(
&self,
_: &mut objects::NativeContext<'_>,
_: &[Value],
) -> Result<objects::NativeResult, VmError> {
Ok(objects::NativeResult::Return(Value::Number(1.into())))
}
}
#[test]
/// 验证能力声明/调用各入口均拒绝同名字段遮蔽,且递归守卫失败后正确清理。
fn typed_method_storage_cannot_diverge_from_class_dispatch() {
// 建立独立堆隔离场景,所有分配根在测试末尾明确归还。
let mut store = ObjectStore::new().unwrap();
// 自举 Object/Type,各有一份外部根;其图内自环需最终 collect_cycles。
let roots = store.bootstrap_types().unwrap();
// 给代理/反射实例附着真实名义类型,字段转发不应替换它。
let class = store
.new_type("Checked", roots.object, TypeConstructor::Record)
.unwrap();
// 上下文原生函数发布完整签名;类方法表还会单独拥有它的强边。
let function = store
.allocate(objects::ContextNativeFunction::new(
"check",
TypedCheck,
vec![],
))
.unwrap();
store.define_method(class, "check", function).unwrap();
// 能力要求与原生方法完整契约一致,包含显式 self 参数。
let capability = store
.new_typed_trait(
"Checked",
vec![objects::TypedMethodRequirement {
name: "check".into(),
signature: objects::CallSignature::exact(
vec![objects::AnnotationType::Any],
objects::AnnotationType::Number,
objects::CallEffect::ReadOnly,
),
}],
)
.unwrap();
// 普通 Record 作为无类型后端存储,只有代理持有时也须存活。
let storage = store.new_object().unwrap();
// Proxy 的 trace 持有后端,字段实际写入后端但类型留在 Proxy 头部。
let proxy = store.allocate(Proxy(storage)).unwrap();
store.attach_type(proxy, class).unwrap();
// 直接写无类型后端可以造出对代理而言不符合能力的实例,后续门禁应拒绝。
store.set_attr(storage, "check", Value::Nil).unwrap();
// 能力声明失败应保持定义版本和声明集合不变。
let version = store.change_version(class).unwrap();
assert!(matches!(
// 存储冲突消除后能力声明才允许提交。
store.implement_trait(class, capability),
Err(VmError::TraitViolation { .. })
));
assert_eq!(store.change_version(class).unwrap(), version);
// 失败声明不能留下一半能力关系。
assert!(!store.implements_trait(class, capability).unwrap());
// 删除遮蔽字段恢复合法存储,后续能力声明/查询应重新成功。
store.del_attr(storage, "check").unwrap();
// 存储冲突消除后能力声明才允许提交。
store.implement_trait(class, capability).unwrap();
// 代理受控写入在真正触及后端前失败,后端版本不得增加。
let version = store.change_version(storage).unwrap();
assert!(matches!(
store.set_attr(proxy, "check", Value::Nil),
Err(VmError::TraitViolation { .. })
));
assert_eq!(store.change_version(storage).unwrap(), version);
assert!(store.supports(proxy, capability).unwrap());
// 可信宿主能直接写无类型后端;supports/get_attr/bind_attr/调用检查/直接存储检查都必须发现失效实例。
// 直接写无类型后端可以造出对代理而言不符合能力的实例,后续门禁应拒绝。
store.set_attr(storage, "check", Value::Nil).unwrap();
assert!(matches!(
store.supports(proxy, capability),
Err(VmError::TraitViolation { .. })
));
assert!(matches!(
store.get_attr(proxy, "check"),
Err(VmError::TraitViolation { .. })
));
assert!(matches!(
store.bind_attr(proxy, "check"),
Err(VmError::TraitViolation { .. })
));
assert!(matches!(
store.check_method_contract_call(function, proxy, false),
Err(VmError::TraitViolation { .. })
));
assert!(matches!(
store.check_typed_instance_storage(proxy),
Err(VmError::TraitViolation { .. })
));
// 另建已有冲突字段的记录,验证 attach_type 也守住能力存储约束。
let candidate = store.new_object().unwrap();
// 先放同名字段再附着类型,不能绕过“创建后写入”的校验。
store.set_attr(candidate, "check", Value::Nil).unwrap();
assert!(matches!(
store.attach_type(candidate, class),
Err(VmError::TraitViolation { .. })
));
// 附着失败不能留下部分头部类型关系。
assert_eq!(store.type_of(candidate).unwrap(), None);
// 删除遮蔽字段恢复合法存储,后续能力声明/查询应重新成功。
store.del_attr(storage, "check").unwrap();
assert!(store.supports(proxy, capability).unwrap());
assert_eq!(
store.get_attr(proxy, "check").unwrap(),
Value::Object(function)
);
store
.check_method_contract_call(function, proxy, false)
.unwrap();
// 用托管 Cell 保存反射对象,初始 Nil 避免初始化时就递归查询未设字段。
let self_cell = store.new_cell(Some(Value::Nil)).unwrap();
// 载荷持有 Cell,Cell 稍后反向持有对象,形成可回收的托管环。
let reflective = store.allocate(ReflectiveType(self_cell)).unwrap();
store
.set_attr(reflective, "selector", Value::Bool(true))
.unwrap();
store
.cell_set(self_cell, Value::Object(reflective))
.unwrap();
// 附着能力类型期间查询 selector 会重入检查,守卫应避免无限递归。
store.attach_type(reflective, class).unwrap();
assert_eq!(
store.get_attr(reflective, "selector").unwrap(),
Value::Bool(true)
);
assert!(store.supports(reflective, capability).unwrap());
assert!(matches!(
store.set_attr(reflective, "check", Value::Nil),
Err(VmError::TraitViolation { .. })
));
// 首次失败必须通过 RAII 移除标记;第二次同样的非法写入仍应失败,不能被误当成递归跳过。
assert!(matches!(
store.set_attr(reflective, "check", Value::Nil),
Err(VmError::TraitViolation { .. })
));
// 归还自引用图外部根;环内边仍在,统一由最终回收处理。
store.release(reflective).unwrap();
// Cell 的外部根也归还,不能仅释放反射门面而留下测试根。
store.release(self_cell).unwrap();
// 逐项释放测试拥有根,不手动 release 内部边,避免重复归还。
for object in [
candidate,
proxy,
storage,
function,
capability,
class,
roots.object,
roots.type_type,
] {
store.release(object).unwrap();
}
// 回收类型自举环及其他已无外部根的环,最终堆应为空。
store.collect_cycles();
assert_eq!(store.object_count(), 0);
}