feat: inherit native allocators and forward managed field storage
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
| 文件 | 覆盖 |
|
||||
| --- | --- |
|
||||
| [attribute_storage.rs](attribute_storage.rs) | 可转移字段存储、原类型身份、替换释放、属性写删和非法路径拒绝 |
|
||||
| [store.rs](store.rs) | 稳定地址、跨存储句柄拒绝与更新原子性、Dict/Cell 循环回收、函数校验失败无泄漏、共享代码与常量生命周期 |
|
||||
|
||||
非法句柄索引、槽位代数与计数溢出仍在 src/heap.rs 的私有单元测试内,不开放句柄构造器。VM 委托 API 与求值行为继续由 [core/tests](../../core/tests/README.md) 检查。随工作区在指定远端运行,本地不测试;现有测试源码不代表已通过,见 [验证](../../../docs/operations/validation.md)。返回 [objects](../README.md)。
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
use objects::{ObjectPayload, ObjectRef, ObjectStore, TypeConstructor, Value, VmError};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Proxy(ObjectRef);
|
||||
impl ObjectPayload for Proxy {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn kind_name(&self) -> &'static str {
|
||||
"proxy"
|
||||
}
|
||||
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
|
||||
visit(self.0);
|
||||
}
|
||||
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();
|
||||
let roots = store.bootstrap_types().unwrap();
|
||||
let class = store
|
||||
.new_type("Proxy", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
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();
|
||||
store.release(old_value).unwrap();
|
||||
let proxy = store.allocate(Proxy(storage)).unwrap();
|
||||
store.attach_type(proxy, class).unwrap();
|
||||
store.release(storage).unwrap();
|
||||
assert_eq!(store.attribute_path(proxy).unwrap(), vec![proxy, storage]);
|
||||
assert_eq!(store.get_attr(proxy, "type").unwrap(), Value::Object(class));
|
||||
assert_eq!(
|
||||
store.get_attr(proxy, "value").unwrap(),
|
||||
Value::Object(old_value)
|
||||
);
|
||||
let next = store.new_object().unwrap();
|
||||
store.replace_payload(proxy, Proxy(next)).unwrap();
|
||||
store.release(next).unwrap();
|
||||
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());
|
||||
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();
|
||||
let storage = store.new_object().unwrap();
|
||||
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);
|
||||
let other = ObjectStore::new().unwrap();
|
||||
assert_eq!(other.attribute_path(proxy), Err(VmError::ForeignHandle));
|
||||
store.release(storage).unwrap();
|
||||
store.release(proxy).unwrap();
|
||||
assert_eq!(store.collect_cycles(), 1);
|
||||
}
|
||||
Reference in New Issue
Block a user