Implement EndorObject runtime operations and managed lifetimes without language types
Verify databases and deploy Aster VM / verify-and-deploy (push) Successful in 14m4s
Verify databases and deploy Aster VM / verify-and-deploy (push) Successful in 14m4s
This commit is contained in:
+27
-19
@@ -2,29 +2,37 @@
|
||||
|
||||
## 内核
|
||||
|
||||
`Vm` 拥有私有 `Heap`。堆中每个条目都是统一的 Rust `Object`:公共 `ObjectHeader` 保存类型句柄,`ObjectData` 保存类元数据或实例字段。`Class` 只是类载荷,不是独立于公共对象头的语言对象。实例不再重复保存类型,统一通过 `vm.type_of(ObjectRef)` 查询对象头;`vm.class`/`vm.instance` 读取相应载荷。
|
||||
当前任务是 Endor 的虚拟机实现层。`Vm::new()` 只创建空堆,不加载语言根类,不分配 Object/Type,也没有类创建、继承或元类 API。`EndorObject` 是公共运行时结构,不能与语言根类混为一谈。
|
||||
|
||||
`Vm::new` 通过私有 `Heap::bootstrap` 一次性建立 Object 和 Type。Object 无父类,类型为 Type;Type 的父类为 Object,类型为自身。初始化器在独占的空堆中计算两个槽位的私有句柄并填入完整对象,不使用裸指针或可空的临时类型引用;完整堆建立后才返回。不存在可观察的“半初始化 VM”,`bootstrap` 只返回已有根类句柄。此过程不依赖数据库、全局命名空间、网络或异步运行时。
|
||||
`EndorObject` 保存 `ObjectHeader`、`Box<dyn ObjectPayload>`、托管属性和载荷引用快照。对象头仅保存身份、总强引用数和宿主外部强引用数。操作通过 Rust trait 的分发机制选择,不需要 Python type 系统;kind_name 只是调试标签,as_any 仅供原生 Rust 扩展识别自身的数据结构。
|
||||
|
||||
```text
|
||||
堆条目:Object { header: ObjectHeader { class }, data: ObjectData }
|
||||
### 分配与句柄
|
||||
|
||||
对象 对象头中的类型 类载荷中的父类
|
||||
Object Type 无
|
||||
Type Type Object
|
||||
用户类 C Type 创建时指定的父类
|
||||
C 的实例 C 不适用
|
||||
```
|
||||
堆槽位为 `Option<Box<EndorObject>>`。Box 保持对象地址稳定,槽位向量扩容不移动实际对象。`ObjectRef` 由私有 VM 身份、索引、代数组成;每次访问先检查 VM,再检查范围、代数和对象是否存活。释放后槽位代数递增,耗尽后永久退役,不回绕。VM 身份由不回绕的原子计数器分配。无 unsafe、裸指针访问或外部句柄构造器。
|
||||
|
||||
`create_class(name, parent)` 校验已有父类后创建不可变描述,并在公共对象头中设置类型为 Type。父类不可事后改写,不能形成继承环;只有内核启动路径可以创建没有父类的类。精确名称 Object 和 Type 保留。`create_instance` 校验类句柄,再创建以该类为类型的对象头和独立字段表。当前禁止直接实例化 Type 或继承 Type,返回 `UnsupportedMetaclass` 且不分配对象,避免把类工厂与普通实例工厂混用;尚未实现自定义元类、类型调用、描述符、方法与继承属性查找。
|
||||
### 引用所有权
|
||||
|
||||
句柄结构是私有 VM 身份加 `usize` 槽位。VM 身份用原子计数器分配,耗尽时返回错误,不回绕复用。`Vec` 扩容会移动 Rust 对象,但外部不保存它们的地址,旧句柄继续通过索引访问。当前绝不删除槽位,所以无代际号。VM 不实现 Clone,复制句柄不能复制 VM 身份。
|
||||
allocate/new_object 返回一个外部强引用。ObjectRef 是可复制的借用句柄;复制或 Value::clone 不 retain。宿主通过 retain/release 显式持有与释放。头部总计数等于外部计数加来自托管属性及载荷引用快照的入边数。
|
||||
|
||||
检查顺序是归属、范围、种类。外部没有从整数构造句柄的 API;低层无效槽位与伪造类型用私有单元测试覆盖。通用句柄可通过 `as_class/as_instance` 做校验后的转换。不同 VM 的引用、已经销毁 VM 的引用,在新 VM 中都报 `ForeignHandle`。同一 VM 中不在有效槽位的引用报 `InvalidHandle`。写字段前检查目标和引用值,失败不会覆盖原字段。
|
||||
属性写入先验证目标、被引用对象和计数溢出,再增加新引用、替换属性、释放旧引用;同目标替换不会短暂释放对象。属性读取和原生调用返回借用值,宿主需要跨越所有者生命周期使用时应先 retain。没有外部引用时,release 返回 NoOwnedReference,不能误释放内部属性引用。release 不代表删除任意对象。
|
||||
|
||||
`Value` 中标量和字符串不强制堆分配,引用目前仅指类或实例。当前字符串值是按 Rust 所有权复制的文本,并非具有对象身份的字符串实例。`Float` 使用 Rust f64/PartialEq 行为,尚未作为最终语言语义承诺。统一对象头适用于堆对象,不表示所有标量都已对象化,也不复制 CPython 的引用计数或内存布局。Type 的自身类型引用是对象图中的环,继承关系仍然无环。
|
||||
载荷通过 trace 报告自己持有的引用;分配时验证全部边并记录快照,相同引用出现两次就计数两次。分配失败不改变已存在对象的计数。载荷持有的边在分配后必须保持不变,可变引用通过 VM 托管属性修改。扩展未声明的借用句柄不受 GC 保护,访问或返回时仍需通过 VM 验证。
|
||||
|
||||
没有 GC,循环引用不形成 Rust 引用计数环,因为边只是句柄;所有内存随 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 中标量和字符串不强制堆分配。Float 暂用 Rust f64 语义;完整字符串、数值对象和语言类型规则尚未定义。更详细的代码说明见 endor-object.md。
|
||||
|
||||
## 宿主与持久化
|
||||
|
||||
@@ -34,12 +42,12 @@ SQLite 文件模式自动创建父目录、开启 WAL,连接池最多 5 条连
|
||||
|
||||
PostgreSQL 最多 5 条连接,获取/建立连接超时 5 秒,启动真实连接而非惰性连接。其配置失败或初始化失败均上抛,不进入 SQLite 分支。SQLite 的缺省只发生在**未配置** DATABASE_URL 时。
|
||||
|
||||
元数据仅供宿主管理,不代表类、实例、句柄或整堆快照。重启宿主会创建新的 VM、新的 Object/Type 和新句柄,只保留文件/数据库内的元数据。服务仅写入 `host.version`,不会把句柄调试值写入数据库。
|
||||
元数据仅供宿主管理,不代表类、实例、句柄或整堆快照。重启宿主会创建新的空 VM;后续对象获得新身份和新句柄,只保留文件/数据库内的元数据。服务仅写入 `host.version`,不会把句柄调试值写入数据库。
|
||||
|
||||
`aster-server` 保存只读 VM 和共享 Storage。GET /bootstrap 只读根类;GET /health 探测数据库可用性。没有对象操作 API、语言执行 API 或鉴权协议。HTTP/Tokio/SQLx 都不进入内核。后续浏览器宿主应通过明确的能力接口桥接,不能把浏览器类型塞进核心对象工厂。
|
||||
`aster-server` 保存只读 VM 和共享 Storage。GET /bootstrap 只读运行时标识和对象数量;GET /health 探测数据库可用性。没有对象操作 API、语言执行 API 或鉴权协议。HTTP/Tokio/SQLx 都不进入内核。后续浏览器宿主应通过明确的能力接口桥接,不能把浏览器类型塞进核心对象工厂。
|
||||
|
||||
## 已决定与未决定
|
||||
|
||||
已决定:唯一无父类 Object、基础元类 Type、堆对象统一对象头、类与实例使用不同载荷、类型与父类分开、句柄由 VM 校验、存储属于宿主。
|
||||
已决定:EndorObject 实现层基础结构、稳定 Box 分配、代际句柄、外部/内部引用计数、手动全堆循环回收、Rust 操作分发、存储属于宿主。
|
||||
|
||||
未决定:自定义元类、字节码格式、调用栈、方法解析、语言异常、外部对象协议、线程间共享 VM、GC/根集、堆序列化、浏览器通信、完整字符串对象/数值对象语义。不得在后续交接中把这些当成已实现契约。
|
||||
未决定:语言根类、Python type/元类体系、继承、字节码格式、调用栈、可变 VM 回调上下文、语言异常与终结器、外部对象协议、共享可变 VM、自动/分代 GC、堆序列化和浏览器通信。不得在交接中把这些当成已实现契约。
|
||||
|
||||
+11
-7
@@ -6,21 +6,25 @@
|
||||
|
||||
核心 API 示例在 `aster-core/src/lib.rs`,由 rustdoc 实际编译执行。公开语义测试在 `aster-core/tests/runtime.rs`;伪造句柄仅在私有堆单元测试中构造,禁止为测试添加公开非法构造器。
|
||||
|
||||
堆对象通过 `ObjectHeader` 统一保存类型,`Class`/`Instance` 仅为不同载荷。旧的 `vm.instance(handle)?.class()` 改为 `vm.type_of(handle.into())`,类对象也使用同一接口。启动分配两个基础对象:Object 与 Type;CLI 示例另建一个类和两个实例后共 5 个对象,HTTP `/bootstrap` 的初始对象数为 2。新增测试覆盖 Type 自身类型、类型与父类分离、跨 VM 类型查询、扩容后类型引用稳定,以及拒绝通过普通工厂创建自定义元类。
|
||||
用户已澄清:当前实现 Endor 的 VM 运行时,不实现 Python type 系统,也不定义语言根类。旧的 Class/Type、create_class、create_instance、root_class 和 type_of API 已移除。公共结构和操作接口在 object.rs,分配、引用计数和循环回收在 heap.rs,对外检查入口在 vm.rs。详见 endor-object.md。
|
||||
|
||||
关键所有权规则:分配给一个外部强引用;句柄拷贝与 Value 克隆只产生借用;额外持有使用 retain。托管属性和 trace 声明的不可变载荷边分别计数。get_attr/call 返回借用值。长链释放使用迭代队列;循环回收以外部引用为根。添加可变载荷引用时必须设计 VM 管理的更新协议,不能只改 trace 的返回结果。
|
||||
|
||||
CLI bootstrap 演示两个原生运行时对象并显式释放;HTTP /bootstrap 返回 object_model=EndorObject、type_system=false、heap_objects=0。修改宿主表示时同步 scripts/smoke.py。
|
||||
|
||||
存储测试用 `tempfile` 隔离文件,关闭池后重开,验证独立连接生命周期中的持久化。内存测试验证克隆共享、独立 store 隔离、特殊字符参数绑定、更新/缺失键。PostgreSQL 失败测试用只监听但不处理协议的本地 TCP socket,检验真实连接超时和不降级;成功测试显式 ignore,需要用户提供专用测试库。
|
||||
|
||||
尚未运行的冒烟脚本用于跨进程调用 CLI、启动 HTTP 宿主、探测真实 TCP 端点、发送 SIGTERM 并确认正常退出。它只创建临时目录,不改 `.env`,不操作外部服务器。端口通过临时绑定选择,释放后启动间存在很小的端口竞争窗口;在指定验证环境中若冲突,可重试整个脚本。
|
||||
远端冒烟脚本用于跨进程调用 CLI、启动 HTTP 宿主、探测真实 TCP 端点、发送 SIGTERM 并确认正常退出。它只创建临时目录,不改 `.env`,不操作外部服务器。端口通过临时绑定选择,释放后启动间存在很小的端口竞争窗口;在指定验证环境中若冲突,可重试整个脚本。
|
||||
|
||||
依赖使用 Cargo.lock。此前编译器为 1.91.1;更换依赖后须在指定环境检查 MSRV 和容器构建版本。核心测试无需数据库,但 Cargo 首次依赖解析仍可能访问注册表;不要因此在本机恢复下载或测试。
|
||||
|
||||
## 下一阶段建议
|
||||
|
||||
1. 先约定对象字段与方法查找规则、宿主调用的参数/返回值和错误边界,补语义测试。
|
||||
2. 在 core 内构建最小、可测的执行循环,输入先用手写指令,继续不做解析器和渲染器。
|
||||
3. 根据执行栈、对象图、宿主持有引用确定 GC 根集;如需回收槽位,升级代际/间接句柄模型并补陈旧句柄测试。
|
||||
4. 对外部对象、浏览器能力、配额与取消建立最小接口,以实际需求驱动,不预先堆砌空 trait。
|
||||
5. 需要升级元数据 schema 时引入可追踪的迁移;若要求 VM 快照,另行定义持久身份与格式,不能直接序列化现有句柄。
|
||||
1. 为执行栈与宿主持有值建立明确的强引用根管理,扩展原生调用上下文,补对应生命周期测试。
|
||||
2. 在 core 内构建可测的最小执行循环,先用手写指令,继续不做解析器和渲染器。
|
||||
3. 按实际分配负载设计 GC 触发、资源限额与暂停策略;现有手动循环回收不等于分代或增量 GC。
|
||||
4. 浏览器能力与外部对象桥接使用原生载荷;保持 core 无网络或宿主库依赖。
|
||||
5. VM 快照需要另行定义持久身份与格式,不能序列化当前进程句柄。语言类型、根类和继承以后再确定。
|
||||
|
||||
## Git 状态与后续远程
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# EndorObject 代码说明
|
||||
|
||||
当前实现的是 Endor 的 VM 运行时基础对象,角色类似 CPython 的公共对象结构。这里没有语言根类、元类、继承或 Python type 系统。标量仍然可以直接保存在 Value 中,不要求一切装箱。
|
||||
|
||||
## 代码分布
|
||||
|
||||
| 文件 | 职责 |
|
||||
| --- | --- |
|
||||
| `crates/aster-core/src/object.rs` | EndorObject、对象头、Value、操作接口和内置原生载荷 |
|
||||
| `crates/aster-core/src/heap.rs` | 稳定分配、句柄验证、引用计数、槽位复用、循环回收 |
|
||||
| `crates/aster-core/src/vm.rs` | 对外 API、错误处理和操作分发 |
|
||||
| `crates/aster-core/tests/runtime.rs` | 宿主可观察的对象行为和生命周期检查 |
|
||||
|
||||
## EndorObject 保存什么
|
||||
|
||||
```text
|
||||
EndorObject
|
||||
header 身份、总强引用数、外部强引用数
|
||||
payload 原生 Rust 数据及操作接口
|
||||
attributes VM 管理的可变属性表
|
||||
payload_edges 分配时记录的载荷引用快照
|
||||
```
|
||||
|
||||
`ObjectHeader` 不保存语言类型指针。`identity` 是 ObjectRef,用于识别同一个对象。`strong_refs` 是所有强引用的总数,`external_refs` 是宿主持有数,也是循环回收的根来源。
|
||||
|
||||
`payload` 是 `Box<dyn ObjectPayload>`。Box 保存原生数据,dyn trait 让运行时通过统一接口调用不同 Rust 实现。Rust 会为 trait 分发生成所需机制;不需要创建一个 Python 式的 Type 对象。`kind_name` 只用于诊断,`as_any` 只用于原生 Rust 扩展判断自己能否处理另一份载荷。
|
||||
|
||||
`attributes` 保存字段名和 Value。对象值会被 VM 计为内部强引用;字符串和整数等直接保存。载荷可选择是否允许属性,例如 Record 允许,NativeFunction 默认不允许。
|
||||
|
||||
`payload_edges` 让原生载荷也可以持有对象。载荷的 trace 方法逐一报告这些边,分配器验证并保存快照;重复边分别计数。分配后这些载荷边必须保持不变;需要修改的对象关系放入托管属性。单纯复制一个借用句柄不算持有,也不保护它免于回收。
|
||||
|
||||
## 指针与句柄
|
||||
|
||||
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 在调用前后检查参数和返回值中的对象句柄。
|
||||
|
||||
原生调用当前不能修改 VM 或直接在该 VM 中分配新对象;对象参数和对象返回值是借用句柄。这个接口足够表达标量计算和已有对象返回,还没有语言执行栈、函数帧、闭包或字节码执行。
|
||||
|
||||
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 全部垃圾。
|
||||
|
||||
该过程会保留可达的环,回收无人持有的自环、互引用环,以及原生载荷与托管属性混合构成的环。收集返回回收数量,未自动按阈值触发。
|
||||
|
||||
## 当前边界
|
||||
|
||||
已实现:统一对象结构、稳定地址、代际句柄、身份、显式引用计数、托管属性、原生操作覆盖、比较与哈希、原生调用、资源清理和显式循环回收。
|
||||
|
||||
尚未实现:Python type 系统、语言根类、继承和描述符、语言执行器、可变 VM 调用上下文、自动/分代/增量 GC、语言终结器/复活、资源限额和 VM 快照。数据库仍然只保存宿主元数据。
|
||||
|
||||
实际测试结果以本次提交对应的远端 Gitea CI 为准,不在本地构建、测试或下载依赖。
|
||||
@@ -54,3 +54,10 @@
|
||||
本次把堆条目改为 `ObjectHeader` 加载荷,所有类与实例通过同一对象头引用类型;启动同时建立 Object 和 Type。新增或更新类型引导、类型查询、无效句柄、跨 VM 隔离、工厂边界、扩容稳定性及文档示例的检查,并同步远端 CLI/HTTP 冒烟的对象数预期。
|
||||
|
||||
本次未在本地运行 Rust 构建、测试、格式化脚本或冒烟脚本,也未下载依赖。本记录提交时尚未取得新版本远端结果,实际通过与否以本次提交对应的 Gitea CI 记录为准;之前已成功的 #160/#161 仅验证旧版对象模型,不代表本次修改已通过。
|
||||
|
||||
|
||||
## EndorObject 运行时边界修正(2026-10-18)
|
||||
|
||||
用户明确当前目标是实现层公共对象,不需要 Python type 系统。移除语言 Object/Type 引导与类工厂,新增 EndorObject 原生操作、稳定 Box、代际槽位、外部/内部引用计数、迭代释放及显式循环回收;CLI/HTTP 冒烟改为运行时对象演示与空堆信息。
|
||||
|
||||
本次针对身份与地址稳定、持有与借用、属性替换与删除、错误写入原子性、长链释放、可达环保护、不可达环清理、载荷边计数、反向比较、原生调用错误和 VM 销毁资源释放编写检查。提交时未取得本版远端结果,以对应 Gitea CI 为准。没有在本地运行构建、测试、冒烟,也没有下载依赖。旧版 #162 已成功,但不代表本次修正通过。
|
||||
|
||||
Reference in New Issue
Block a user