# 原生对象调用、弱引用与变更记录 本页定义宿主扩展使用的通用接口,不引入渲染或窗口语义。托管名义类型见[类型机制](types.md)。 ## 有上下文的调用 旧 ObjectPayload::call 保留借用参数和借用结果协议。新载荷可通过 native_callable 返回 Arc;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 回调要求接受零参数,登记前拒绝无元数据/不兼容函数;普通旧原生调用的兼容性不等于允许其参加这些严格回调契约。