feat: add managed types, source classes and scoped native calls

This commit is contained in:
2026-10-21 01:47:08 +08:00
parent 7c842789c0
commit a970fd7707
48 changed files with 2069 additions and 134 deletions
+2
View File
@@ -2,7 +2,9 @@
| 文档 | 内容 |
| --- | --- |
| [types.md](types.md) | 托管 Object/Type、名义继承、构造与类型边生命周期 |
| [objects.md](objects.md) | 对象、句柄与默认操作 |
| [native-calls.md](native-calls.md) | 原生上下文、非递归委托、弱引用、托管赋值与变更批次 |
| [builtins.md](builtins.md) | Number、String、Dict、Cell、Function、异常数据 |
| [reader.md](reader.md) | 输入、来源和修订标签 |
| [frontend.md](frontend.md) | 词法、语法和诊断 |
+5 -4
View File
@@ -30,17 +30,18 @@ Rust 用 struct/enum、Vec/Option 表达结构;生成器在按值依赖图
| 层级 | 当前数据模型 |
| --- | --- |
| AstRoot | Module(Program)、Expression(Expr)、Interactive(InteractiveInput) |
| Program | declarations: Vec<Decl> 与 span;当前声明种类为 Function |
| Decl/DeclKind | 声明的位置及其具体种类;Function 保存参数、返回类型名称和 Block |
| Program | declarations: Vec<Decl> 与 span;声明种类为 Function、Class |
| Decl/DeclKind | 声明的位置及其具体种类;Function 保存参数、返回类型名称和 Block;Class 保存名称、可选基类、字段及方法 |
| Block | statements: Vec<Stmt> 与 span,不再用固定的 bindings/result 字段表示 |
| Stmt/StmtKind | Binding、Assignment、Return、Expression、Declaration;每条语句有自身范围 |
| Stmt/StmtKind | Binding、Assignment、MemberAssignment、Return、Expression、Declaration;每条语句有自身范围 |
| MemberAssignment | 对象表达式、属性名称、右侧表达式与原文范围 |
| Assignment | 名称目标、右侧表达式及原文范围;独立于 let 的 Binding 节点 |
| ReturnStatement | 可选表达式与范围;当前源码中的 return 仍要求表达式 |
| Expr/ExprKind | 名称、数字原文、字符串、符号、一元/二元运算、成员和调用 |
参数与返回类型仍为 Name,只保留语法,不实施类型或名称检查。Program::functions() 提供函数迭代便捷接口。旧的 Program.functions、Block.bindings/result/return_span 字段已移除;源码构造和使用方改为声明/语句列表。ExprKind 的既有表达式结构保留。
AST 数据模型允许一般语句列表,不代表 parser 接受了所有组合。模块仍要求一个或多个函数;函数体接受 let/裸赋值序列后跟一个带值 return。Interactive 模式接受一次函数声明、let 绑定、裸名称赋值或表达式,必须消费完整输入;不创建交互会话,不处理续行提示,不执行语言代码。多条交互提交由宿主分别解析。条件、循环、导入、类等尚未实现,也不加入 FunctionType 这种 Python 专用入口。
AST 数据模型允许一般语句列表,不代表 parser 接受了所有组合。模块要求一个或多个函数/类声明;函数体接受 let/名称赋值/属性赋值/表达式语句,以及可选的末尾带值 return。Interactive 模式接受一次函数/类声明、let 绑定、名称/属性赋值或表达式,必须消费完整输入;不创建交互会话,不处理续行提示,不执行语言代码。多条交互提交由宿主分别解析。条件、循环、导入、trait 等尚未实现,也不加入 FunctionType 这种 Python 专用入口。
## 解析与位置
+1 -1
View File
@@ -1,6 +1,6 @@
# Endor 基本对象与统一 Number
这些对象统一定义在独立 [objects 包](../../crates/objects/README.md),属于实现层的数据结构与宿主 API,不定义 Python type、语言根类或解析器;语言函数已接入 [小型字节码执行器](bytecode.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。
这些对象统一定义在独立 [objects 包](../../crates/objects/README.md),属于实现层的数据结构与宿主 API,尚未自动注册到[托管名义类型](types.md),不定义解析器;语言函数已接入 [小型字节码执行器](bytecode.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。
## 对应关系
+11 -4
View File
@@ -26,7 +26,7 @@ Endor 是解释型语言,字节码是内部执行表示;从 AST 生成字节
所有执行位置和跳转距离以**两字节代码单元**计数,包含扩展参数单元;不是字节偏移,也不一定等于逻辑指令下标。前跳从当前逻辑指令之后的位置加距离;后跳从该位置减距离。目标必须是完整逻辑指令的开头,不能跳进扩展参数中间。
当前有 29 个可执行操作码和一个由编解码器处理的 EXTENDED_ARG:
当前有 30 个可执行操作码和一个由编解码器处理的 EXTENDED_ARG:
| 指令组 | 内容 |
| --- | --- |
@@ -37,7 +37,7 @@ Endor 是解释型语言,字节码是内部执行表示;从 AST 生成字节
| 控制流 | `JumpForward`、`JumpBackward`、`PopJumpIfFalse`、`PopJumpIfTrue` |
| 真值与比较 | `ToBool`、`UnaryNot`、`CompareOp` |
| 闭包 | `LoadDeref`、`StoreDeref`、`DeleteDeref`、`LoadClosure` |
| 容器 | `BuildMap` |
| 容器与类型 | `BuildMap`、`BuildClass` |
没有把 Number 转为 f64 来补运算。数字仍以无损表示为主;真假可以根据是否为零判断,数值比较和算术尚未定义。文本可作内容比较,普通对象使用现有比较接口。
@@ -152,11 +152,11 @@ fn main() -> Result<(), endor_core::VmError> {
}
```
该示例直接提供指令,不经过源码前端;当前 Endor 前端可生成 AST,但尚不能从源码生成字节码,也不解析 Python 源码。宿主仍负责准备代码对象和执行环境;函数既可由宿主 new_function 工厂创建,也可由上述 MakeFunction 在字节码运行期间创建。
该示例直接提供指令,不经过源码前端;当前 Endor 的 frontend/compiler 已能从源码生成 AST 和字节码,也不解析 Python 源码。宿主仍负责准备代码对象和执行环境;函数既可由宿主 new_function 工厂创建,也可由上述 MakeFunction 在字节码运行期间创建。
## 限额、诊断与边界
`ExecutionLimits` 默认限制 1000 个活动帧,指令配额为 `usize::MAX`,正常使用不设置有限的指令预算。`execute_with_limits`/`call_with_limits` 可显式指定限额,所有嵌套语言调用共享计步器;扩展参数不单独计步。可选指令预算是 Endor 的宿主运行边界,不是 CPython 默认行为,也不会抢占正在执行的宿主回调。
`ExecutionLimits` 默认限制 1000 个活动帧,指令配额为 `usize::MAX`。`execute_with_limits`/`call_with_limits` 可显式指定限额,嵌套语言调用和原生分派共享计步器;每次原生分派额外计一步,扩展参数不单独计步。原生 Invoke 委托通过同一帧循环推进,不重置预算;详见 [原生调用](native-calls.md)。指令预算不会抢占正在执行的宿主回调。
运行错误仍返回 `VmError`。`last_traceback()` 另提供从外到内的函数名、文件、代码首行和出错指令偏移;它保存独立元数据,不保活已经清理的帧。首行是代码声明的 first_line,不是由行号表推导出的实际错误源码行。成功的新一次调用会清空旧记录。尚无语言异常对象传播、raise/try/except 或完整源码行号表。
@@ -175,3 +175,10 @@ fn main() -> Result<(), endor_core::VmError> {
`ObjectPayload::assign_from(source)` 是可选协议,默认返回 false,普通值与对象仍替换变量绑定。StoreGlobal、StoreFast、StoreDeref 在既有值和新值均为对象时调用此协议;返回 true 表示宿主对象已替换自己的目标,绑定保留旧对象身份,其他别名随共享引用观察更新。相同句柄赋值为无操作。此协议同时适用于这些存储指令实现的声明初始化/赋值;字典 API 与 StoreAttr 不隐式调用它。
协议不传递变量名字或 UI 元数据,也不引入渲染器。实现必须先验证再修改,拒绝替换时保留旧目标;不得通过可变 payload 悄悄增加未跟踪的 VM 对象边。宿主原生关系如持有受管理对象,必须通过 VM retain/release 登记根。原生资源的内部引用可以由宿主维护,但其管理对象身份和存活根仍由 Aster 校验和计数。StoreFast 会先持有选择后的值,再释放栈值与旧槽,失败执行仍走统一帧清理。
## 类创建与方法读取
BuildClass(编号 29,无参数)消费四个栈值 `[name, baseType, fieldsDict, methodsDict]`,产生一个受管类对象;验证器按 4 入 1 出检查栈。字段字典按声明顺序保存字段名与注解名,方法字典保存 MakeFunction 创建的函数。VM 先验证基类、字段与方法关系,再创建完整类型;输入根在成功前保持在执行栈上,失败由帧统一清理。
LoadAttr 对实例自有属性返回原值;缺失时沿类型父链找方法并创建绑定方法。绑定方法持有 receiver 和选定的函数,调用时经已有非递归委托路径补入 self。类对象上的方法读取提供未绑定函数;type/name/base 元数据优先。宿主读取继承方法应使用返回拥有式结果的 bind_attr;get_attr 保持借用式读取,类型方法结果为未绑定函数。所有委托消耗同一指令预算。
+8 -3
View File
@@ -15,7 +15,7 @@
## 名称与作用域
首版是模块作用域和函数作用域。模块先登记全部函数名,再分析函数体,因此支持前向引用、自递归和函数作为普通值传递。函数参数占据最前面的局部槽,let 与首次赋值目标按出现顺序分配后续槽;引用优先使用函数局部,其次使用已声明全局。
首版是模块作用域和函数作用域。模块先登记全部函数/类名,再分析函数体,因此支持前向引用、自递归和函数作为普通值传递。函数参数占据最前面的局部槽,let 与首次赋值目标按出现顺序分配后续槽;引用优先使用函数局部,其次使用已声明全局。
函数内先收集全部局部名称,再按语句顺序检查初始化。后面声明的 let 会遮蔽整个函数中的同名全局,但提前读取报错;let x = x 也会报未初始化。参数、局部绑定和同一模块的函数不允许在各自作用域内重复声明;参数或局部可以遮蔽模块函数。裸名称赋值 `x = expression` 可在交互全局创建或更新名字;函数内赋值复用已有参数/局部槽,不存在时分配局部槽。赋值右侧先求值再写入,失败不会覆盖该目标;函数内赋值目标仍遮蔽同名全局,首次初始化前读取报错。`let` 的重复声明检查不变。嵌套函数/闭包分析和块级作用域尚未实现。
@@ -31,10 +31,12 @@ CompileOptions.globals 显式声明宿主会提供的 globals/builtins 名称
| 全局读取/赋值 | LoadGlobal/StoreGlobal,使用该代码对象的 names 表 |
| 函数声明 | 子代码模板 → LoadConst(code) → MakeFunction(0, 0) → StoreGlobal |
| 普通调用 | 求值 callee → PushNull → 从左到右求值位置参数 → Call(argc) |
| 成员读取 | 求值对象 → LoadAttr;沿用 VM 属性协议,无自动 self 绑定 |
| 成员读取 | 求值对象 → LoadAttr;沿用 VM 属性协议;类型方法返回持有接收者的绑定方法 |
| 属性赋值 | 求值右侧,再求值目标对象,使用 StoreAttr 维护托管引用与变更 |
| 类声明 | 名称、基类、字段映射、MakeFunction 构造的方法映射 → BuildClass → StoreGlobal |
| return | 求值结果 → ReturnValue;无值 return 或 AST 函数末尾无 return 时生成 nil |
源码 parser 仍要求函数体为 let/裸赋值序列加一个带值 return;更通用的表达式语句和空 return 可以由 AST 调用方构造,但不能因此宣称源码语法已开放。模块初始化只发布函数并返回 nil,不自动调用 main。Lyra 宿主选择入口属于宿主行为。
源码 parser 接受绑定/名称及属性赋值/表达式语句与可选末尾带值 return;空 return 仍仅由 AST 调用方构造。模块初始化发布函数/类并返回 nil,不自动调用 main。Lyra 宿主选择入口属于宿主行为。
二元算术、非字面量的一元负号和符号值 .name 目前产生明确 Compilation 诊断,不伪造运行结果。导入、类、Agent、条件/循环、关键字参数等仍未实现。函数参数个数和对象是否可调用由 VM 在运行时检查。
@@ -53,3 +55,6 @@ Expression 模式返回表达式值;Interactive 模式严格编译一次声明
lyra run [--entry name] <file|-> 编译模块、执行初始化、调用无参数入口(默认 main),输出返回值一行。lyra eval <file|-> 编译执行一个表达式。字符串输出原始内容,数字输出无损表示,对象使用 VM str;失败仅输出 stderr 并返回非零退出码。文件和 stdin 严格 UTF-8,stdin 到 EOF 后才处理。Lyra 在成功或失败后释放代码/结果并清空自己创建的命名空间。
可执行文件见 [示例目录](../../crates/compiler/examples/README.md),API/所有权与错误路径见 [pipeline.rs](../../crates/compiler/tests/pipeline.rs),进程入口见 [CLI 检查](https://gitea.entroia.com/Zayden-Jung/endorphin-lyra-cli/src/branch/main/crates/cli/tests/README.md)。引擎和进程入口检查由独立 Lyra CI 分别显式执行;本地只做静态检查,不据此宣称实际执行通过,见 [验证约定](../operations/validation.md)。返回 [运行时索引](README.md)。
类方法使用显式首参数 self;编译器检查本类重复字段/方法和缺少 self。方法代码仍是共享的普通代码对象,参数类型名目前不实施运行时类型检查。BuildClass 在 VM 中检查继承字段冲突和原生分配边界;构造失败不发布类型到目标全局名。默认基类 Object 须由宿主通过 CompileOptions 声明并在运行全局中提供,编译器不自行引导类型。普通类构造和继承分派见[类型机制](types.md)。
+7 -4
View File
@@ -52,12 +52,12 @@ AST 结构及 Visitor、Spanned 和跨工具 JSON 契约见 [AST 模型](ast.md)
program = function+
function = "function" name "(" parameters? ")" ":" name block
parameter = name ":" name
block = "{" (binding | assignment)* "return" expression "}"
block = "{" (binding | assignment | expression)* ("return" expression)? "}"
binding = "let" name "=" expression
assignment = name "=" expression
```
parameters 和调用参数列表使用逗号分隔,不接受尾随逗号。函数体要求一个末尾 return;函数体尚不支持独立表达式语句、属性赋值、嵌套函数声明或默认参数源码写法。新增 Interactive 解析模式接受一次函数声明、let 绑定、裸名称赋值或表达式,完整消费输入,但不创建 REPL 或执行代码;AST 可表示的语句组合比当前源码子集更广。参数和返回类型目前只是名称节点;并不据此创建语言 type 对象或检查类型。
parameters 和调用参数列表使用逗号分隔,不接受尾随逗号。函数体支持独立表达式语句(包括方法调用)和可选的末尾 return;省略 return 时编译器生成 Nil 结果。函数体支持属性赋值,尚不支持嵌套声明或默认参数源码写法。Interactive 解析模式接受一次函数/类声明、let 绑定、名称/属性赋值或表达式,完整消费输入,但不创建 REPL 或执行代码。参数和返回类型目前只是名称节点;并不据此创建语言 type 对象或检查类型,省略 return 不代表已实施 Unit 类型检查。
表达式包括名称、数字、字符串、`.name` 符号、括号分组、成员访问与位置参数调用、一元负号以及四则运算。优先级从高到低为:
@@ -68,7 +68,7 @@ parameters 和调用参数列表使用逗号分隔,不接受尾随逗号。函
符号字面量、名称与成员分别保留为独立 AST 结构。true、false、Nil 等词目前只是普通名称,没有额外的源码字面量或内建值绑定。
use、namespace、class、agent、完整类型语法、条件/循环、比较/布尔运算和闭包声明等尚未加入首版。当前语法可修改;扩展时同步 AST、PEG 规则、样例和测试,不将旧草案视作不可修改的 ABI。
use、namespace、trait、agent、完整类型语法、条件/循环、比较/布尔运算和闭包声明等尚未加入首版。当前语法可修改;扩展时同步 AST、PEG 规则、样例和测试,不将旧草案视作不可修改的 ABI。
## 诊断和命令
@@ -78,4 +78,7 @@ use、namespace、class、agent、完整类型语法、条件/循环、比较
当前 Compiler 将 AST 经模块/函数名称分析转换为现有字节码,再交给 VM 解释执行。这是解释器内部的编译/执行准备步骤,不要求使用者先构建程序,见 [执行与版本模型](../architecture/execution-and-versions.md)。MakeFunction 已接入源码函数编译,但解析接受的二元算术和符号值仍没有执行语义;编译器会给出明确诊断,见 [编译链路](compiler.md)。源码闭包尚未实现。解析器也不是不可信代码沙箱,当前没有通用输入大小或解析递归深度限额。
裸赋值 `name = expression` 创建或更新当前作用域中的名称;它不是表达式,不能链式赋值,当前也不接受属性/索引目标。`let` 仍表示声明,保留重复声明检查;名称与初始化规则见 [Compiler](compiler.md)。两种源码写法产生独立的 Binding/Assignment 节点,宿主不改写输入。
裸赋值 `name = expression` 创建或更新当前作用域中的名称;它不是表达式,不能链式赋值,属性目标使用独立 MemberAssignment 节点;索引目标尚不支持。`let` 仍表示声明,保留重复声明检查;名称与初始化规则见 [Compiler](compiler.md)。两种源码写法产生独立的 Binding/Assignment 节点,宿主不改写输入。
源码类实现子集采用 `class Name: Base { field: TypeName function method(self: Name): ResultType { ... } }`;基类可省略,执行环境须提供 Object。成员函数沿用既有 function 语法,接收者 self 为显式首参数。普通类字段按基类字段、再本类字段的顺序接收位置构造实参。这里记录实现范围,不确认语言文档中的全部候选构造语法;详细限制见[名义类型](types.md)。
+2
View File
@@ -29,6 +29,8 @@ Rust panic 尚无专门的捕获恢复或执行根 RAII 清理机制。宿主捕
## 回收和整体销毁
NativeContext 的分配与弱引用升级使用作用域根,成功时仅移交 Return 或 Invoke 结果,失败时释放临时持有。WeakObjectRef 和变更通知不保活对象;具体接口见 [原生调用](native-calls.md)。
通常释放在总引用数归零时立即执行。堆使用显式工作列表递减出边,长链不会递归析构。不可达循环通过手动 collect_cycles 从根标记后批量移除;没有自动阈值、分代或增量 GC。
Vm 没有独立后台线程、暂停状态或显式 shutdown 状态机。Rust 所有权销毁它所拥有的堆和剩余载荷,包括没有先做循环回收的对象;原生共享资源继续遵循各自的 Rust 所有权。外部 ObjectRef 不保活 Vm。
+55
View File
@@ -0,0 +1,55 @@
# 原生对象调用、弱引用与变更记录
本页定义宿主扩展使用的通用接口,不引入渲染或窗口语义。托管名义类型见[类型机制](types.md)。
## 有上下文的调用
旧 ObjectPayload::call 保留借用参数和借用结果协议。新载荷可通过 native_callable 返回
Arc<dyn NativeCallable>;VM 先取得独立分派器,再可变访问 ObjectStore,避免同时借用
堆中载荷与可变对象存储。ContextNativeFunction 提供名称、分派器和不可变捕获边列表。
捕获中的 ObjectRef 必须全部在 captures 中登记,未登记句柄不能保活对象。
NativeCallable::invoke 接收 NativeContext 和借用参数。context 提供对象、字典、Cell
分配、托管属性和容器写入、载荷事务替换及弱引用升级。store() 仅暴露只读访问;
callee() 返回当前可调用对象的借用句柄;attach_type 通过受校验的类型边事务附加实例类型。
没有不受控的 &mut Vm、手动释放根或重入执行器入口。
context 内分配或升级得到的对象由本次作用域临时持有。NativeResult::Return(value)
返回借用结果;作用域为结果额外持有一份根,再释放其余临时根。出错或 unwind 也会
清理本作用域根,但此前完成的业务写入不会回滚;这不承诺整个执行帧的 panic 恢复。
NativeResult::Invoke { function, arguments } 将调用委托给另一个原生或语言函数。
function 和每个对象参数在移交前取得根;VM 完成调用准备后释放这些临时输入。
委托通过原有帧循环推进,不递归调用 Vm::call;语言函数使用原有帧限额。每次原生
分派计入同一指令预算,避免无界原生委托链。预算仍不能抢占一次长时间原生操作。
委托函数的结果就是该次调用结果,当前不提供任意原生调用后续 continuation。
## 托管共享引用赋值
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);本地不执行测试。
+8 -8
View File
@@ -1,6 +1,6 @@
# EndorObject 代码说明
当前实现的是 Endor 的 VM 运行时基础对象,角色类似 CPython 的公共对象结构。这里没有语言根类、元类、继承或 Python type 系统。标量仍然可以直接保存在 Value 中,不要求一切装箱。
当前实现的是 Endor 的 VM 运行时基础对象,角色类似 CPython 的公共对象结构。宿主可显式引导托管 Object/Type 与名义继承,见[类型机制](types.md);普通源码类和继承方法分派已实现,trait 和原生类分配继承尚待接入。标量仍然可以直接保存在 Value 中,不要求一切装箱。
基本对象及统一数字表示已在此基础上加入,详见 [基本对象与 Number](builtins.md)。`builtins.rs` 定义字符串、字典、Cell、函数和异常数据;`number.rs` 定义无损数字表示;`store_builtins.rs` 提供容器操作及关联对象校验。
@@ -12,15 +12,15 @@
```text
EndorObject
header 身份、总强引用数、外部强引用数
header 身份、总强引用数、外部强引用数、变更版本、可选类型引用
payload 原生 Rust 数据及操作接口
attributes VM 管理的可变属性表
payload_edges 分配时记录的载荷引用快照
```
`ObjectHeader` 不保存语言类型指针。`identity` 是 ObjectRef,用于识别同一个对象。`strong_refs` 是所有强引用的总数,`external_refs` 统计宿主持有和执行帧持有的根引用,也是循环回收的根来源。
`ObjectHeader.type_object` 是可选的、受 GC 追踪的类型对象引用,普通实例附加后不可更改。`identity` 是 ObjectRef,用于识别同一个对象。`strong_refs` 是所有强引用的总数,`external_refs` 统计宿主持有和执行帧持有的根引用,也是循环回收的根来源。
`payload` 是 `Box<dyn ObjectPayload>`。Box 保存原生数据,dyn trait 让运行时通过统一接口调用不同 Rust 实现。Rust 会为 trait 分发生成所需机制;不需要创建一个 Python 式的 Type 对象。`kind_name` 只用于诊断,`as_any` 只用于原生 Rust 扩展判断自己能否处理另一份载荷。
`payload` 是 `Box<dyn ObjectPayload>`。Box 保存原生数据,dyn trait 让运行时通过统一接口调用不同 Rust 实现。Rust 会为 trait 分发生成所需机制;其原生操作接口与托管 Type 身份分别保存。`kind_name` 只用于诊断,`as_any` 只用于原生 Rust 扩展判断自己能否处理另一份载荷。
`attributes` 保存字段名和 Value。对象值会被 VM 计为内部强引用;字符串和整数等直接保存。载荷可选择是否允许属性,例如 Record 允许,NativeFunction 默认不允许。
@@ -77,7 +77,7 @@ ObjectPayload 提供以下默认行为,载荷可以逐项覆盖:
Record 使用身份语义并开启属性。NativeFunction 保存名称和 Rust 函数指针,覆盖 repr 和 call。原生函数收到 Value 参数并返回 Result<Value, VmError>;VM 在调用前后检查参数和返回值中的对象句柄。
原生回调当前不能修改 VM 或直接在该 VM 中分配新对象;其参数和原始返回对象值是借用句柄。Vm::call 会验证并 retain 对象结果,对外统一返回一份持有式结果。语言函数已通过 [wordcode 执行器](bytecode.md) 使用显式帧、参数槽、闭包和求值栈执行;call/execute 的结果完成使用后须 release_value。
旧 call 回调不能修改 VM 或分配新对象;需要这些能力的原生方法可使用 NativeCallable 与受限 NativeContext。两种原生入口都接受借用参数,Vm::call 对外统一返回一份持有式结果。上下文临时根、非递归委托与捕获规则见 [原生调用](native-calls.md)。语言函数通过 [wordcode 执行器](bytecode.md) 使用显式帧、参数槽、闭包和求值栈执行;call/execute 的结果完成使用后须 release_value。
compare 的反向规则是 Lt 对应 Gt、Le 对应 Ge,Eq/Ne 保持不变。它不实现 Python 的子类型优先规则。内容比较由扩展定义;如果两个对象内容相等,它们必须给出相同哈希,或者明确拒绝哈希。
@@ -92,7 +92,7 @@ compare 的反向规则是 Lt 对应 Gt、Le 对应 Ge,Eq/Ne 保持不变。
引用计数无法自行释放 A → B → A,所以提供显式 `collect_cycles()`:
1. 把 external_refs 大于零的对象作为根。
2. 沿托管属性和载荷边标记所有可达对象。
2. 沿类型、托管属性和载荷边标记所有可达对象。
3. 先从槽位中摘除全部不可达对象。
4. 扣减垃圾指向幸存对象的引用,再 Drop 全部垃圾。
@@ -100,8 +100,8 @@ compare 的反向规则是 Lt 对应 Gt、Le 对应 Ge,Eq/Ne 保持不变。
## 当前边界
已实现:统一对象结构、稳定地址、代际句柄、身份、显式引用计数、托管属性、原生操作覆盖、比较与哈希、原生调用、资源清理和显式循环回收。
已实现:统一对象结构、稳定地址、代际句柄、身份、显式引用计数、托管属性、原生操作覆盖、比较与哈希、原生调用、资源清理和显式循环回收。另有受限原生可变上下文、安全弱引用、托管 Cell 赋值及有界对象变更记录,见 [原生调用](native-calls.md)。
尚未实现:Python type 系统、语言根类、继承和描述符、可变 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)。
尚未实现: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)。
+56
View File
@@ -0,0 +1,56 @@
# 托管名义类型
类型与实例保存在同一个 ObjectStore 中。此处是已实现的运行时机制;源码普通 `class`、字段构造和继承方法分派已经接入;trait、原生类分配继承和完整初始化协议尚未接入,不把 Rust 的 ObjectPayload 当成 Endor 的类定义。
## 创建与关系
ObjectStore/Vm 仍从空堆启动。宿主显式调用 `bootstrap_types()` 得到 Object 和 Type 两个类型对象,每个返回字段各拥有一份外部根,需要分别释放。
- Object 没有父类型。
- Type 的父类型是 Object。
- Object 的实例类型是 Type。
- Type 的实例类型也是 Type。
`new_type(name, base, constructor)` 创建一份新的名义定义,并沿 base 取得元类型。名字相同不代表类型相同。base 在创建时固定,不能通过替换载荷或修改属性改变,因此继承链不会因后续修改出现循环。`attach_type(instance, definition)` 只允许给尚未附加类型的对象设置一次类型,验证同堆、存活状态和 Type 载荷。已有 `type` 普通字段时拒绝附加,避免遮蔽数据。旧的原生对象可以继续保持未分类。
`type_of` 返回借用式类型引用;`is_subtype(candidate, expected)` 检查名义父链,含自身;`is_instance(instance, expected)` 先取得实例类型,再检查父链。非法句柄和非 Type 参数报错,不返回假值来掩盖错误。这些接口目前面向堆对象,直接保存在 Value 中的标量尚未统一接入。
## 源码可见操作与构造
带类型的对象通过 `get_attr(object, "type")` 暴露类型对象,不能写入或删除。Type 对象额外提供只读的 `name`、`base`;根 Object 的 base 为 Nil。源码前端已有的成员读取和调用表达式可以直接使用这些对象。类型名称由宿主注册到全局字典,Aster 不硬编码 Block 或 Page。
TypeConstructor 三种入口:
- Record:按声明字段接收位置参数,创建带此类型的普通 Record 实例;无字段时不接收参数。
- Native(callable):通过现有显式 VM 调用循环委托给托管可调用对象;宿主构造器负责返回该类型的实例。
- Abstract:拒绝直接构造。
原生 Type 调用通过 NativeContext::callee 取得当前定义,委托计入现有指令限额,不递归调用 Rust 求值器。尚不提供构造后初始化 continuation。
共享引用载荷可显式实现 `type_target(store)`:外壳身份保持稳定,类型查询沿当前被引用对象解析。目标必须由已登记的托管边保活;查询验证句柄并检测循环。普通实例使用固定 header 类型。Tessera 的 Block 共享赋值选择此协议,使 TextBlock 引用换成 RectBlock 后,类型查询与实际展示一致。这不是修改类继承链。
## 生命周期
header 的类型引用、Type 的 base 和原生构造器都是计数和循环回收可见的强边。实例保活其定义;类型对象及元类型没有隐藏的全局根。释放宿主根后,仍有实例的类型保留,无人引用的定义、Object/Type 环和原生构造器捕获环可以一起回收。
远端回归用例见 [types.rs](../../crates/core/tests/types.rs),覆盖名义身份、关系区分、类型边保活与回收、只读元数据及外来句柄失败原子性。本地仅编译检查,不执行测试。
## 源码普通类与方法
```endor
class Counter {
value: Number
function read(self: Counter): Number { return self.value }
}
class DetailedCounter: Counter {
label: String
function read(self: DetailedCounter): Number { return 12 }
}
```
当前可用 `DetailedCounter(9, "demo")` 构造,位置参数依次对应继承字段及本类字段;`object.field = value` 经 StoreAttr 更新同一对象并记录属性变更。方法需显式声明 self;`object.read` 创建持有该接收者的绑定方法,保存后不因原变量重新赋值而失效。方法体通过 self 再次取方法时按实例实际类型分派,子类可覆盖方法。字段与方法不能相互重名,子类不能重新声明继承字段;类型身份仍按定义对象区分。
方法表本身是类型对象拥有的托管边。宿主 define_method 可显式替换条目,不改变类型身份、基类或已有绑定方法选中的函数。bind_attr 返回一份拥有式根;get_attr 返回借用值和未绑定方法,宿主不能把两者的调用/释放协议混用。positional_parameter_count 会扣除绑定的接收者,供宿主判断事件回调的剩余源码参数。
这是一份实现子集说明:构造器只按字段赋值,没有默认值、命名字段构造或隐式 init;字段/参数/返回注解保留名称,尚未实施类型检查。普通类当前只能继承普通 Record 分配的类,继承原生或抽象分配器明确报错,不能伪造带 Block 名义身份的普通 Record。trait、原生视觉子类和用户自定义测量/绘制协议仍待实现。正式语言文档的候选语法不因这一实现被自动确认为规范。