docs(core): explain method substitution and update regression scenarios
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
//! 方法替换规则的集成回归:使用源码/原生完整签名,观察定义提交与调用前后契约边界。
|
||||
//! Compile here; execute only in the designated remote validation environment.
|
||||
use endor_core::{
|
||||
AnnotationType as T, CallEffect, CallSignature, CallType, CodeLayout, ContextNativeFunction,
|
||||
@@ -6,6 +7,7 @@ use endor_core::{
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// 测试辅助,生成先写 globals.ran=42 再返回 result 的源码方法,附显式 owner/参数/返回注解。
|
||||
fn source(
|
||||
vm: &mut Vm,
|
||||
globals: ObjectRef,
|
||||
@@ -14,6 +16,7 @@ fn source(
|
||||
returns: &str,
|
||||
result: Value,
|
||||
) -> ObjectRef {
|
||||
// 手工指令保证源码体可观测,参数/局部槽按注解数量保持一致。
|
||||
let code = EndorCode::from_instructions(
|
||||
&[
|
||||
Instruction::new(Opcode::LoadConst, 0),
|
||||
@@ -36,13 +39,17 @@ fn source(
|
||||
method_owner: Some(owner.into()),
|
||||
})
|
||||
.unwrap();
|
||||
// 代码先分配拥有根,后续函数持有它的内部边。
|
||||
let code = vm.new_code(code).unwrap();
|
||||
// 辅助方法函数持有代码/全局,返回拥有根供类方法表引用。
|
||||
let function = vm
|
||||
.new_function("check", code, globals, vec![], vec![])
|
||||
.unwrap();
|
||||
// 归还辅助构造代码临时根,不释放函数内部拥有边。
|
||||
vm.release(code).unwrap();
|
||||
function
|
||||
}
|
||||
/// 测试辅助,以给定基类构造只声明 check 方法、无自有字段的源码类。
|
||||
fn class(
|
||||
vm: &mut Vm,
|
||||
name: &str,
|
||||
@@ -58,11 +65,17 @@ fn class(
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 创建派生类即检查参数可接受集合不能缩窄、返回集合不能扩大。
|
||||
fn source_parameters_are_contravariant_and_returns_covariant_at_class_creation() {
|
||||
// 独立 VM 及名义类型图,避免全局重绑定与定义修改跨测试污染。
|
||||
let mut vm = Vm::new().unwrap();
|
||||
// 显式自举 Object/Type 根,最终归还并收集其类型图环。
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
// 普通 override 检查显式开启,不能仅依赖能力 opt-in 强制规则。
|
||||
vm.set_source_type_checks(true).unwrap();
|
||||
// 当前源码方法自己的注解解析环境,同时保存 ran 副作用标志。
|
||||
let globals = vm.new_dict().unwrap();
|
||||
// 父签名确定可接受参数集合和返回上界,方法自身类名由 owner 解析。
|
||||
let base_fn = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -71,7 +84,9 @@ fn source_parameters_are_contravariant_and_returns_covariant_at_class_creation()
|
||||
"Object",
|
||||
Value::Nil,
|
||||
);
|
||||
// 创建基类并提交父方法,自身没有父同名覆盖约束。
|
||||
let base = class(&mut vm, "Base", roots.object, base_fn).unwrap();
|
||||
// 参数 Number 放宽到 Object,返回 Object 收窄到 Number,符合可替换关系。
|
||||
let good_fn = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -80,7 +95,9 @@ fn source_parameters_are_contravariant_and_returns_covariant_at_class_creation()
|
||||
"Number",
|
||||
Value::Number(7.into()),
|
||||
);
|
||||
// 合法派生创建成功,保留其函数及基类引用。
|
||||
let good = class(&mut vm, "Good", base, good_fn).unwrap();
|
||||
// String 不能接纳父方法允许的 Number,所以即使返回约束合法也应拒绝。
|
||||
let narrow = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -96,6 +113,7 @@ fn source_parameters_are_contravariant_and_returns_covariant_at_class_creation()
|
||||
..
|
||||
})
|
||||
));
|
||||
// 另建父方法要求返回 Number,用于单独测试返回协变方向。
|
||||
let numeric = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -104,7 +122,9 @@ fn source_parameters_are_contravariant_and_returns_covariant_at_class_creation()
|
||||
"Number",
|
||||
Value::Number(0.into()),
|
||||
);
|
||||
// 以返回 Number 的方法作为新基线,不和先前 Object 返回父方法混淆。
|
||||
let numeric_type = class(&mut vm, "Numeric", roots.object, numeric).unwrap();
|
||||
// 返回 Object 比 Number 更宽,派生实现不能让父调用者失去数值返回保证。
|
||||
let broad = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -120,6 +140,7 @@ fn source_parameters_are_contravariant_and_returns_covariant_at_class_creation()
|
||||
..
|
||||
})
|
||||
));
|
||||
// 归还每份测试拥有根,类方法/基类/签名内部边由 Heap 维护。
|
||||
for object in [
|
||||
good,
|
||||
good_fn,
|
||||
@@ -135,16 +156,23 @@ fn source_parameters_are_contravariant_and_returns_covariant_at_class_creation()
|
||||
] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
// 显式回收无根类型/函数图环,最后验证存活对象数为零。
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 父方法拟议更新必须检查现有覆盖后代,失败保持父版本及双方原方法表。
|
||||
fn replacing_an_ancestor_validates_overriding_descendants_before_mutation() {
|
||||
// 独立 VM 及名义类型图,避免全局重绑定与定义修改跨测试污染。
|
||||
let mut vm = Vm::new().unwrap();
|
||||
// 显式自举 Object/Type 根,最终归还并收集其类型图环。
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
// 普通 override 检查显式开启,不能仅依赖能力 opt-in 强制规则。
|
||||
vm.set_source_type_checks(true).unwrap();
|
||||
// 当前源码方法自己的注解解析环境,同时保存 ran 副作用标志。
|
||||
let globals = vm.new_dict().unwrap();
|
||||
// 父签名确定可接受参数集合和返回上界,方法自身类名由 owner 解析。
|
||||
let base_fn = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -153,7 +181,9 @@ fn replacing_an_ancestor_validates_overriding_descendants_before_mutation() {
|
||||
"Object",
|
||||
Value::Nil,
|
||||
);
|
||||
// 创建基类并提交父方法,自身没有父同名覆盖约束。
|
||||
let base = class(&mut vm, "Base", roots.object, base_fn).unwrap();
|
||||
// 后代自己的签名需与当前父签名保持替换关系,即使后代已先创建。
|
||||
let child_fn = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -162,7 +192,9 @@ fn replacing_an_ancestor_validates_overriding_descendants_before_mutation() {
|
||||
"Number",
|
||||
Value::Number(0.into()),
|
||||
);
|
||||
// 先创建当前合法后代,再对父方法提出可能使它失效的更新。
|
||||
let child = class(&mut vm, "Child", base, child_fn).unwrap();
|
||||
// 父参数从 Number 放宽到 Object 后,现有子方法仍只接收 Number,将不再可替换。
|
||||
let replacement = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -171,6 +203,7 @@ fn replacing_an_ancestor_validates_overriding_descendants_before_mutation() {
|
||||
"Object",
|
||||
Value::Nil,
|
||||
);
|
||||
// 记录父定义版本,验证失败在真实载荷替换前发生。
|
||||
let version = vm.change_version(base).unwrap();
|
||||
assert!(matches!(
|
||||
vm.define_method(base, "check", replacement),
|
||||
@@ -185,6 +218,7 @@ fn replacing_an_ancestor_validates_overriding_descendants_before_mutation() {
|
||||
vm.object_store().lookup_method(child, "check").unwrap(),
|
||||
Some(child_fn)
|
||||
);
|
||||
// 归还每份测试拥有根,类方法/基类/签名内部边由 Heap 维护。
|
||||
for object in [
|
||||
replacement,
|
||||
child,
|
||||
@@ -197,27 +231,38 @@ fn replacing_an_ancestor_validates_overriding_descendants_before_mutation() {
|
||||
] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
// 显式回收无根类型/函数图环,最后验证存活对象数为零。
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 类型名全局重绑定可破坏已有覆盖关系,调用前须重验并阻止体副作用,恢复绑定后可再调用。
|
||||
fn global_rebinding_is_rechecked_before_the_source_body_and_recovers() {
|
||||
// 独立 VM 及名义类型图,避免全局重绑定与定义修改跨测试污染。
|
||||
let mut vm = Vm::new().unwrap();
|
||||
// 显式自举 Object/Type 根,最终归还并收集其类型图环。
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
// 普通 override 检查显式开启,不能仅依赖能力 opt-in 强制规则。
|
||||
vm.set_source_type_checks(true).unwrap();
|
||||
// Result 基类型是父方法返回约束,ResultChild 初始满足其协变要求。
|
||||
let parent = vm
|
||||
.new_type("Result", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 派生返回类型起初是父返回类型子类,后续全局绑定将故意破坏该关系。
|
||||
let result_type = vm
|
||||
.new_type("ResultChild", parent, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 无关类型可实例化但不是 Result 子类,不能作为合法协变返回类型。
|
||||
let other = vm
|
||||
.new_type("Other", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 实际返回对象先保留测试根,源码常量表也会持有它的边。
|
||||
let result = vm.new_object().unwrap();
|
||||
vm.attach_type(result, result_type).unwrap();
|
||||
// 父方法独立环境固定 Result,子环境 Actual 重绑定不应改变父约束。
|
||||
let base_globals = vm.new_dict().unwrap();
|
||||
// 当前源码方法自己的注解解析环境,同时保存 ran 副作用标志。
|
||||
let globals = vm.new_dict().unwrap();
|
||||
vm.dict_set(
|
||||
base_globals,
|
||||
@@ -233,6 +278,7 @@ fn global_rebinding_is_rechecked_before_the_source_body_and_recovers() {
|
||||
.unwrap();
|
||||
vm.dict_set(globals, Value::Text("ran".into()), Value::Number(0.into()))
|
||||
.unwrap();
|
||||
// 父签名确定可接受参数集合和返回上界,方法自身类名由 owner 解析。
|
||||
let base_fn = source(
|
||||
&mut vm,
|
||||
base_globals,
|
||||
@@ -241,7 +287,9 @@ fn global_rebinding_is_rechecked_before_the_source_body_and_recovers() {
|
||||
"Result",
|
||||
Value::Nil,
|
||||
);
|
||||
// 创建基类并提交父方法,自身没有父同名覆盖约束。
|
||||
let base = class(&mut vm, "Base", roots.object, base_fn).unwrap();
|
||||
// 后代自己的签名需与当前父签名保持替换关系,即使后代已先创建。
|
||||
let child_fn = source(
|
||||
&mut vm,
|
||||
globals,
|
||||
@@ -250,7 +298,9 @@ fn global_rebinding_is_rechecked_before_the_source_body_and_recovers() {
|
||||
"Actual",
|
||||
Value::Object(result),
|
||||
);
|
||||
// 先创建当前合法后代,再对父方法提出可能使它失效的更新。
|
||||
let child = class(&mut vm, "Child", base, child_fn).unwrap();
|
||||
// 显式 self 为 Child 实例,调用时据其继承链解析各方法 own-class 注解。
|
||||
let receiver = vm.new_object().unwrap();
|
||||
vm.attach_type(receiver, child).unwrap();
|
||||
vm.dict_set(globals, Value::Text("Actual".into()), Value::Object(other))
|
||||
@@ -269,9 +319,12 @@ fn global_rebinding_is_rechecked_before_the_source_body_and_recovers() {
|
||||
Value::Object(result_type),
|
||||
)
|
||||
.unwrap();
|
||||
// 恢复合法全局绑定后调用成功,结果根仍须与原测试对象根分别归还。
|
||||
let value = vm.call(child_fn, &[Value::Object(receiver)]).unwrap();
|
||||
assert_eq!(value, Value::Object(result));
|
||||
// 只释放本次调用新增的结果根,源码常量及测试原根还在。
|
||||
vm.release_value(value).unwrap();
|
||||
// 归还每份测试拥有根,类方法/基类/签名内部边由 Heap 维护。
|
||||
for object in [
|
||||
receiver,
|
||||
child,
|
||||
@@ -289,34 +342,46 @@ fn global_rebinding_is_rechecked_before_the_source_body_and_recovers() {
|
||||
] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
// 显式回收无根类型/函数图环,最后验证存活对象数为零。
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// 无捕获测试回调,签名通过包装载荷 trace 保活,不手动按定义身份 retain。
|
||||
struct Native(CallSignature);
|
||||
impl NativeCallable for Native {
|
||||
/// 包装载荷取得完整签名快照,定义引用由 ContextNativeFunction trace 持有。
|
||||
fn signature(&self) -> Option<CallSignature> {
|
||||
Some(self.0.clone())
|
||||
}
|
||||
/// 测试原生体只返回直接 Number;被拒绝场景主要检查声明的替换关系而非运行副作用。
|
||||
fn invoke(&self, _: &mut NativeContext<'_>, _: &[Value]) -> Result<NativeResult, VmError> {
|
||||
Ok(NativeResult::Return(Value::Number(0.into())))
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
/// 原生覆盖不能丢弃父联合参数分支,也不能把已知 ReadOnly 扩大为 Mutating。
|
||||
fn native_overrides_preserve_union_inputs_and_do_not_expand_effects() {
|
||||
// 独立 VM 及名义类型图,避免全局重绑定与定义修改跨测试污染。
|
||||
let mut vm = Vm::new().unwrap();
|
||||
// 显式自举 Object/Type 根,最终归还并收集其类型图环。
|
||||
let roots = vm.bootstrap_types().unwrap();
|
||||
// 普通 override 检查显式开启,不能仅依赖能力 opt-in 强制规则。
|
||||
vm.set_source_type_checks(true).unwrap();
|
||||
// 原生场景的名义 Base,签名首项按它校验接收者。
|
||||
let base = vm
|
||||
.new_type("Base", roots.object, TypeConstructor::Record)
|
||||
.unwrap();
|
||||
// 先建立完整父签名,再把普通参数换为联合 Number | String。
|
||||
let mut signature = CallSignature::exact(
|
||||
vec![T::Nominal(base), T::Any],
|
||||
T::Number,
|
||||
CallEffect::ReadOnly,
|
||||
);
|
||||
// 保留两条可接受输入分支,子实现必须全部接纳。
|
||||
signature.parameters[1] = CallType::Union(vec![T::Number, T::String]);
|
||||
// ContextNativeFunction 签名快照持有 Base 定义,方法表又持函数边,最终可能成环。
|
||||
let base_fn = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"check",
|
||||
@@ -325,7 +390,9 @@ fn native_overrides_preserve_union_inputs_and_do_not_expand_effects() {
|
||||
))
|
||||
.unwrap();
|
||||
vm.define_method(base, "check", base_fn).unwrap();
|
||||
// 接收者由 Base 派生,子签名仍须接受声明类型实例。
|
||||
let child = vm.new_type("Child", base, TypeConstructor::Record).unwrap();
|
||||
// 输入 Any 足够宽,但 Mutating 扩大父 ReadOnly 效果,必须拒绝。
|
||||
let effect_fn = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"effect",
|
||||
@@ -344,6 +411,7 @@ fn native_overrides_preserve_union_inputs_and_do_not_expand_effects() {
|
||||
..
|
||||
})
|
||||
));
|
||||
// 效果保持只读却只接受 Number,丢失父 String 分支,同样拒绝。
|
||||
let narrow_fn = vm
|
||||
.allocate(ContextNativeFunction::new(
|
||||
"narrow",
|
||||
@@ -362,6 +430,7 @@ fn native_overrides_preserve_union_inputs_and_do_not_expand_effects() {
|
||||
..
|
||||
})
|
||||
));
|
||||
// 归还每份测试拥有根,类方法/基类/签名内部边由 Heap 维护。
|
||||
for object in [
|
||||
narrow_fn,
|
||||
effect_fn,
|
||||
@@ -373,6 +442,7 @@ fn native_overrides_preserve_union_inputs_and_do_not_expand_effects() {
|
||||
] {
|
||||
vm.release(object).unwrap();
|
||||
}
|
||||
// 显式回收无根类型/函数图环,最后验证存活对象数为零。
|
||||
vm.collect_cycles();
|
||||
assert_eq!(vm.object_count(), 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user