Validate unified source and native positional call contracts

This commit is contained in:
2026-10-22 12:02:41 +08:00
parent 20e29e3c5a
commit 44519abf7e
17 changed files with 331 additions and 28 deletions
+2 -2
View File
@@ -5,9 +5,9 @@
| 文件 | 职责 |
| --- | --- |
| [lib.rs](lib.rs) | VM 类型导出、objects 类型兼容重导出、宿主调用与字节码 rustdoc 示例 |
| [vm.rs](vm.rs) | Vm 拥有 ObjectStore 与错误位置记录;对象操作和宿主 new_class 和显式Trait注册/关系查询显式委托给 objects;函数/字段检查统一开关与函数类型配置 |
| [vm.rs](vm.rs) | Vm 拥有 ObjectStore 与错误位置记录;对象操作和宿主 new_class 和显式Trait注册/关系查询显式委托给 objects;函数/字段检查统一开关、统一参数区间查询与函数类型配置 |
| [frame.rs](frame.rs) | EndorFrame、栈条目、返回类型执行根、执行位置元数据、ExecutionLimits |
| [eval.rs](eval.rs) | execute/call、只读调用与依赖结果、参数绑定、可选注解检查、源码类字段环境绑定、帧切换、取指分派、MakeFunction、结果根转移与失败清理 |
| [eval.rs](eval.rs) | execute/call、只读调用与依赖结果、参数绑定、原生实现前数量检查、可选注解检查、源码类字段环境绑定、帧切换、取指分派、MakeFunction、结果根转移与失败清理 |
## 常见修改入口
+10
View File
@@ -260,6 +260,16 @@ impl Vm {
for argument in arguments {
self.validate_value(argument)?;
}
if let Some(arity) = self.objects.call_arity(function)?
&& !arity.accepts(arguments.len())
{
return Err(VmError::ArgumentCount {
name: self.object(function)?.payload().kind_name().into(),
required: arity.required,
maximum: arity.maximum,
provided: arguments.len(),
});
}
if let Some(callable) = self.object(function)?.payload().native_callable() {
return NativeContext::execute(&mut self.objects, function, arguments, callable);
}
+7 -7
View File
@@ -50,12 +50,12 @@ pub use objects::{
TypeConstructor,
};
pub use objects::{
ChangeBatch, ChangeKey, CodeLayout, CompareOp, ContextNativeFunction, DecodedInstruction,
EndorBaseExceptionObject, EndorCellObject, EndorCode, EndorCodeObject, EndorDecimal,
EndorDictObject, EndorFunctionObject, EndorInteger, EndorNativeCall, EndorNativeFunctionObject,
EndorNumber, EndorNumberObject, EndorObject, EndorRational, EndorReal, EndorStringObject,
EndorTracebackEntry, Instruction, NativeCall, NativeCallable, NativeContext, NativeFunction,
NativeResult, ObjectChange, ObjectHeader, ObjectPayload, ObjectRef, Opcode, Record,
SourceSignature, Value, VmError, WeakObjectRef,
CallArity, ChangeBatch, ChangeKey, CodeLayout, CompareOp, ContextNativeFunction,
DecodedInstruction, EndorBaseExceptionObject, EndorCellObject, EndorCode, EndorCodeObject,
EndorDecimal, EndorDictObject, EndorFunctionObject, EndorInteger, EndorNativeCall,
EndorNativeFunctionObject, EndorNumber, EndorNumberObject, EndorObject, EndorRational,
EndorReal, EndorStringObject, EndorTracebackEntry, Instruction, NativeCall, NativeCallable,
NativeContext, NativeFunction, NativeResult, ObjectChange, ObjectHeader, ObjectPayload,
ObjectRef, Opcode, Record, SourceSignature, Value, VmError, WeakObjectRef,
};
pub use vm::Vm;
+4
View File
@@ -25,6 +25,10 @@ impl Vm {
self.source_type_checks = enabled;
Ok(())
}
pub fn call_arity(&self, function: ObjectRef) -> Result<Option<objects::CallArity>, VmError> {
self.objects.call_arity(function)
}
pub fn set_function_type(&mut self, definition: ObjectRef) -> Result<(), VmError> {
self.objects.set_function_type(definition)
}
+1 -1
View File
@@ -7,7 +7,7 @@
| [runtime.rs](runtime.rs) | 空堆启动、默认操作、引用/借用、属性原子性、稳定地址、长链/循环、原生回调、VM 销毁 |
| [builtins.rs](builtins.rs) | 无损 Number、Unicode 字符串、Dict 顺序与键、Cell、函数环境/共享代码、异常数据 |
| [execution.rs](execution.rs) | wordcode 与校验、控制流、参数/默认值、嵌套帧、字节码函数创建/默认值/环境捕获、闭包、返回值存活、失败清理、限额 |
| [types.rs](types.rs) | 名义身份、继承与实例关系、类型边保活/循环回收、只读类型元数据与外来句柄、显式Trait身份/继承/参数约束及后代更新原子性;统一函数分类与返回类型错误根清理;仅远端执行 |
| [types.rs](types.rs) | 名义身份、继承与实例关系、类型边保活/循环回收、只读类型元数据与外来句柄、显式Trait身份/继承/参数约束及后代更新原子性;统一函数分类、原生数量元数据/副作用前拒绝/能力更新/默认参数与返回类型错误根清理;仅远端执行 |
| [native_context.rs](native_context.rs) | 原生作用域根、分配失败清理、委托、只读结果分配/写入拒绝/依赖与失败恢复、弱引用过期及有界变更;仅远端执行 |
核心对象现由 objects 定义,本目录保留既有 endor_core 导入与 Vm 方法的兼容性检查。独立对象存储检查见 [objects/tests](../../objects/tests/README.md)。堆内部伪造句柄与计数溢出检查在 objects 的 src/heap.rs,公开示例由 src/lib.rs rustdoc 提供。文件职责见 [src](../src/README.md),检查方式和结果边界见 [验证](../../../docs/operations/validation.md)。本地不执行测试;存在测试文件不代表某次提交已通过。返回 [core](../README.md)。
+158
View File
@@ -329,3 +329,161 @@ fn failed_nominal_return_releases_value_and_captured_type_roots() {
vm.collect_cycles();
assert_eq!(vm.object_count(), 0);
}
#[derive(Debug)]
struct CountedNative(endor_core::ObjectRef);
impl endor_core::NativeCallable for CountedNative {
fn arity(&self) -> Option<endor_core::CallArity> {
Some(endor_core::CallArity::range(1, 2))
}
fn invoke(
&self,
cx: &mut endor_core::NativeContext<'_>,
_: &[Value],
) -> Result<endor_core::NativeResult, VmError> {
cx.set_attr(self.0, "called", Value::Bool(true))?;
Ok(endor_core::NativeResult::Return(Value::Nil))
}
}
#[test]
fn native_arity_checks_precede_effects_and_traits_require_published_signatures() {
use endor_core::{CallArity, ContextNativeFunction, MethodRequirement, NativeFunction};
let mut vm = Vm::new().unwrap();
let roots = vm.bootstrap_types().unwrap();
let state = vm.new_object().unwrap();
vm.set_attr(state, "called", Value::Bool(false)).unwrap();
let native = vm
.allocate(ContextNativeFunction::new(
"counted",
CountedNative(state),
vec![state],
))
.unwrap();
assert_eq!(vm.call_arity(native).unwrap(), Some(CallArity::range(1, 2)));
assert!(matches!(
vm.call(native, &[]),
Err(VmError::ArgumentCount { .. })
));
assert_eq!(vm.get_attr(state, "called").unwrap(), Value::Bool(false));
let class = vm
.new_class(
"Data",
roots.object,
vec![],
[("run".into(), native)].into(),
)
.unwrap();
let capability = vm
.new_trait(
"Runnable",
vec![MethodRequirement {
name: "run".into(),
parameters: 0,
}],
)
.unwrap();
vm.implement_trait(class, capability).unwrap();
let Value::Object(instance) = vm.call(class, &[]).unwrap() else {
panic!("instance")
};
let Value::Object(bound) = vm.bind_attr(instance, "run").unwrap() else {
panic!("bound")
};
assert_eq!(vm.call_arity(bound).unwrap(), Some(CallArity::range(0, 1)));
assert_eq!(vm.call(bound, &[]).unwrap(), Value::Nil);
assert_eq!(vm.get_attr(state, "called").unwrap(), Value::Bool(true));
let unknown = vm
.allocate(NativeFunction::new("unknown", |_| Ok(Value::Nil)))
.unwrap();
assert_eq!(vm.call_arity(unknown).unwrap(), None);
assert!(vm.define_method(class, "run", unknown).is_err());
let wrong = vm
.allocate(NativeFunction::new("wrong", |_| Ok(Value::Nil)).with_arity(CallArity::exact(3)))
.unwrap();
assert!(vm.define_method(class, "run", wrong).is_err());
assert_eq!(
vm.object_store().lookup_method(class, "run").unwrap(),
Some(native)
);
let variadic = vm
.allocate(
NativeFunction::new("variadic", |_| Ok(Value::Nil))
.with_arity(CallArity::range(1, usize::MAX)),
)
.unwrap();
vm.define_method(class, "many", variadic).unwrap();
let Value::Object(variadic_bound) = vm.bind_attr(instance, "many").unwrap() else {
panic!("bound")
};
assert_eq!(
vm.call_arity(variadic_bound).unwrap(),
Some(CallArity::range(0, usize::MAX))
);
vm.release(variadic_bound).unwrap();
vm.release(variadic).unwrap();
let malformed = vm
.allocate(
NativeFunction::new("malformed", |_| panic!("invalid metadata must not execute"))
.with_arity(CallArity::range(2, 1)),
)
.unwrap();
assert!(vm.call_arity(malformed).is_err());
assert!(vm.call(malformed, &[]).is_err());
for object in [
roots.object,
roots.type_type,
state,
native,
class,
capability,
instance,
bound,
unknown,
wrong,
malformed,
] {
vm.release(object).unwrap();
}
vm.collect_cycles();
assert_eq!(vm.object_count(), 0);
}
#[test]
fn unified_arity_preserves_source_defaults_and_legacy_native_calls() {
use endor_core::{CallArity, CodeLayout, EndorCode, Instruction, NativeFunction, Opcode};
let mut vm = Vm::new().unwrap();
let globals = vm.new_dict().unwrap();
let code = EndorCode::from_instructions(
&[
Instruction::new(Opcode::LoadConst, 0),
Instruction::simple(Opcode::ReturnValue),
],
vec![Value::Nil],
CodeLayout {
parameters: vec!["value".into()],
locals: vec!["value".into()],
..CodeLayout::default()
},
)
.unwrap();
let code = vm.new_code(code).unwrap();
let function = vm
.new_function("optional", code, globals, vec![Value::Nil], vec![])
.unwrap();
assert_eq!(
vm.call_arity(function).unwrap(),
Some(CallArity::range(0, 1))
);
assert_eq!(vm.call(function, &[]).unwrap(), Value::Nil);
let legacy = vm
.allocate(NativeFunction::new("legacy", |_| Ok(Value::Nil)))
.unwrap();
assert_eq!(vm.call_arity(legacy).unwrap(), None);
assert_eq!(vm.call(legacy, &[]).unwrap(), Value::Nil);
for object in [globals, code, function, legacy] {
vm.release(object).unwrap();
}
vm.collect_cycles();
assert_eq!(vm.object_count(), 0);
}
+4 -4
View File
@@ -4,10 +4,10 @@
| 文件 | 职责 |
| --- | --- |
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement/SourceSignature/AnnotationType)与独立存储 rustdoc 示例 |
| [object.rs](object.rs) | Value、CompareOp、ObjectPayload(含显式函数标记)、ObjectHeader、EndorObject、Record、NativeFunction |
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式Trait定义/声明/方法参数约束、原生构造委托、声明环境托管边、字段构造/写入检查与注解解析/匹配 |
| [native.rs](native.rs) | NativeContext、上下文原生函数、作用域语言函数工厂、显式能力声明入口、函数标记、根与非递归委托调用 |
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement/SourceSignature/AnnotationType/CallArity)与独立存储 rustdoc 示例 |
| [object.rs](object.rs) | Value、CompareOp、ObjectPayload(含显式函数标记)、ObjectHeader、EndorObject、Record、可发布数量元数据的 NativeFunction |
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式Trait定义/声明/方法参数约束、原生构造委托、统一源码/原生/绑定方法参数区间查询与能力验证、声明环境托管边、字段构造/写入检查与注解解析/匹配 |
| [native.rs](native.rs) | NativeContext、CallArity 元数据与调用前数量检查、上下文原生函数、作用域语言函数工厂、显式能力声明入口、函数标记、根与非递归委托调用 |
| [changes.rs](changes.rs) | 合并变更批次、变更键与有界通知额度 |
| [number.rs](number.rs) | Integer/Decimal/Rational/BinaryFloat/Complex 的无损表示和装箱载荷 |
| [builtins.rs](builtins.rs) | String、Dict、Cell、Function、BaseException 载荷与 trace 边、语言函数标记 |
+1 -1
View File
@@ -35,7 +35,7 @@ pub use code::{CodeLayout, EndorCode, EndorCodeObject, SourceSignature};
pub use error::VmError;
pub use heap::{ObjectRef, WeakObjectRef};
pub use instruction::{DecodedInstruction, Instruction, Opcode};
pub use native::{ContextNativeFunction, NativeCallable, NativeContext, NativeResult};
pub use native::{CallArity, ContextNativeFunction, NativeCallable, NativeContext, NativeResult};
pub use number::{
EndorDecimal, EndorInteger, EndorNumber, EndorNumberObject, EndorRational, EndorReal,
};
+48
View File
@@ -13,7 +13,49 @@ pub enum NativeResult {
},
}
/// Accepted positional argument interval, including explicit receivers.
/// The store validates metadata before use; usize::MAX denotes an open upper bound.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CallArity {
pub required: usize,
pub maximum: usize,
}
impl CallArity {
pub const fn exact(count: usize) -> Self {
Self::range(count, count)
}
pub const fn range(required: usize, maximum: usize) -> Self {
Self { required, maximum }
}
pub fn accepts(self, count: usize) -> bool {
(self.required..=self.maximum).contains(&count)
}
pub(crate) fn validate(self) -> Result<Self, VmError> {
if self.required > self.maximum {
return Err(VmError::InvalidArgument("invalid callable arity metadata"));
}
Ok(self)
}
pub(crate) fn check(self, name: String, provided: usize) -> Result<(), VmError> {
self.validate()?;
if !self.accepts(provided) {
return Err(VmError::ArgumentCount {
name,
required: self.required,
maximum: self.maximum,
provided,
});
}
Ok(())
}
}
pub trait NativeCallable: fmt::Debug + Send + Sync + 'static {
/// Optional positional contract. Missing metadata remains an explicit legacy gap.
fn arity(&self) -> Option<CallArity> {
None
}
fn invoke(
&self,
context: &mut NativeContext<'_>,
@@ -149,6 +191,12 @@ impl<'a> NativeContext<'a> {
arguments: &[Value],
callable: Arc<dyn NativeCallable>,
) -> Result<NativeResult, VmError> {
if let Some(arity) = callable.arity() {
arity.check(
store.object(function)?.payload().kind_name().into(),
arguments.len(),
)?;
}
let mut context = Self {
store,
roots: Vec::new(),
+17
View File
@@ -117,6 +117,11 @@ pub trait ObjectPayload: Any + fmt::Debug + Send + Sync {
/// Host-call entry: arguments and raw results are borrowed handles. The VM
/// validates/retains the result before exposing it as an owned call result.
/// Language functions are dispatched by Vm's frame evaluator instead.
/// Positional metadata for native call protocols; source code is queried by the store.
fn call_arity(&self) -> Option<crate::CallArity> {
self.native_callable().and_then(|callable| callable.arity())
}
fn call(&self, _arguments: &[Value]) -> Result<Value, VmError> {
Err(VmError::UnsupportedOperation("call"))
}
@@ -176,6 +181,7 @@ pub type NativeCall = EndorNativeCall;
pub struct EndorNativeFunctionObject {
name: String,
function: NativeCall,
arity: Option<crate::CallArity>,
}
impl EndorNativeFunctionObject {
@@ -183,12 +189,17 @@ impl EndorNativeFunctionObject {
Self {
name: name.into(),
function,
arity: None,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn with_arity(mut self, arity: crate::CallArity) -> Self {
self.arity = Some(arity);
self
}
}
/// Compatibility name for existing embedders.
@@ -213,7 +224,13 @@ impl ObjectPayload for EndorNativeFunctionObject {
Ok(format!("<native-function {}>", self.name))
}
fn call_arity(&self) -> Option<crate::CallArity> {
self.arity
}
fn call(&self, arguments: &[Value]) -> Result<Value, VmError> {
if let Some(arity) = self.arity {
arity.check(self.name.clone(), arguments.len())?;
}
(self.function)(arguments)
}
}
+59 -8
View File
@@ -434,6 +434,58 @@ impl ObjectStore {
}
}
/// Unified source/native positional interval, after any bound receivers.
/// None means no published native metadata (including generic Type construction).
pub fn call_arity(&self, function: ObjectRef) -> Result<Option<crate::CallArity>, VmError> {
let mut current = function;
let mut receivers = 0usize;
loop {
let payload = self.object(current)?.payload();
if let Some(bound) = payload.as_any().downcast_ref::<BoundMethod>() {
receivers = receivers
.checked_add(1)
.ok_or(VmError::ExecutionLimit("bound receivers"))?;
current = bound.function;
continue;
}
let arity = if let Some(function) = payload
.as_any()
.downcast_ref::<crate::EndorFunctionObject>()
{
let maximum = self
.payload::<crate::EndorCodeObject>(function.code(), "code")?
.code()
.parameters()
.len();
Some(crate::CallArity::range(
maximum - function.defaults().len(),
maximum,
))
} else {
payload.call_arity()
};
return arity
.map(|arity| {
let arity = arity.validate()?;
let maximum = if arity.maximum == usize::MAX {
usize::MAX
} else {
arity
.maximum
.checked_sub(receivers)
.ok_or(VmError::InvalidArgument(
"method receiver exceeds parameter count",
))?
};
Ok(crate::CallArity::range(
arity.required.saturating_sub(receivers),
maximum,
))
})
.transpose();
}
}
/// Returns one owned root, including for a newly bound inherited method.
pub fn bind_attr(&mut self, receiver: ObjectRef, name: &str) -> Result<Value, VmError> {
match self.lookup_attr(receiver, name)? {
@@ -641,14 +693,13 @@ impl ObjectStore {
reason,
};
let function = function.ok_or_else(|| invalid("required method is missing"))?;
if let Some(count) = self.positional_parameter_count(function)? {
if count != requirement.parameters + 1 {
return Err(invalid(
"positional parameter count does not match (including self)",
));
}
} else if self.object(function)?.payload().native_callable().is_none() {
return Err(invalid("required member is not callable"));
let arity = self
.call_arity(function)?
.ok_or_else(|| invalid("required member has no positional signature"))?;
if !arity.accepts(requirement.parameters + 1) {
return Err(invalid(
"positional parameter count does not match (including self)",
));
}
Ok(())
}
+1 -1
View File
@@ -5,7 +5,7 @@
| 引擎与宿主边界 | [架构](architecture/README.md) | [crates](../crates/README.md) |
| 解释执行、资源修订与引擎升级 | [执行与版本](architecture/execution-and-versions.md) | frontend/compiler/core |
| EndorObject、句柄、默认操作 | [对象模型](runtime/objects.md) | objects/src |
| 原生上下文、只读调用、子调用读取依赖、委托与变更通知 | [原生调用](runtime/native-calls.md) | objects/src/native.rs、changes.rs、core/src/eval.rs |
| 原生上下文、只读调用、子调用读取依赖、统一参数数量、委托与变更通知 | [原生调用](runtime/native-calls.md) | objects/src/native.rs、changes.rs、core/src/eval.rs |
| 托管类型、宿主类注册、原生分配继承、字段存储、显式能力、函数分类、声明字段与可选源码注解检查 | [名义类型](runtime/types.md) | objects/src/types.rs |
| Number、String、Dict、Cell、Function | [基本对象](runtime/builtins.md) | objects/src |
| 源码读取与来源 | [Reader](runtime/reader.md) | frontend/src/reader.rs |
+1 -1
View File
@@ -21,7 +21,7 @@ Aster 是可嵌入引擎,只保留 objects、core、frontend、compiler。Obje
## 当前缺口
平台资源版本/依赖锁、源码闭包、数值算术、完整异常传播、内存配额、可靠取消、不可信代码沙箱、自动 GC 和宿主 panic 后执行根恢复尚未完成。源码函数参数/返回注解已支持默认关闭的运行时检查,Tessera 显式启用;普通名称按调用时函数全局解析,声明字段构造/写入检查已接入同一可选开关;完整初始化、静态类型/效果与原生统一签名仍未完成,见[类型机制](../runtime/types.md#源码函数注解检查)。不引入 Python 元类。UI 接入适配由 Tessera 实现。通用 ObjectPayload::assign_from 允许宿主对象选择共享引用赋值;协议和根计数归 Aster,UI 挂载、布局、绘制仍归 Tessera,见字节码 VM 的共享引用章节。
平台资源版本/依赖锁、源码闭包、数值算术、完整异常传播、内存配额、可靠取消、不可信代码沙箱、自动 GC 和宿主 panic 后执行根恢复尚未完成。源码函数参数/返回注解已支持默认关闭的运行时检查,Tessera 显式启用;普通名称按调用时函数全局解析,声明字段构造/写入检查已接入同一可选开关;完整初始化、静态类型/效果与原生参数/返回类型签名仍未完成;统一位置参数数量已接入原生调用与显式能力检查,见[类型机制](../runtime/types.md#源码函数注解检查)。不引入 Python 元类。UI 接入适配由 Tessera 实现。通用 ObjectPayload::assign_from 允许宿主对象选择共享引用赋值;协议和根计数归 Aster,UI 挂载、布局、绘制仍归 Tessera,见字节码 VM 的共享引用章节。
## 工作约定
+1 -1
View File
@@ -4,7 +4,7 @@
| --- | --- |
| [types.md](types.md) | 托管 Object/Type、名义继承、声明字段/函数注解检查、构造与类型边生命周期 |
| [objects.md](objects.md) | 对象、句柄与默认操作 |
| [native-calls.md](native-calls.md) | 原生上下文、非递归委托、弱引用、托管赋值与变更批次 |
| [native-calls.md](native-calls.md) | 原生上下文、统一参数数量、非递归委托、弱引用、托管赋值与变更批次 |
| [builtins.md](builtins.md) | Number、String、Dict、Cell、Function、异常数据 |
| [reader.md](reader.md) | 输入、来源和修订标签 |
| [frontend.md](frontend.md) | 词法、语法和诊断 |
+2
View File
@@ -136,3 +136,5 @@ Endor 当前用 `EndorBaseExceptionObject` 保存公共数据,名称只是错
## 验证入口
独立存储行为在 [objects/tests](../../crates/objects/tests/README.md) 检查;既有 Vm 对象入口在 [core/tests](../../crates/core/tests/README.md) 的 builtins.rs 检查,执行行为在 execution.rs;验证方式见 [验证](../operations/validation.md)。文档描述当前契约,不代表某次 CI 已通过。CPython 284 项研究表保持为工作区外的研究材料,不纳入项目文档或 Git。
原生函数可通过 NativeFunction::with_arity 提供位置参数数量;源码、原生、绑定方法的统一查询与兼容边界见[原生调用](native-calls.md#统一位置参数数量)。
+13
View File
@@ -66,3 +66,16 @@ take_changes 按对象与变更键合并同一批写入,返回最终版本。
远端回归用例见 [native_context.rs](../../crates/core/tests/native_context.rs);本地不执行测试。
声明字段检查启用时,NativeContext::set_attr 使用同一字段写入屏障。NativeTyped 分配器应在副作用前调用 ObjectStore::check_class_field_arguments 检查自己的字段参数切片,具体参数组合仍由宿主定义;配置、声明环境与生命周期的唯一契约见[名义类型](types.md#声明字段注解检查)。
## 统一位置参数数量
`CallArity { required, maximum }` 表示接受的位置参数区间,包含未绑定的显式接收者;`exact(n)` 与 `range(min,max)` 提供构造方式,`accepts(n)` 查询接受性。`usize::MAX` 表示开放上界。公开字段不能绕过使用时校验:required 大于 maximum 时,查询/调用报告 InvalidArgument,不能进入实现。此元数据只描述数量,不描述参数类型、返回类型或效果。
上下文原生实现通过 `NativeCallable::arity()` 提供 `Some(CallArity)`;默认 None 明确表示旧实现没有元数据。ObjectPayload::call_arity 默认委托该入口,也供直接 call 协议发布元数据;传统 NativeFunction 可使用 `with_arity(...)`。VM 在进入原生实现前检查数量,NativeContext::execute 也检查其 NativeCallable 元数据,失败不调用业务实现。传统无元数据的普通原生调用仍保留原协议,不伪造精确签名。
`ObjectStore::call_arity(function)`/`Vm::call_arity(function)` 统一查询源码与原生函数。源码从真实形参和默认值计算区间;绑定方法沿实际函数扣除每层接收者,最大值不足时报错,最小值饱和到零。查询返回无托管引用的复制数据,不增加 GC 根。旧 positional_parameter_count 保留“源码总形参数量减绑定接收者”的兼容语义,不用于新的原生回调分派。通用 Type 构造器的参数组合由宿主决定,返回 None;NativeTyped 内部委托和宿主实际构造仍各自检查。
显式 Trait 声明、源码派生及方法表更新使用统一区间检查“规范参数数量加 self”是否被接受;缺少元数据的原生方法不能再登记为能力实现。可选参数允许实现接受规范调用数量之外的额外形式。更新仍先检查后代再修改,无隐藏类型根。类型注解兼容性、方差、参数名称及效果检查尚未接入此结构。
Tessera 为原生方法、回调和内部视觉 continuation 发布数量元数据,事件监听要求接受零参数或一个 Event;两者都接受时优先传 Event。绑定计算和 Task 回调要求接受零参数,登记前拒绝无元数据/不兼容函数;普通旧原生调用的兼容性不等于允许其参加这些严格回调契约。
+2 -2
View File
@@ -72,7 +72,7 @@ ObjectPayload 可选择实现 `attribute_target(store)`,把实例字段放在
implement_trait(Type, Trait)明确建立能力关系并校验必需方法。只有同名成员不算实现。implements_trait(Type, Trait)检查声明和继承;supports(instance, Trait)先查实际类型;未分类对象返回false,非法句柄或非定义参数报错。一个类型可显式实现多个能力,派生类继承关系。声明由Type的受管强边持有,查询和临时后代扫描不保活定义。
源码函数必须具有要求数量加self的参数;原生可调用对象的签名由注册宿主保证,当前没有原生形参元数据。缺少方法、非可调用成员或参数数量错误以TraitViolation报告能力名、方法名和原因。本子集不验证注解、关联类型、返回类型、效果或可选形参的替换兼容性;没有默认实现和trait源码语法。
源码与原生方法通过统一 CallArity 验证是否接受规范数量加 self;无元数据的原生方法不能登记能力。数量区间包括源码默认参数与原生可选参数,详见[原生调用](native-calls.md#统一位置参数数量)。缺少方法、非可调用成员或参数数量错误以TraitViolation报告能力名、方法名和原因。本子集只验证数量接受性,不验证注解、关联类型、返回类型、效果或完整替换兼容性;没有默认实现和trait源码语法。
建立声明前检查已有后代;构造源码子类检查继承的所有能力。define_method更新前检查受影响后代的显式/继承契约,被后代自己的覆盖遮蔽的方法不受该祖先更新影响。失败保留原方法表和关系。内部扫描只枚举当前借用句柄,不引入全局强类型注册表。声明更新通过普通堆写屏障与trace进行,既有类型的只读作用域内变更会被拒绝。NativeContext也可使用同一implement_trait入口,仍受该屏障约束。
@@ -96,7 +96,7 @@ implement_trait(Type, Trait)明确建立能力关系并校验必需方法。只
| Type/Trait | 真实 Type/Trait 定义载荷 |
| 其他名称 | 从函数自己的 globals、其次创建时捕获的 builtins 找到实际 Type 或 Trait;前者检查名义实例/继承,后者检查显式能力 |
普通名称每次调用重新解析,不是静态推断或定义时冻结的类型绑定。同名新定义不等于旧定义;自由函数可观察后续全局类型名重绑定。方法注解中指向自身声明类的名称,优先从实际接收者祖先中查找仍持有该函数的方法定义,因此 REPL 重定义全局类名后,原实例保存的绑定方法仍可调用。其他类型名仍遵循上述全局解析规则。此机制不提供重写方差、泛型、效果类型或原生函数的统一签名元数据。字段赋值检查见下文。
普通名称每次调用重新解析,不是静态推断或定义时冻结的类型绑定。同名新定义不等于旧定义;自由函数可观察后续全局类型名重绑定。方法注解中指向自身声明类的名称,优先从实际接收者祖先中查找仍持有该函数的方法定义,因此 REPL 重定义全局类名后,原实例保存的绑定方法仍可调用。其他类型名仍遵循上述全局解析规则。此机制不提供重写方差、泛型、效果类型或原生参数/返回类型与效果签名;位置参数数量元数据见[原生调用](native-calls.md#统一位置参数数量)。字段赋值检查见下文。
源码与原生函数的统一名义分类由 `ObjectStore::set_function_type`/`Vm::set_function_type` 显式配置。配置验证 Type、拒绝只读修改,只保留弱引用且不可改成另一个定义;设置后新分配的显式 `ObjectPayload::is_function` 载荷自动附加该类型,已有对象不追溯改写。语言函数、两类原生函数及绑定方法提供此标记;仅有 call 能力不算函数。配置类型过期则函数分配报错并清理新对象,不引入隐藏 GC 根。宿主应在创建函数前配置,并通过正常对象边保活定义。