docs(objects): explain method binding and capability construction

This commit is contained in:
2026-11-15 17:38:31 +08:00
parent 7a40e85b94
commit 2e48e7455c
+85 -2
View File
@@ -512,14 +512,18 @@ impl ObjectStore {
Ok(class)
}
/// Store/VM/能力检查沿继承链查最近同名方法,返回借用函数,不自动绑定接收者。
pub fn lookup_method(
&self,
type_object: ObjectRef,
name: &str,
) -> Result<Option<ObjectRef>, VmError> {
// 从类型自身向父链查找,最近声明优先。
let mut current = Some(type_object);
// 基类固定于创建,逐层校验类型句柄而不是按名称检索。
while let Some(type_object) = current {
let definition = self.payload::<EndorTypeObject>(type_object, "Type")?;
// 找到方法即返回借用身份,不替调用方 retain。
if let Some(function) = definition.methods.get(name) {
return Ok(Some(*function));
}
@@ -530,15 +534,22 @@ impl ObjectStore {
/// Source positional parameters after receiver binding. None means the
/// callable uses a native protocol without a source signature.
/// 旧式能力检查/宿主查询源码参数总数,扣除所有绑定接收者;原生协议返回 None。
pub fn positional_parameter_count(
&self,
function: ObjectRef,
) -> Result<Option<usize>, VmError> {
// 从传入可调用对象开始逐层展开 BoundMethod。
let mut current = function;
// 累计已绑定接收者槽数,不能只扣一层。
let mut receivers = 0usize;
// 按链迭代而非递归,未知原生元数据保持 None。
loop {
// 通过 object 验证句柄并记录读取,随后仅借用载荷元数据。
let payload = self.object(current)?.payload();
// 每层绑定都额外供应一个接收者,继续查询真实目标函数。
if let Some(bound) = payload.as_any().downcast_ref::<BoundMethod>() {
// 计数使用 checked_add,防极深包装使参数扣减绕回。
receivers = receivers
.checked_add(1)
.ok_or(VmError::ExecutionLimit("bound receivers"))?;
@@ -547,6 +558,7 @@ impl ObjectStore {
.as_any()
.downcast_ref::<crate::EndorFunctionObject>()
{
// 源码参数列表包含显式 self,默认值不改变总参数槽数。
let count = self
.payload::<crate::EndorCodeObject>(function.code(), "code")?
.code()
@@ -564,16 +576,21 @@ impl ObjectStore {
}
}
/// Validated direct native metadata. Bound methods delegate to their actual
/// target, so receiver slots remain explicit here. Returned handles are borrowed.
/// 查询直接原生元数据;完整签名保留显式接收者,返回类型句柄是借用。
/// BoundMethod 本身没有完整签名,参数区间查询通过 call_arity 另行解包。
/// 原生调用/契约分析查询当前载荷完整签名并校验;不会在这里展开 BoundMethod。
pub fn native_signature(
&self,
function: ObjectRef,
) -> Result<Option<crate::CallSignature>, VmError> {
// 通过 object 验证句柄并记录读取,随后仅借用载荷元数据。
let payload = self.object(function)?.payload();
// 查询当前载荷提供的签名快照,不执行回调,也不新增定义根。
let signature = payload.call_signature();
// 存在完整签名才校验全部类型边及数量元数据一致性。
if let Some(signature) = &signature {
signature.validate(self)?;
// 载荷若同时发布 arity,必须与完整签名完全一致。
if payload
.call_arity()
.is_some_and(|arity| arity != signature.arity)
@@ -588,28 +605,38 @@ impl ObjectStore {
/// native wrappers and source call graphs with unproved operations remain unknown.
/// This query follows current globals, closure Cells and known receivers without
/// executing code, freezing targets or retaining a cross-query cache.
/// VM/契约通过 effects 分析当前调用图快照,None 表示无法证明整体效果。
pub fn call_effect(&self, function: ObjectRef) -> Result<Option<crate::CallEffect>, VmError> {
self.infer_call_effect(function)
}
/// Unified source/native positional interval, after any bound receivers.
/// None means no published native metadata (including generic Type construction).
/// VM 数量检查统一查询源码/原生区间,先解绑定方法再扣除接收者。
pub fn call_arity(&self, function: ObjectRef) -> Result<Option<crate::CallArity>, VmError> {
// 从传入可调用对象开始逐层展开 BoundMethod。
let mut current = function;
// 累计已绑定接收者槽数,不能只扣一层。
let mut receivers = 0usize;
// 按链迭代而非递归,未知原生元数据保持 None。
loop {
// 通过 object 验证句柄并记录读取,随后仅借用载荷元数据。
let payload = self.object(current)?.payload();
// 每层绑定都额外供应一个接收者,继续查询真实目标函数。
if let Some(bound) = payload.as_any().downcast_ref::<BoundMethod>() {
// 计数使用 checked_add,防极深包装使参数扣减绕回。
receivers = receivers
.checked_add(1)
.ok_or(VmError::ExecutionLimit("bound receivers"))?;
current = bound.function;
continue;
}
// 源码区间由总参数数减默认值数;原生优先完整签名,否则兼容独立 arity。
let arity = if let Some(function) = payload
.as_any()
.downcast_ref::<crate::EndorFunctionObject>()
{
// 源码代码布局已经校验,函数 defaults 数量也经构造校验。
let maximum = self
.payload::<crate::EndorCodeObject>(function.code(), "code")?
.code()
@@ -626,7 +653,9 @@ impl ObjectStore {
};
return arity
.map(|arity| {
// 先校验原始区间,再调整已绑定接收者造成的可见数量。
let arity = arity.validate()?;
// 开放上界保持 usize::MAX,有限上界 checked_sub 防绑定超出参数容量。
let maximum = if arity.maximum == usize::MAX {
usize::MAX
} else {
@@ -647,9 +676,12 @@ impl ObjectStore {
}
/// Returns one owned root, including for a newly bound inherited method.
/// eval LoadAttr/宿主获取可用属性;对象值 retain 一根,方法则分配拥有接收者和函数的绑定对象。
pub fn bind_attr(&mut self, receiver: ObjectRef, name: &str) -> Result<Value, VmError> {
// 借用查找与拥有结果分开:返回到执行栈的对象必须先取得根。
match self.lookup_attr(receiver, name)? {
AttributeLookup::Value(value) => {
// 标量由 Rust 值直接持有,仅对象属性结果需要 retain。
if let Value::Object(object) = value {
self.retain(object)?;
}
@@ -661,43 +693,54 @@ impl ObjectStore {
}
}
/// Store get_attr/bind_attr 与效果分析共用借用查找,不分配 BoundMethod 或创建结果根。
pub(crate) fn lookup_attr(
&self,
receiver: ObjectRef,
name: &str,
) -> Result<AttributeLookup, VmError> {
// 保留最初 receiver 身份,用于名义元数据、能力遮蔽检查与方法绑定。
let object = self.object(receiver)?;
// 已附着类型优先作为保留属性暴露,返回借用类型句柄。
if name == "type"
&& let Some(type_object) = self.type_of(receiver)?
{
return Ok(AttributeLookup::Value(Value::Object(type_object)));
}
// 能力名称元数据优先于普通字段,但仍受方法遮蔽保护。
if name == "name"
&& let Some(capability) = object.payload().as_any().downcast_ref::<EndorTraitObject>()
{
self.check_typed_method_write(receiver, name)?;
return Ok(AttributeLookup::Value(Value::Text(capability.name.clone())));
}
// 类型对象先处理自身 name/base 元数据和类方法表,不绑定为实例方法。
if let Some(definition) = object.payload().as_any().downcast_ref::<EndorTypeObject>() {
// 根类型 base 为 Nil,与没有这个元数据属性不同。
let metadata = match name {
"name" => Some(Value::Text(definition.name.clone())),
"base" => Some(definition.base.map(Value::Object).unwrap_or(Value::Nil)),
_ => None,
};
// 元数据返回前核对类型化能力方法不能被同名属性遮蔽。
if let Some(value) = metadata {
self.check_typed_method_write(receiver, name)?;
return Ok(AttributeLookup::Value(value));
}
// 直接从类型对象取方法返回函数值,不绑定该类型为 self。
if let Some(function) = self.lookup_method(receiver, name)? {
self.check_typed_method_write(receiver, name)?;
return Ok(AttributeLookup::Value(Value::Object(function)));
}
}
// 普通字段沿 attribute_target 找最终存储,成功路径必非空。
let storage = *self
.attribute_path(receiver)?
.last()
.expect("nonempty attribute path");
// 存储缺字段/不允许属性时还可尝试实例类型方法;其他错误直接传播。
match self.heap.get_attr(storage, name) {
// 字段命中仍检查能力方法遮蔽,然后返回借用值。
Ok(value) => {
self.check_typed_method_write(receiver, name)?;
return Ok(AttributeLookup::Value(value));
@@ -705,7 +748,9 @@ impl ObjectStore {
Err(VmError::AttributeNotFound(_) | VmError::UnsupportedOperation("attributes")) => {}
Err(error) => return Err(error),
}
// 根据接收者有效类型查实例方法,绑定的是原始 receiver 身份。
if let Some(type_object) = self.type_of(receiver)? {
// 实例方法返回函数/接收者对,由 bind_attr 决定是否分配绑定对象。
if let Some(function) = self.lookup_method(type_object, name)? {
return Ok(AttributeLookup::Method { function, receiver });
}
@@ -717,6 +762,7 @@ impl ObjectStore {
}
}
/// 内部借用查找结果;Method 延迟绑定,使只读分析不必分配 BoundMethod。
pub(crate) enum AttributeLookup {
Value(Value),
Method {
@@ -726,27 +772,36 @@ pub(crate) enum AttributeLookup {
}
#[derive(Debug)]
/// 持有函数和原接收者的托管包装,由 bind_attr 分配一份外部根。
struct BoundMethod {
// 原始接收者强边,不一定是最终属性存储对象。
receiver: ObjectRef,
// 底层函数强边,委托调用时由 NativeContext 取得输出根。
function: ObjectRef,
}
impl ObjectPayload for BoundMethod {
/// 绑定方法按函数分类,可在 Store 分配时附着 Function 类型。
fn is_function(&self) -> bool {
true
}
/// 绑定方法/能力定义不实现原地赋值,允许只读局部重绑定引用。
fn reference_assignment_only(&self) -> bool {
true
}
/// Store 的载荷下转型接口,不是语言名义类型判断。
fn as_any(&self) -> &dyn std::any::Any {
self
}
/// 错误/诊断载荷类别。
fn kind_name(&self) -> &'static str {
"bound-method"
}
/// Heap 分配/更新载荷时枚举拥有边,重复边仍逐条报告。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
visit(self.receiver);
visit(self.function);
}
/// VM 取得绑定方法分派器,调用时由上下文保护其载荷边。
fn native_callable(&self) -> Option<Arc<dyn NativeCallable>> {
Some(Arc::new(CallBoundMethod))
}
@@ -755,14 +810,17 @@ impl ObjectPayload for BoundMethod {
#[derive(Debug)]
struct CallBoundMethod;
impl NativeCallable for CallBoundMethod {
/// VM 分派绑定方法,把接收者插在显式参数前并委托底层函数,不递归运行执行器。
fn invoke(
&self,
cx: &mut NativeContext<'_>,
arguments: &[Value],
) -> Result<NativeResult, VmError> {
// 绑定载荷中的强边由上下文保护,回调只借用其元数据。
let bound = cx
.store()
.payload::<BoundMethod>(cx.callee(), "bound method")?;
// 新参数向量先放 self 再放调用方参数,clone 本身不 retain。
let mut values = Vec::with_capacity(arguments.len() + 1);
values.push(Value::Object(bound.receiver));
values.extend_from_slice(arguments);
@@ -787,56 +845,73 @@ pub struct TypedMethodRequirement {
pub signature: crate::CallSignature,
}
#[derive(Debug)]
/// 显式能力定义:最小数量要求与可选完整签名同属托管载荷,定义本身不可任意替换。
pub struct EndorTraitObject {
name: String,
requirements: Vec<MethodRequirement>,
signatures: BTreeMap<String, crate::CallSignature>,
}
impl EndorTraitObject {
/// 宿主/诊断借用能力名称,能力相等仍按身份。
pub fn name(&self) -> &str {
&self.name
}
/// 能力校验借用最小位置参数要求列表,不含绑定 self。
pub fn requirements(&self) -> &[MethodRequirement] {
&self.requirements
}
/// 契约校验借用指定方法完整签名,含显式 self,未声明返回 None。
pub fn signature(&self, name: &str) -> Option<&crate::CallSignature> {
self.signatures.get(name)
}
}
impl ObjectPayload for EndorTraitObject {
/// Heap 分配/更新载荷时枚举拥有边,重复边仍逐条报告。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
// 能力完整签名的全部类型定义参与 trace,不只看当前调用到的分支。
for signature in self.signatures.values() {
// 每个引用都是拥有边,不能按身份去重。
for reference in signature.references() {
visit(reference);
}
}
}
/// 绑定方法/能力定义不实现原地赋值,允许只读局部重绑定引用。
fn reference_assignment_only(&self) -> bool {
true
}
/// Store 的载荷下转型接口,不是语言名义类型判断。
fn as_any(&self) -> &dyn std::any::Any {
self
}
/// 错误/诊断载荷类别。
fn kind_name(&self) -> &'static str {
"trait"
}
/// Store 显示能力名称,不执行方法或展开签名。
fn repr(&self, _: ObjectRef) -> Result<String, VmError> {
Ok(format!("<trait {}>", self.name))
}
}
impl ObjectStore {
/// VM/宿主创建只约束方法存在与参数数量的能力,返回一份外部根。
pub fn new_trait(
&mut self,
name: impl Into<String>,
requirements: Vec<MethodRequirement>,
) -> Result<ObjectRef, VmError> {
// 持有能力名称,禁止空名称但不把名称作为名义身份。
let name = name.into();
// 只约束列表内部去重,避免重复方法要求。
let mut names = std::collections::BTreeSet::new();
// 能力名称必须非空。
if name.is_empty() {
return Err(VmError::InvalidArgument("trait name must not be empty"));
}
// 最小能力要求不含 self,usize::MAX 无法再加 self,提前拒绝。
for requirement in &requirements {
// 拒绝空/保留名、无法加接收者的数量与重复要求。
if requirement.name.is_empty()
|| requirement.name == "type"
|| requirement.parameters == usize::MAX
@@ -854,20 +929,28 @@ impl ObjectStore {
})
}
/// VM/宿主创建显式参数/返回/效果能力,要求含 self 的固定规范签名。
pub fn new_typed_trait(
&mut self,
name: impl Into<String>,
methods: Vec<TypedMethodRequirement>,
) -> Result<ObjectRef, VmError> {
// 持有能力名称,禁止空名称但不把名称作为名义身份。
let name = name.into();
// 能力名称必须非空。
if name.is_empty() {
return Err(VmError::InvalidArgument("trait name must not be empty"));
}
// 完整方法签名按名保存,同时派生兼容的最小参数要求。
let mut signatures = BTreeMap::new();
// 每份规范完整签名生成一个不含 self 的参数数量要求。
let mut requirements = Vec::new();
// 全部元数据验证完后才分配能力,失败不产生半成品。
for method in methods {
method.signature.validate(self)?;
// 类型化能力使用固定且至少含一接收者的规范签名,不接受可选接收者。
let arity = method.signature.arity;
// 数量必须 required==maximum 且有限,方法名也不能重复或占用 type。
if method.name.is_empty()
|| method.name == "type"
|| arity.required == 0