Prevent instance attributes from shadowing typed capability methods
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
| 文件 | 覆盖 |
|
||||
| --- | --- |
|
||||
| [attribute_storage.rs](attribute_storage.rs) | 可转移字段存储、原类型身份、替换释放、属性写删和非法路径拒绝 |
|
||||
| [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)。
|
||||
|
||||
@@ -76,3 +76,174 @@ fn cyclic_and_foreign_attribute_storage_fail_without_mutating_fields() {
|
||||
store.release(proxy).unwrap();
|
||||
assert_eq!(store.collect_cycles(), 1);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ReflectiveType(ObjectRef);
|
||||
impl ObjectPayload for ReflectiveType {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn kind_name(&self) -> &'static str {
|
||||
"reflective-type"
|
||||
}
|
||||
fn allows_attributes(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
|
||||
visit(self.0);
|
||||
}
|
||||
fn type_target(&self, store: &ObjectStore) -> Result<Option<ObjectRef>, VmError> {
|
||||
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 {
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
let storage = store.new_object().unwrap();
|
||||
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());
|
||||
// A trusted host can access the untyped backing record directly. Every
|
||||
// language/host dispatch gate must detect the resulting invalid instance.
|
||||
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 { .. })
|
||||
));
|
||||
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();
|
||||
let self_cell = store.new_cell(Some(Value::Nil)).unwrap();
|
||||
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();
|
||||
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 { .. })
|
||||
));
|
||||
// A failed guarded check must not suppress the next one.
|
||||
assert!(matches!(
|
||||
store.set_attr(reflective, "check", Value::Nil),
|
||||
Err(VmError::TraitViolation { .. })
|
||||
));
|
||||
store.release(reflective).unwrap();
|
||||
store.release(self_cell).unwrap();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user