60 lines
7.5 KiB
Markdown
60 lines
7.5 KiB
Markdown
# 架构与语义
|
|
|
|
## 内核
|
|
|
|
当前任务是 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。call/execute 的对象结果持有一份外部引用,宿主使用后须 release_value。没有外部引用时,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;回调的原始对象结果是借用句柄,Vm::call 会验证并 retain 后作为持有式结果返回。自定义载荷可覆盖操作,若提供内容相等语义,必须保持哈希一致,或显式拒绝哈希。错误通过 Result 返回,不是语言异常机制。
|
|
|
|
Value 中标量和字符串不强制堆分配;数字统一为 EndorNumber,同时可装箱为 EndorNumberObject。整数和精确小数通过文本保留任意长度数字,分数保留分子分母,浮点保留 IEEE 位,复数保存两个实数表示。当前不实现数值运算、跨表示数值相等或哈希,数字暂不能当字典键。Rust 值相等只比较表示,不是语言数字相等。
|
|
|
|
EndorDictObject 保留插入顺序,当前线性查询;EndorCellObject 提供共享可变槽。EndorFunctionObject 保存共享代码对象句柄、全局/内建字典、默认参数和闭包 Cell;NativeFunction 的正式名称是 EndorNativeFunctionObject。EndorBaseExceptionObject 保存异常数据及链,不建立类型继承或异常执行流程。EndorStringObject 使用 UTF-8,按 Unicode 标量值访问字符。更详细的接口和限制见 [基本对象](endor-builtins.md) 与 [基础对象](endor-object.md)。
|
|
|
|
## 指令、执行帧与求值循环
|
|
|
|
小型 wordcode 采用两字节 opcode/oparg 代码单元和 EXTENDED_ARG。代码对象持有验证后的指令、常量、名称表和局部槽布局,验证控制流汇合的栈高度并计算最大求值栈深度。函数共享代码对象;执行帧保存指令位置、调用者索引、本地/Cell/自由变量槽和求值栈。语言调用切换显式帧,不递归进入 Rust 求值器。返回和报错时清理根引用,移交的返回值继续存活。详见 [字节码 VM](bytecode-vm.md)。
|
|
|
|
## 宿主与持久化
|
|
|
|
`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、堆序列化和浏览器通信。不得在交接中把这些当成已实现契约。
|