Files
endorphin-aster-vm/docs/runtime/native-calls.md
T

89 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 原生对象调用、弱引用与变更记录
本页定义宿主扩展使用的通用接口,不引入渲染或窗口语义。托管名义类型见[类型机制](types.md)。
## 有上下文的调用
旧 ObjectPayload::call 保留借用参数和借用结果协议。新载荷可通过 native_callable 返回
Arc<dyn NativeCallable>;VM 先取得独立分派器,再可变访问 ObjectStore,避免同时借用
堆中载荷与可变对象存储。ContextNativeFunction 提供名称、分派器和不可变捕获边列表。
捕获中的 ObjectRef 必须全部在 captures 中登记,未登记句柄不能保活对象。
NativeCallable::invoke 接收 NativeContext 和借用参数。context 提供对象、字典、Cell
分配、托管属性和容器写入、载荷事务替换及弱引用升级。store() 仅暴露只读访问;
callee() 返回当前可调用对象的借用句柄;attach_type 通过受校验的类型边事务附加实例类型。
new_function(name, code, globals) 可在当前作用域创建无默认参数、无闭包的语言函数;代码与 globals 使用现有 ObjectStore 校验,函数临时根随作用域管理。宿主可生成受静态验证的字节码来顺序调用多个处理器,再通过 Invoke 移交同一帧循环;这些调用继续共用当前预算,不逐个重置限额。
没有不受控的 &mut Vm、手动释放根或重入执行器入口。
context 内分配或升级得到的对象由本次作用域临时持有。NativeResult::Return(value)
返回借用结果;作用域为结果额外持有一份根,再释放其余临时根。出错或 unwind 也会
清理本作用域根,但此前完成的业务写入不会回滚;这不承诺整个执行帧的 panic 恢复。
NativeResult::Invoke { function, arguments } 将调用委托给另一个原生或语言函数。
function 和每个对象参数在移交前取得根;VM 完成调用准备后释放这些临时输入。
委托通过原有帧循环推进,不递归调用 Vm::call;语言函数使用原有帧限额。每次原生
分派计入同一指令预算,避免无界原生委托链。预算仍不能抢占一次长时间原生操作。
委托函数的结果就是该次调用结果,当前不提供任意原生调用后续 continuation。
## 只读调用与读取依赖
Vm::call_read_only_with_limits 沿用显式帧与预算,返回拥有式结果及借用式读取对象身份列表。进入时现存对象的属性、载荷、容器、Cell、类型边与共享赋值写入均在 prepare_change 屏障前拒绝;调用内新分配的结果图可以正常构造。局部变量对普通引用的重新绑定仍可进行:已知无可变 legacy assign_from 的载荷声明 reference_assignment_only=true,共享 assignment_cell 写入仍先检查屏障;未知宿主载荷默认保守拒绝。作用域正常返回或报错后恢复;这不提供 panic 恢复或外部 I/O 沙箱。
ObjectStore 的 begin_read_only/end_read_only 提供同样的 LIFO 嵌套机制,is_read_only 供宿主协议检查。保护集合与读取集合不持有 GC 根,代际句柄避免槽位复用误判。读取是对象粒度;宿主须把借用身份转为版本依赖并验证存活,不能据此保活整个对象图。
ObjectStore::begin_read_tracking/end_read_tracking 可在当前只读作用域中登记单次子调用的对象读取区域。区域按 LIFO 嵌套,重复读取仍分别进入每个活动区域,不清空整个调用的读取集合;返回身份仍是借用,不保活对象。NativeContext 可经 store() 使用此接口。没有只读作用域或关闭顺序不匹配时返回错误。外层只读调用退出时丢弃未结束区域,因此源码子调用出错不会把区域泄漏到下一次调用。
只读结果的构造仍维护自身变更版本,但不推送应用 ChangeBatch 通知;现有对象不会在此模式成功写入,因此没有遗漏已有观察者的修改。通过普通调用保存结果到旧对象时,保存动作仍照常通知。可信 Rust 的内部可变状态、外部资源与 I/O 需要宿主遵守只读契约;本机制只强制堆的公开变更入口。
## 托管共享引用赋值
assignment_cell(source, store) 是与原生 assign_from 并存的可选协议。实现先只读验证
目标及组合环,返回自身持有的内建 Cell;对象存储再事务性地将 source 写入 Cell。
修改的引用属于可追踪堆边,旧对象身份与方法不变。默认返回 None,沿用既有赋值规则。
Cell 的持有边也必须通过 trace 或托管字段登记;字典 API 和属性写入不隐式执行此协议。
## 弱引用
downgrade 验证对象后产生 WeakObjectRef,不增加强引用。upgrade 在同一个对象存储中
重新验证堆、槽位与代次;存活时返回一份新的拥有式根,过期返回 None,其他存储返回
ForeignHandle。释放后复用槽位不会使旧弱引用指向新对象。弱引用不保活 VM 本身。
宿主使用升级结果后须 release;NativeContext::upgrade 的升级根归当前调用作用域。
## 对象变更
每个对象头有从 0 开始的 change_version。成功的属性写入或删除产生 Attribute(name),
字典、Cell、通用载荷替换及共享引用赋值产生 Contents;失败写入不产生变更。
同值写入仍视为成功写入,消费方可自行比较派生结果。版本耗尽拒绝修改。
take_changes 按对象与变更键合并同一批写入,返回最终版本。记录不持有对象,消费时
对象可能已经回收,应重新验证身份。通知只保证修改信息,不包含旧值或回滚日志。
最多保留 4096 个不同变更键,超额后清空细项并设置 rescan_required,直到下一次提取;
消费方必须完整重查依赖,不能把溢出当成没有变化。版本仍继续递增。
引擎只记录通用对象变化,依赖订阅、视觉失效和事件提交由宿主负责。托管原生状态须
通过 replace_payload、属性或容器事务更新;载荷内部自行变动不会自动通知。
远端回归用例见 [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 回调要求接受零参数,登记前拒绝无元数据/不兼容函数;普通旧原生调用的兼容性不等于允许其参加这些严格回调契约。
## 校验读取与程序内容读取
只读子区域新增 `ObjectStore::end_read_tracking_details(token) -> Vec<ReadAccess>`,每项仍是借用 ObjectRef,content_read 表示是否存在普通内容读取。matches_annotation 内部的名义/基础值检查标为校验读取;同一区域随后发生普通读取时,content_read 合并为 true。嵌套区域均保留两类读取,不增加对象根。原 end_read_tracking 与完整 end_read_only 仍返回两类读取的完整并集,没有删除依赖或放宽只读写屏障。
校验分类按线程记录嵌套深度,作用域退出、错误和展开时通过 RAII 恢复,不让另一线程继承标签。公开 ReadAccess 仅提供信息,不保证调用者可忽略任何对象;动态类型/能力/全局定义等校验依赖仍须参与失效。Tessera 独立验证每次活动 Mount/归属/方法后,仅跳过该 Mount 的纯参数类型校验读取;程序实际读取 Mount 内容仍禁止复用测量缓存。其他读取继续记录版本。详见 core/tests/native_context.rs 的合并、完整依赖并集与错误恢复回归源码,仅编译未本地执行。