Files
endorphin-aster-vm/docs/runtime/objects.md
T

108 lines
8.5 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.
# EndorObject 代码说明
当前实现的是 Endor 的 VM 运行时基础对象,角色类似 CPython 的公共对象结构。宿主可显式引导托管 Object/Type 与名义继承,见[类型机制](types.md);普通源码类和继承方法分派已实现,显式 NativeTyped 原生分配继承已接入,trait 尚待接入。标量仍然可以直接保存在 Value 中,不要求一切装箱。
基本对象及统一数字表示已在此基础上加入,详见 [基本对象与 Number](builtins.md)。`builtins.rs` 定义字符串、字典、Cell、函数和异常数据;`number.rs` 定义无损数字表示;`store_builtins.rs` 提供容器操作及关联对象校验。
这些定义全部位于独立 objects 包。ObjectStore 拥有私有 Heap,负责对象操作与引用事务;core 的 Vm 拥有一个 ObjectStore,仅委托对象操作并管理执行状态。对象存储无需 VM 即可使用,仍不提供句柄字段或裸指针构造;不同存储的句柄互不通用。VmError 也在 objects 定义以避免反向依赖,endor_core 兼容重导出现有类型和保留 Vm 方法。
代码文件及职责见 [objects 源码导航](../../crates/objects/src/README.md),执行器见 [core 源码导航](../../crates/core/src/README.md),对象及执行的生命周期见 [生命周期](lifecycle.md)。
## EndorObject 保存什么
```text
EndorObject
header 身份、总强引用数、外部强引用数、变更版本、可选类型引用
payload 原生 Rust 数据及操作接口
attributes VM 管理的可变属性表
payload_edges 分配时记录的载荷引用快照
```
`ObjectHeader.type_object` 是可选的、受 GC 追踪的类型对象引用,普通实例附加后不可更改。`identity` 是 ObjectRef,用于识别同一个对象。`strong_refs` 是所有强引用的总数,`external_refs` 统计宿主持有和执行帧持有的根引用,也是循环回收的根来源。
`payload` 是 `Box<dyn ObjectPayload>`。Box 保存原生数据,dyn trait 让运行时通过统一接口调用不同 Rust 实现。Rust 会为 trait 分发生成所需机制;其原生操作接口与托管 Type 身份分别保存。`kind_name` 只用于诊断,`as_any` 只用于原生 Rust 扩展判断自己能否处理另一份载荷。
`attributes` 保存字段名和 Value。对象值会被 VM 计为内部强引用;字符串和整数等直接保存。载荷可选择是否允许属性,例如 Record 允许,NativeFunction 默认不允许。
`payload_edges` 让原生载荷也可以持有对象。载荷的 trace 方法逐一报告这些边,分配器验证并保存快照;重复边分别计数。自定义载荷分配后不能自行修改这些边。内置字典和 Cell 通过 VM 的载荷替换协议更新:验证和保留新边,替换载荷与快照,最后释放旧边;对象 Box 保持稳定。其他可变对象关系也可放入托管属性。单纯复制一个借用句柄不算持有,也不保护它免于回收。
## 指针与句柄
Heap 使用 `Option<Box<EndorObject>>` 保存槽位,实际对象位于独立分配的 Box 中。槽位向量扩容时,移动的是 Box,存活对象本身的地址保持稳定。
公开的 ObjectRef 使用三部分定位:
```text
heap_id 属于哪个 VM
index 位于哪个槽位
generation 这是该槽位的哪一次分配
```
每次访问检查归属、范围、代数和存活状态。对象释放后,复用同一槽位会更新代数,所以旧句柄不会指向后来分配的新对象。代数耗尽就停用该槽位,VM 身份也不回绕。
公共 API 不暴露裸指针解引用,也不允许调用方构造索引。句柄是进程内引用,不是数据库持久 ID。
## 分配、持有与释放
`new_object()` 创建 Record;`allocate(payload)` 创建指定原生载荷的对象。分配成功时,总引用数和外部引用数均为 1,返回这一份外部持有对应的句柄。
```rust
let object = vm.new_object()?;
let borrowed = object; // 只有句柄复制,引用数仍为 1。
vm.retain(borrowed)?; // 再持有一份,总数和外部数变为 2。
vm.release(object)?; // 释放一份,仍然存活。
vm.release(borrowed)?; // 最后一份释放,清理对象。
```
ObjectRef 是 Copy,但它不是自动管理持有关系的智能指针。宿主有责任为每一份外部持有配对 retain/release。没有外部持有时不能通过 release 消耗属性拥有的内部引用;此时返回 NoOwnedReference。
引用数归零后,堆先摘除对象,再迭代减少它持有的出边,最后执行 Rust Drop。若某个目标也归零,就加入工作列表继续处理。长对象链不会形成递归析构栈。载荷可实现 Drop 释放原生文件、缓冲区或其他宿主资源;这里没有语言级终结器和复活协议。
销毁整个 Vm 时,Rust 所有权会释放全部剩余对象,包括尚未显式回收的环。复制出的句柄不能让 VM 本身继续存活。
## 默认操作和覆盖方式
ObjectPayload 提供以下默认行为,载荷可以逐项覆盖:
| 操作 | 默认行为 |
| --- | --- |
| `repr` | 诊断名和对象身份组成的文本 |
| `str` | 调用该载荷的 repr |
| `is_true` | true |
| `hash` | 基于对象身份计算哈希 |
| `compare` | 返回未实现,由 VM 尝试反向比较;Eq/Ne 最终按身份回退,排序报错 |
| `call` | 返回不支持调用的错误 |
| `allows_attributes` | 不允许属性,载荷可显式允许 |
| `trace` | 没有载荷持有的对象引用 |
Record 使用身份语义并开启属性。NativeFunction 保存名称和 Rust 函数指针,覆盖 repr 和 call。原生函数收到 Value 参数并返回 Result<Value, VmError>;VM 在调用前后检查参数和返回值中的对象句柄。
旧 call 回调不能修改 VM 或分配新对象;需要这些能力的原生方法可使用 NativeCallable 与受限 NativeContext。两种原生入口都接受借用参数,Vm::call 对外统一返回一份持有式结果。上下文临时根、非递归委托与捕获规则见 [原生调用](native-calls.md)。语言函数通过 [wordcode 执行器](bytecode.md) 使用显式帧、参数槽、闭包和求值栈执行;call/execute 的结果完成使用后须 release_value。
compare 的反向规则是 Lt 对应 Gt、Le 对应 Ge,Eq/Ne 保持不变。它不实现 Python 的子类型优先规则。内容比较由扩展定义;如果两个对象内容相等,它们必须给出相同哈希,或者明确拒绝哈希。
## 属性与错误原子性
`get_attr` 读取借用值;`set_attr` 写入;`del_attr` 删除。对象属性持有内部强引用,删除或覆盖时会释放旧引用。宿主若希望读取到的对象在属性删除后继续存活,必须先 retain。
写入先验证所有句柄和计数溢出,再增加新引用,最后替换并释放旧引用。因此跨 VM、失效句柄或溢出错误不会覆盖原值;把同一个对象重复赋给同一属性,也不会在替换间隙误释放它。
## 循环回收
引用计数无法自行释放 A → B → A,所以提供显式 `collect_cycles()`:
1. 把 external_refs 大于零的对象作为根。
2. 沿类型、托管属性和载荷边标记所有可达对象。
3. 先从槽位中摘除全部不可达对象。
4. 扣减垃圾指向幸存对象的引用,再 Drop 全部垃圾。
该过程会保留可达的环,回收无人持有的自环、互引用环,以及原生载荷与托管属性混合构成的环。收集返回回收数量,未自动按阈值触发。
## 当前边界
已实现:统一对象结构、稳定地址、代际句柄、身份、显式引用计数、托管属性、原生操作覆盖、比较与哈希、原生调用、资源清理和显式循环回收。另有受限原生可变上下文、安全弱引用、托管 Cell 赋值及有界对象变更记录,见 [原生调用](native-calls.md)。
尚未实现:Python type 兼容、trait 和描述符、任意 VM 重入调用上下文、自动/分代/增量 GC、语言终结器/复活、完整资源限额和 VM 快照;独立 frontend 已提供阅读器、lexer/parser 与 AST,基础源码执行由独立 [Compiler](compiler.md) 接入,见 [源码前端](frontend.md);执行器已有帧数和指令数限额。数据库仅为未来特殊类提供按需连接,普通对象或宿主基础信息不自动持久化,详见 [数据库特殊类](https://gitea.entroia.com/Zayden-Jung/endorphin-hestia-server/src/branch/main/docs/runtime/database-objects.md)。
验证范围与执行约定见 [验证](../operations/validation.md)。