docs(core): explain typed capability validation scenarios
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
//! 完整类型化能力的集成回归:注册替换关系、调用实参/结果验证,以及能力定义的强边保活。
|
||||
//! 测试框架使用公开 Vm API,源码辅助函数手工构造已验证指令,无需源码解析器。
|
||||
//! Remote-only coverage of explicit typed capability contracts.
|
||||
use endor_core::{
|
||||
AnnotationType as T, CallEffect, CallSignature, CodeLayout, ContextNativeFunction, EndorCode,
|
||||
@@ -5,6 +7,7 @@ use endor_core::{
|
||||
TypeConstructor, TypedMethodRequirement, Value, Vm, VmError,
|
||||
};
|
||||
|
||||
/// 测试辅助:构造单个 check 方法能力,完整参数列表含显式 self,返回一份能力根。
|
||||
fn capability(
|
||||
vm: &mut Vm,
|
||||
name: &str,
|
||||
@@ -21,14 +24,17 @@ fn capability(
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
/// 测试辅助:构造先写 globals.ran=42 再返回给定常量的源码方法,附 owner 与类型注解。
|
||||
fn source(
|
||||
vm: &mut Vm,
|
||||
/// 含 Current 注解名称的字典,包装载荷通过 captures 持边。
|
||||
globals: ObjectRef,
|
||||
owner: &str,
|
||||
types: &[&str],
|
||||
returns: &str,
|
||||
value: Value,
|
||||
) -> ObjectRef {
|
||||
// 手工代码提供可观察 ran 副作用,参数/局部布局按类型列表一一对应。
|
||||
let code = EndorCode::from_instructions(
|
||||
&[
|
||||
Instruction::new(Opcode::LoadConst, 0),
|
||||
@@ -51,30 +57,41 @@ fn source(
|
||||
method_owner: Some(owner.into()),
|
||||
})
|
||||
.unwrap();
|
||||
// 分配代码后常量成为内部强边,函数将持有代码引用。
|
||||
let code = vm.new_code(code).unwrap();
|
||||
// 分配源码/原生函数拥有根,完整签名与显式 captures 各自报告托管引用。
|
||||
let function = vm
|
||||
.new_function("check", code, globals, vec![], vec![])
|
||||
.unwrap();
|
||||
// 归还辅助函数临时代码根,源码函数仍拥有代码边。
|
||||
vm.release(code).unwrap();
|
||||
function
|
||||
}
|
||||
#[derive(Debug)]
|
||||
/// 返回固定直接整数的测试原生分派器,签名由包装载荷快照持有。
|
||||
struct Native(CallSignature);
|
||||
impl NativeCallable for Native {
|
||||
/// 原生实现公布不可变完整签名,ContextNativeFunction 会快照并 trace 类型定义引用。
|
||||
fn signature(&self) -> Option<CallSignature> {
|
||||
Some(self.0.clone())
|
||||
}
|
||||
/// 原生测试入口;Native 返回直接整数,RebindThenReturn 先重绑定环境再返回借用对象。
|
||||
fn invoke(&self, _: &mut NativeContext<'_>, _: &[Value]) -> Result<NativeResult, VmError> {
|
||||
Ok(NativeResult::Return(Value::Number(7.into())))
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
/// 注册失败不改变版本/声明;实现参数可比能力更宽,调用允许其自己签名支持的额外值。
|
||||
fn typed_registration_is_atomic_and_wider_native_inputs_remain_callable() {
|
||||
// 每个测试独立 VM,避免契约/全局修改污染其他场景。
|
||||
let mut vm = Vm::new().unwrap();
|
||||
// Object/Type 自举根在末尾归还,元类型环随后显式回收。
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
// 名义类型用于方法 owner/签名;身份不是按类型名字符串比较。
|
||||
let definition = vm
|
||||
.new_type("Model", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 能力签名供注册与实际调用核对,名义/能力引用由能力载荷 trace 保活。
|
||||
let contract = capability(
|
||||
&mut vm,
|
||||
"Numeric",
|
||||
@@ -82,10 +99,12 @@ fn typed_registration_is_atomic_and_wider_native_inputs_remain_callable() {
|
||||
T::Number,
|
||||
CallEffect::ReadOnly,
|
||||
);
|
||||
// 分别测试参数缩窄与效果扩大,两者都必须在声明阶段失败。
|
||||
for (argument, effect) in [
|
||||
(T::String, CallEffect::ReadOnly),
|
||||
(T::Any, CallEffect::Mutating),
|
||||
] {
|
||||
// 分配源码/原生函数拥有根,完整签名与显式 captures 各自报告托管引用。
|
||||
let function = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"bad",
|
||||
@@ -98,6 +117,7 @@ fn typed_registration_is_atomic_and_wider_native_inputs_remain_callable() {
|
||||
))
|
||||
.unwrap();
|
||||
vm.define_method(definition, "check", function).unwrap();
|
||||
// 失败能力声明不能修改类型载荷版本或留下已声明关系。
|
||||
let version = vm.change_version(definition).unwrap();
|
||||
assert!(matches!(
|
||||
vm.implement_trait(definition, contract),
|
||||
@@ -107,6 +127,7 @@ fn typed_registration_is_atomic_and_wider_native_inputs_remain_callable() {
|
||||
assert!(!vm.implements_trait(definition, contract).unwrap());
|
||||
vm.release(function).unwrap();
|
||||
}
|
||||
// 分配源码/原生函数拥有根,完整签名与显式 captures 各自报告托管引用。
|
||||
let function = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"good",
|
||||
@@ -120,6 +141,7 @@ fn typed_registration_is_atomic_and_wider_native_inputs_remain_callable() {
|
||||
.unwrap();
|
||||
vm.define_method(definition, "check", function).unwrap();
|
||||
vm.implement_trait(definition, contract).unwrap();
|
||||
// 实例先分配根再附着类型,作为显式 self 传入方法。
|
||||
let receiver = vm.new_object().unwrap();
|
||||
vm.attach_type(receiver, definition).unwrap();
|
||||
assert_eq!(
|
||||
@@ -133,6 +155,7 @@ fn typed_registration_is_atomic_and_wider_native_inputs_remain_callable() {
|
||||
.unwrap(),
|
||||
Value::Number(7.into())
|
||||
);
|
||||
// 逐项归还测试拥有根,类型/签名/捕获边由 Heap 自动维护,不能重复释放。
|
||||
for object in [
|
||||
receiver,
|
||||
function,
|
||||
@@ -143,18 +166,25 @@ fn typed_registration_is_atomic_and_wider_native_inputs_remain_callable() {
|
||||
] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
// 收集无根元类型/签名/方法图环,断言最终没有存活堆对象。
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 显式能力启用源码注解检查,参数错误应阻止函数体副作用,返回错误发生在体执行之后。
|
||||
fn typed_source_methods_enforce_their_annotations_even_when_global_checks_are_off() {
|
||||
// 每个测试独立 VM,避免契约/全局修改污染其他场景。
|
||||
let mut vm = Vm::new().unwrap();
|
||||
// Object/Type 自举根在末尾归还,元类型环随后显式回收。
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
// 名义类型用于方法 owner/签名;身份不是按类型名字符串比较。
|
||||
let definition = vm
|
||||
.new_type("Model", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 源码方法注解在此环境解析,同时 ran 标志观测函数体是否进入。
|
||||
let globals = vm.new_dict().unwrap();
|
||||
// 源码参数声明 Number 但返回 Bool,分别验证参数阶段和返回阶段的检查。
|
||||
let function = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -164,14 +194,16 @@ fn typed_source_methods_enforce_their_annotations_even_when_global_checks_are_of
|
||||
Value::Bool(true),
|
||||
);
|
||||
vm.define_method(definition, "check", function).unwrap();
|
||||
// 实例先分配根再附着类型,作为显式 self 传入方法。
|
||||
let receiver = vm.new_object().unwrap();
|
||||
vm.attach_type(receiver, definition).unwrap();
|
||||
// Existing untyped declarations retain the default-off compatibility mode.
|
||||
// 尚未声明类型化能力且未开全局源码检查时,旧方法保持兼容:Bool 参数与结果都暂不验注解。
|
||||
assert_eq!(
|
||||
vm.call(function, &[Value::Object(receiver), Value::Bool(false)])
|
||||
.unwrap(),
|
||||
Value::Bool(true)
|
||||
);
|
||||
// 能力签名供注册与实际调用核对,名义/能力引用由能力载荷 trace 保活。
|
||||
let contract = capability(
|
||||
&mut vm,
|
||||
"Numeric",
|
||||
@@ -180,6 +212,7 @@ fn typed_source_methods_enforce_their_annotations_even_when_global_checks_are_of
|
||||
CallEffect::ReadOnly,
|
||||
);
|
||||
vm.implement_trait(definition, contract).unwrap();
|
||||
// 先复位 ran;类型不匹配参数应在任何源码 StoreGlobal 前被拒绝。
|
||||
vm.dict_set(globals, Value::Text("ran".into()), Value::Number(0.into()))
|
||||
.unwrap();
|
||||
assert!(matches!(
|
||||
@@ -197,6 +230,7 @@ fn typed_source_methods_enforce_their_annotations_even_when_global_checks_are_of
|
||||
vm.dict_get(globals, &Value::Text("ran".into())).unwrap(),
|
||||
Some(Value::Number(42.into()))
|
||||
);
|
||||
// 逐项归还测试拥有根,类型/签名/捕获边由 Heap 自动维护,不能重复释放。
|
||||
for object in [
|
||||
receiver,
|
||||
function,
|
||||
@@ -208,18 +242,25 @@ fn typed_source_methods_enforce_their_annotations_even_when_global_checks_are_of
|
||||
] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
// 收集无根元类型/签名/方法图环,断言最终没有存活堆对象。
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 能力签名的参数/返回名义定义被强边保留,能力销毁后无隐藏注册根阻止回收。
|
||||
fn typed_trait_definitions_trace_their_signature_types_without_hidden_roots() {
|
||||
// 每个测试独立 VM,避免契约/全局修改污染其他场景。
|
||||
let mut vm = Vm::new().unwrap();
|
||||
// Object/Type 自举根在末尾归还,元类型环随后显式回收。
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
// 名义类型用于方法 owner/签名;身份不是按类型名字符串比较。
|
||||
let definition = vm
|
||||
.new_type("Value", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 弱身份不持根,用于观察签名边是否真的保留定义及何时释放。
|
||||
let weak = vm.downgrade(definition).unwrap();
|
||||
// 能力签名供注册与实际调用核对,名义/能力引用由能力载荷 trace 保活。
|
||||
let contract = capability(
|
||||
&mut vm,
|
||||
"ValueContract",
|
||||
@@ -227,34 +268,48 @@ fn typed_trait_definitions_trace_their_signature_types_without_hidden_roots() {
|
||||
T::Nominal(definition),
|
||||
CallEffect::ReadOnly,
|
||||
);
|
||||
// 归还定义测试根,能力参数/返回签名的拥有边必须继续保活它。
|
||||
vm.release(definition).unwrap();
|
||||
// 成功弱升级取得额外根,读完必须归还,否则会掩盖隐藏根检查。
|
||||
let retained = vm
|
||||
.upgrade(weak)
|
||||
.unwrap()
|
||||
.expect("signature retains its type");
|
||||
// 仅归还弱升级的那一份根,签名仍持有自己的内部边。
|
||||
vm.release(retained).unwrap();
|
||||
// 能力最后根归还后,其签名定义边也应消失。
|
||||
vm.release(contract).unwrap();
|
||||
// 收集无根元类型/签名/方法图环,断言最终没有存活堆对象。
|
||||
vm.collect_cycles();
|
||||
assert!(vm.upgrade(weak).unwrap().is_none());
|
||||
// 逐项归还测试拥有根,类型/签名/捕获边由 Heap 自动维护,不能重复释放。
|
||||
for object in [roots.object, roots.type_type] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
// 收集无根元类型/签名/方法图环,断言最终没有存活堆对象。
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RebindThenReturn {
|
||||
/// 自身直接返回约束仍满足 Model,但外层能力返回约束需更严格重验 Feature。
|
||||
signature: CallSignature,
|
||||
/// 含 Current 注解名称的字典,包装载荷通过 captures 持边。
|
||||
globals: ObjectRef,
|
||||
/// 用于破坏 Current 约束的无关类型,captures 负责保活。
|
||||
other: ObjectRef,
|
||||
/// 返回的借用 Model 实例,结果根由 NativeContext 获取。
|
||||
result: ObjectRef,
|
||||
}
|
||||
impl NativeCallable for RebindThenReturn {
|
||||
/// 原生实现公布不可变完整签名,ContextNativeFunction 会快照并 trace 类型定义引用。
|
||||
fn signature(&self) -> Option<CallSignature> {
|
||||
Some(self.signature.clone())
|
||||
}
|
||||
/// 原生测试入口;Native 返回直接整数,RebindThenReturn 先重绑定环境再返回借用对象。
|
||||
fn invoke(&self, cx: &mut NativeContext<'_>, _: &[Value]) -> Result<NativeResult, VmError> {
|
||||
// 函数体把 Current 重绑定为无关 Other,主动使返回 Model 的 Feature 契约失效。
|
||||
cx.dict_set(
|
||||
self.globals,
|
||||
Value::Text("Current".into()),
|
||||
@@ -264,21 +319,29 @@ impl NativeCallable for RebindThenReturn {
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
/// 原生体重绑定返回对象的能力所需注解环境后,返回时必须重验并报错,重复失败不能残留递归标记。
|
||||
fn actual_trait_returns_revalidate_capabilities_after_body_side_effects_and_restore_guards() {
|
||||
// 每个测试独立 VM,避免契约/全局修改污染其他场景。
|
||||
let mut vm = Vm::new().unwrap();
|
||||
// Object/Type 自举根在末尾归还,元类型环随后显式回收。
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
// Feature 方法返回值必须属于该基类型或其子类型。
|
||||
let result_base = vm
|
||||
.new_type("Result", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// Current 初始指向合法子类型,回调结束后的重验会观察该名称被替换。
|
||||
let result_child = vm
|
||||
.new_type("ResultChild", result_base, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 同为 Record 但不是 Result 子类,替换 Current 后应破坏 Feature 契约。
|
||||
let other = vm
|
||||
.new_type("Other", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 返回值实例的类型,check 源码方法声明 Current 作为返回类型。
|
||||
let model = vm
|
||||
.new_type("Model", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 源码方法注解在此环境解析,同时 ran 标志观测函数体是否进入。
|
||||
let globals = vm.new_dict().unwrap();
|
||||
vm.dict_set(
|
||||
globals,
|
||||
@@ -286,8 +349,10 @@ fn actual_trait_returns_revalidate_capabilities_after_body_side_effects_and_rest
|
||||
Value::Object(result_child),
|
||||
)
|
||||
.unwrap();
|
||||
// 手工方法虽此处不运行,其当前签名仍用于能力符合性验证。
|
||||
let read = source(&mut vm, globals, "Model", &["Model"], "Current", Value::Nil);
|
||||
vm.define_method(model, "check", read).unwrap();
|
||||
// Model 显式实现 Feature,要求 check 返回 Result 基类型。
|
||||
let feature = capability(
|
||||
&mut vm,
|
||||
"Feature",
|
||||
@@ -296,11 +361,14 @@ fn actual_trait_returns_revalidate_capabilities_after_body_side_effects_and_rest
|
||||
CallEffect::External,
|
||||
);
|
||||
vm.implement_trait(model, feature).unwrap();
|
||||
// 原生 produce 返回此实例;返回约束要求它仍满足 Feature 而非仅有历史声明。
|
||||
let value = vm.new_object().unwrap();
|
||||
vm.attach_type(value, model).unwrap();
|
||||
// 调用原生 produce 的接收者类型,与被返回 Model 分开。
|
||||
let actor = vm
|
||||
.new_type("Actor", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 能力签名供注册与实际调用核对,名义/能力引用由能力载荷 trace 保活。
|
||||
let contract = capability(
|
||||
&mut vm,
|
||||
"Producer",
|
||||
@@ -308,6 +376,7 @@ fn actual_trait_returns_revalidate_capabilities_after_body_side_effects_and_rest
|
||||
T::Capability(feature),
|
||||
CallEffect::External,
|
||||
);
|
||||
// 分配源码/原生函数拥有根,完整签名与显式 captures 各自报告托管引用。
|
||||
let function = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"produce",
|
||||
@@ -326,13 +395,15 @@ fn actual_trait_returns_revalidate_capabilities_after_body_side_effects_and_rest
|
||||
.unwrap();
|
||||
vm.define_method(actor, "check", function).unwrap();
|
||||
vm.implement_trait(actor, contract).unwrap();
|
||||
// 实例先分配根再附着类型,作为显式 self 传入方法。
|
||||
let receiver = vm.new_object().unwrap();
|
||||
vm.attach_type(receiver, actor).unwrap();
|
||||
assert!(matches!(
|
||||
vm.call(function, &[Value::Object(receiver)]),
|
||||
Err(VmError::TraitViolation { .. })
|
||||
));
|
||||
// Repeated failures must not leave a recursive-validation guard marked active.
|
||||
// 验证失败必须清理递归 guard;环境仍坏时每一次重新检查都应再次失败。
|
||||
// 重复能力失败验证 RAII 已清除递归 key,不能第二次被当重入跳过。
|
||||
for _ in 0..2 {
|
||||
assert!(vm.implements_trait(model, feature).is_err());
|
||||
}
|
||||
@@ -343,6 +414,7 @@ fn actual_trait_returns_revalidate_capabilities_after_body_side_effects_and_rest
|
||||
)
|
||||
.unwrap();
|
||||
assert!(vm.implements_trait(model, feature).unwrap());
|
||||
// 逐项归还测试拥有根,类型/签名/捕获边由 Heap 自动维护,不能重复释放。
|
||||
for object in [
|
||||
receiver,
|
||||
function,
|
||||
@@ -361,6 +433,7 @@ fn actual_trait_returns_revalidate_capabilities_after_body_side_effects_and_rest
|
||||
] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
// 收集无根元类型/签名/方法图环,断言最终没有存活堆对象。
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user