From 72cdd04603f420cf24b2a00b39258e2b8ad491cb Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sun, 15 Nov 2026 18:02:50 +0800 Subject: [PATCH] docs(core): explain typed capability validation scenarios --- crates/core/tests/typed_traits.rs | 77 ++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/crates/core/tests/typed_traits.rs b/crates/core/tests/typed_traits.rs index 50ad5a2..23cb45d 100644 --- a/crates/core/tests/typed_traits.rs +++ b/crates/core/tests/typed_traits.rs @@ -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 { Some(self.0.clone()) } + /// 原生测试入口;Native 返回直接整数,RebindThenReturn 先重绑定环境再返回借用对象。 fn invoke(&self, _: &mut NativeContext<'_>, _: &[Value]) -> Result { 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 { Some(self.signature.clone()) } + /// 原生测试入口;Native 返回直接整数,RebindThenReturn 先重绑定环境再返回借用对象。 fn invoke(&self, cx: &mut NativeContext<'_>, _: &[Value]) -> Result { + // 函数体把 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); }