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

119 lines
17 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,不能进入实现。CallArity 本身只描述数量;可选的 CallSignature 在此基础上声明类型与宿主效果。
上下文原生实现通过 `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”是否被接受;缺少元数据的原生方法不能再登记为能力实现。可选参数允许实现接受规范调用数量之外的额外形式。更新仍先检查后代再修改,无隐藏类型根。旧 new_trait 保留数量规则;new_typed_trait 的参数/返回类型、方差及已知效果检查见类型主题,参数名称不构成调用契约。
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 的合并、完整依赖并集与错误恢复回归源码,仅编译未本地执行。
## 原生参数、返回类型与宿主效果
CallSignature 保存 CallArity、固定位置的 CallType 列表、可选 variadic 类型、returns 与 CallEffect。exact 提供无默认参数的便捷构造;可选参数仍通过 arity.required/maximum 表示,列表覆盖所有固定位置。开放上界必须有 variadic,有限上界未被固定列表覆盖的部分也必须有 variadic;错误区间、不完整列表、非 Type/Trait 的名义/能力引用和独立 arity 与签名不一致在调用前拒绝。名义类型引用是实际定义,不从字符串或调用者全局环境重新解析。
CallType::Single 封装一个现有 AnnotationType;Union 保存非空的平坦分支列表,nullable(T) 等价于 T | Unit。每个分支先完整校验,不能因较早的 Object/匹配分支而隐藏后续错误定义。匹配任一分支即接受,不匹配时报告按声明顺序组合的名称。全部分支的类型/能力引用都参加 trace 与委托返回执行根保活,包括未被本次值选中的分支;根取得中途出错会撤销已取得的根。exact 仍接受原来的单类型列表作便捷构造并转换为 Single。此能力属于原生宿主契约,没有增加 Endor 的联合类型语法。
NativeCallable::signature 默认 None,明确表示尚未发布类型/效果契约;默认 arity 从签名取得。ObjectPayload::call_signature 供自定义直接调用协议发布。ContextNativeFunction 在创建时保存不可变签名并在 trace 中自动登记每个类型/能力引用;其他捕获仍须显式传 captures。NativeFunction::with_signature 同样追踪签名边并设置数量。自定义载荷须在 trace 中登记自己的签名引用,元数据与其他载荷边一样禁止原地改变;更换使用 replace_payload。ObjectStore::native_signature 校验并返回借用描述,不创建隐藏强注册表。
已发布的原生签名总是检查,不依赖默认关闭的源码注解开关。所有实参在进入原生实现前匹配类型,失败不产生该实现的业务副作用;可选的未提供参数不检查,实际提供的重复参数逐个匹配。Return 在交付前匹配返回类型,错误释放本次作用域分配和结果根,但不回滚已经执行的业务写入。传统直接 call 结果仍按借用协议处理。NativeContext 的独立执行桥同样执行参数、效果与直接返回检查。
NativeResult::Invoke 仍是尾委托,不递归进入 Rust/VM。VM 为每层已声明的返回契约保留检查项;委托原生立即返回或 Endor 帧返回时,按由内到外顺序验证全部契约。返回定义作为执行根保留,原生载荷替换、释放原委托者或源码全局重绑定不会使检查丢失。准备失败、帧限额、指令限额、错误返回与正常完成都释放这些执行根。委托不会为检查额外创建 VM 帧;仍共享每次原生分派和 Endor 指令预算。NativeContext::execute 单独返回 Invoke 时,调用者须使用具有等效最终返回检查的求值器;通常直接使用 Vm::call。
CallEffect::ReadOnly 声明只读现存对象,可分配临时结果;Delegated 声明原生包装自身只读,但后续委托的整体效果动态确定,VM 保持同一只读/预算作用域并检查实际目标;Mutating 与 External 在 ObjectStore 只读作用域内、进入实现前拒绝。普通执行允许这些效果。ReadOnly 声明不绕过已有写屏障,也不证明可信 Rust 不会进行隐藏 I/O;未知旧原生函数仍沿原调用/宿主检查,不能把 None 理解为纯函数。这不是完整 I/O 沙箱或对阻塞原生代码的抢占。统一已知效果查询和保守源码/调用图只读证明见[类型主题](types.md#已知调用效果与保守源码证明)。
Tessera 的 measure/arrange/paint/hitTest、Point 几何查询、Window、Block/Container/Page、Mount、Task、Event、Subscription 方法和 ObjectList.at 发布签名。Page.open 的 Window | Unit、Page.window 的 Window | Unit 返回,以及 Block.on 的 Bool | String 可选阶段有准确元数据;Mount 的父/子/同级/Page 弱查询为可空结果。Mount.bounds 现在返回带 Bounds 名义类型的现有几何记录,可直接组合到 Point.inside 或视觉方法。滚动位置/字典等通用记录仍声明 Object,不冒充新语言类型;任务 owner 的具体资源资格仍按原域检查。构造器和其他管理 API 的签名覆盖仍未全部接入。已接入的方法覆写方差与动态名称规则见类型主题。旧能力注册保留数量模式;类型化能力还检查类型/已知效果,并在实际返回和尾委托结果处保留能力返回约束。
回归源码 native_context.rs 覆盖参数副作用前拒绝、错误结果清理、跨原生/源码委托检查、单帧限制、名义签名边与绑定方法循环回收、只读效果拒绝、传统直接函数及重复参数和元数据错误。只编译,不本地执行。
联合/可空回归另覆盖两个标量分支、nil、数值拒绝、Object 分支不掩盖非法名义类型、空联合拒绝,以及移除签名后联合返回定义仍存活。
Delegated 在替换兼容中视为未知整体效果,与源码未知效果一样不冒充静态纯度证明。包装不得在委托前进行业务写入或外部I/O,直接返回路径也须只读;委托的原生 Mutating/External 目标仍在当前只读作用域内、进入目标实现前拒绝。源码委托继续受堆写屏障约束,旧无元数据宿主调用仍须遵守可信边界。native_context.rs 的回归覆盖只读包装进入后拒绝写目标、普通作用域允许同一链,以及替换成纯目标后只读调用成功,仅编译。
ContextNativeFunction::implementation<T: NativeCallable>() 只借用当前具体宿主实现,不执行或复制载荷,也不改变捕获/签名的托管边。NativeCallable 继承Any(原有static要求保持);宿主可据自己的实现类型/操作识别原生快速路径,不能只凭函数句柄或当前可变类型方法表认定原生身份。替换载荷后查询跟随新实现,旧类型查询返回None;宿主缓存仍须订阅函数对象版本变化。此接口不暴露可变借用,不替代原生类型/数量/效果检查。
精确只读观察:end_read_only_precise返回ReadSet,兼容objects列表并提供ReadDependency;end_read_tracking_precise返回区域的ReadAccess与相同观察。Object依赖验证对象版本;DictionaryKind只验证存活/字典载荷;Name验证文本键的存在与值身份/表示。缺失名字也被记录,因此后续全局遮蔽内建名会失效;相同值删除/重建后可恢复有效,这是值观察而非“发生过写入”的通知。dict_get_name用于全局/注解/__builtins__名字查询,普通dict_get、dict_items和其他内容读取仍保守依赖整个对象。read_dependency_valid在只读区域内检查时会重记相同观察,使缓存命中仍向外传播依赖。所有对象句柄(包括Name中的Value::Object)均借用,不增加GC根;立即字符串/数字保留比较快照,值不写回业务对象。现有end_read_only/end_read_tracking/details仍返回保守对象列表,不自动替调用方放宽缓存。类型验证字典通过DictionaryKind观察,不把只验证载荷种类误当读取所有条目。