Files
endorphin-aster-vm/docs/architecture.md
T
codex-bot 891defb34c
Verify databases and deploy Aster VM / verify-and-deploy (push) Waiting to run
Add Endor builtin objects and lossless unified number representations
2026-10-18 19:54:17 +08:00

6.8 KiB

架构与语义

内核

当前任务是 Endor 的虚拟机实现层。Vm::new() 只创建空堆,不加载语言根类,不分配 Object/Type,也没有类创建、继承或元类 API。EndorObject 是公共运行时结构,不能与语言根类混为一谈。

EndorObject 保存 ObjectHeader、Box<dyn ObjectPayload>、托管属性和载荷引用快照。对象头仅保存身份、总强引用数和宿主外部强引用数。操作通过 Rust trait 的分发机制选择,不需要 Python type 系统;kind_name 只是调试标签,as_any 仅供原生 Rust 扩展识别自身的数据结构。

分配与句柄

堆槽位为 Option<Box<EndorObject>>。Box 保持对象地址稳定,槽位向量扩容不移动实际对象。ObjectRef 由私有 VM 身份、索引、代数组成;每次访问先检查 VM,再检查范围、代数和对象是否存活。释放后槽位代数递增,耗尽后永久退役,不回绕。VM 身份由不回绕的原子计数器分配。无 unsafe、裸指针访问或外部句柄构造器。

引用所有权

allocate/new_object 返回一个外部强引用。ObjectRef 是可复制的借用句柄;复制或 Value::clone 不 retain。宿主通过 retain/release 显式持有与释放。头部总计数等于外部计数加来自托管属性及载荷引用快照的入边数。

属性写入先验证目标、被引用对象和计数溢出,再增加新引用、替换属性、释放旧引用;同目标替换不会短暂释放对象。属性读取和原生调用返回借用值,宿主需要跨越所有者生命周期使用时应先 retain。没有外部引用时,release 返回 NoOwnedReference,不能误释放内部属性引用。release 不代表删除任意对象。

载荷通过 trace 报告自己持有的引用;分配时验证全部边并记录快照,相同引用出现两次就计数两次。分配失败不改变已存在对象的计数。自定义载荷不能自行修改分配后的引用;内置字典和 Cell 通过 VM 事务性替换载荷与快照更新引用,先保留新边,再释放旧边。对象本身的 Box 和身份不变。扩展未声明的借用句柄不受 GC 保护,访问或返回时仍需通过 VM 验证。

清理与循环回收

总计数变为零时先摘除槽位,再用显式工作列表递减出边,最后 Drop 对象及原生资源;长链不会递归调用对象析构。对象不会因为仍有一个复制出的借用句柄而继续存活。

collect_cycles 是显式、全堆的追踪收集器:以外部引用数大于零的对象为根,沿属性和载荷快照标记,先批量摘除全部不可达对象,再扣减指向幸存对象的引用,最后 Drop 垃圾。这样可以处理自环、互相引用和原生载荷与属性构成的环,也不会误收仍可达的对象。trace API 遍历的是受管理引用,不是语言继承关系。

VM 销毁时 Box 的 Rust 所有权会清理全部剩余载荷,即使其计数仍大于零或尚未显式回收循环。没有语言级 del、复活、自动阈值、分代或增量收集。外部句柄不延长 VM 的生命周期。

操作协议

ObjectPayload 定义 repr、str、is_true、hash、compare、call 及属性许可。默认表示带诊断名和身份,默认 str 复用 repr,默认真值为 true,默认哈希基于身份,默认 Eq/Ne 按身份,排序和调用默认报 UnsupportedOperation。compare 返回 None 时尝试右侧反向操作,再使用身份回退;没有 Python 继承优先级规则。

Record 支持托管属性,NativeFunction 包装 Rust 函数指针。原生调用先验证参数句柄,再执行回调,再验证结果句柄。回调只计算返回值,不能修改或分配当前 VM;对象结果是借用句柄。自定义载荷可覆盖操作,若提供内容相等语义,必须保持哈希一致,或显式拒绝哈希。错误通过 Result 返回,不是语言异常机制。

Value 中标量和字符串不强制堆分配;数字统一为 EndorNumber,同时可装箱为 EndorNumberObject。整数和精确小数通过文本保留任意长度数字,分数保留分子分母,浮点保留 IEEE 位,复数保存两个实数表示。当前不实现数值运算、跨表示数值相等或哈希,数字暂不能当字典键。Rust 值相等只比较表示,不是语言数字相等。

EndorDictObject 保留插入顺序,当前线性查询;EndorCellObject 提供共享可变槽。EndorFunctionObject 保存代码数据、全局字典、默认参数和闭包 Cell;NativeFunction 的正式名称是 EndorNativeFunctionObject。EndorBaseExceptionObject 保存异常数据及链,不建立类型继承或异常执行流程。EndorStringObject 使用 UTF-8,按 Unicode 标量值访问字符。更详细的接口和限制见 基本对象 与 基础对象。

宿主与持久化

aster-storage 不引用任何内核类型。它连接一个确定的后端,初始化 aster_metadata(key TEXT PRIMARY KEY NOT NULL, value TEXT NOT NULL),通过参数绑定完成幂等更新及读取。当前 schema 是单条幂等 DDL;修改 schema 时再引入版本化迁移,禁止把 CREATE IF NOT EXISTS 当作升级已有列结构的工具。

SQLite 文件模式自动创建父目录、开启 WAL,连接池最多 5 条连接,busy timeout 为 5 秒。内存模式单连接,关闭空闲回收和最大存活时间,防止池正常轮换导致数据库消失;独立 Storage 实例的内存数据库相互隔离。Storage 克隆共享连接池,close 关闭全部克隆的池。

PostgreSQL 最多 5 条连接,获取/建立连接超时 5 秒,启动真实连接而非惰性连接。其配置失败或初始化失败均上抛,不进入 SQLite 分支。SQLite 的缺省只发生在未配置 DATABASE_URL 时。

元数据仅供宿主管理,不代表类、实例、句柄或整堆快照。重启宿主会创建新的空 VM;后续对象获得新身份和新句柄,只保留文件/数据库内的元数据。服务仅写入 host.version,不会把句柄调试值写入数据库。

aster-server 保存只读 VM 和共享 Storage。GET /bootstrap 只读运行时标识和对象数量;GET /health 探测数据库可用性。没有对象操作 API、语言执行 API 或鉴权协议。HTTP/Tokio/SQLx 都不进入内核。后续浏览器宿主应通过明确的能力接口桥接,不能把浏览器类型塞进核心对象工厂。

已决定与未决定

已决定:EndorObject 实现层基础结构、稳定 Box 分配、代际句柄、外部/内部引用计数、手动全堆循环回收、Rust 操作分发、存储属于宿主。

未决定:语言根类、Python type/元类体系、继承、字节码格式、调用栈、可变 VM 回调上下文、语言异常与终结器、外部对象协议、共享可变 VM、自动/分代 GC、堆序列化和浏览器通信。不得在交接中把这些当成已实现契约。