Organize living documentation and directory guides
Verify databases and deploy Aster VM / verify-and-deploy (push) Successful in 13m40s

This commit is contained in:
2026-10-18 20:58:42 +08:00
parent 6fa95b64b0
commit 8bbe78e01e
30 changed files with 674 additions and 396 deletions
+5
View File
@@ -0,0 +1,5 @@
# Gitea 配置
本目录保存仓库自己的自动化定义。目前仅有 [workflows](workflows/README.md),不保存服务器凭据或共享 Runner 配置。
main 推送沿用 ces-control 验证和部署;机制见 [部署](../docs/operations/deployment.md),结果边界见 [验证](../docs/operations/validation.md)。返回 [项目入口](../README.md)。
+9
View File
@@ -0,0 +1,9 @@
# CI 工作流
[ci-deploy.yml](ci-deploy.yml) 是当前唯一工作流:main push 或 workflow_dispatch 触发,运行于 ces-control,生产并发组串行且不取消正在运行的发布,任务超时 60 分钟。
顺序为检出 → ci-ces-control.sh 验证 → 构建 SHA 生产镜像 → main 分支执行 deploy-ces-control.sh。设置 DOCKER_BUILDKIT=0 使用当前 builder 路径,检出不保留认证信息;工作流不更改共享 Runner。
脚本职责见 [scripts](../../scripts/README.md),完整发布、保留数据和回退规则见 [部署](../../docs/operations/deployment.md)。main 推送会进入现有发布链,不能把推送成功写成 CI 或部署成功;按用户约定不主动跟踪状态。
修改步骤后同步本页及对应全局主题。返回 [Gitea 配置](../README.md)。
+29 -165
View File
@@ -1,174 +1,38 @@
# Aster VM
# Endor / Aster VM
Rust 编写的可嵌入运行时。当前实现 Endor 的 VM 基础设施:`EndorObject`、稳定对象分配、代际句柄、显式引用计数、循环回收和 Rust 原生操作分发。VM 从空堆启动,暂不实现 Python 的 type/元类体系,也不定义语言根类和继承规则。
Rust 编写的 Endor 虚拟机实现。当前具备 EndorObject、稳定对象分配、代际句柄、引用计数、显式循环回收,以及可执行语言函数的小型 wordcode VM。数字统一为无损表示优先的 Number。
已具备小型 wordcode 执行器,尚无词法/语法前端或渲染器,不承诺 Python 兼容。数据库只保存宿主元数据,不保存或恢复 VM 堆。对象基础层的逐段说明见 [EndorObject](docs/endor-object.md)。
VM 从空堆启动;尚无源码 lexer/parser、Python type/元类体系、数值算术或完整异常传播。宿主数据库保存元数据,不保存 VM 堆。当前边界见 [系统架构](docs/architecture/README.md)。
现有基础对象包括 `EndorFunctionObject`、`EndorNativeFunctionObject`、`EndorDictObject`、`EndorCellObject`、`EndorBaseExceptionObject`、`EndorStringObject` 和 `EndorNumberObject`。统一 `EndorNumber` 优先无损表示任意长度整数、精确十进制数、分数、IEEE 浮点和复数;不先实现重计算。语言函数现可执行验证后的 wordcode,异常目前仍是数据基础,尚无 raise/try/except。接口、示例与边界见 [基本对象和 Number](docs/endor-builtins.md) 和 [字节码 VM](docs/bytecode-vm.md)。
## 从哪里读
## 结构
| 目录 | Cargo 包名 | 职责 |
| --- | --- | --- |
| `crates/core` | `aster-core` | 零外部依赖的 EndorObject、操作接口、引用计数、循环回收与 VM |
| `crates/storage` | `aster-storage` | SQLx PostgreSQL/SQLite 适配、元数据表初始化、读写接口;不依赖内核 |
| `crates/cli` | `aster-cli` | `aster bootstrap` 演示,以及宿主元数据读写 |
| `crates/server` | `aster-server` | 最小常驻宿主,提供健康检查与运行时信息,供 Docker 等方式运行 |
依赖方向:CLI/Server → Core 和 Storage;Core 与 Storage 互不依赖。未来浏览器宿主的网络、DOM、渲染等能力应留在宿主一侧。
## 直接运行
使用 Rust 1.91 或更高兼容版本;当前实测版本为 1.91.1,依赖锁定在 `Cargo.lock`。不需要 Docker。首次构建需要访问 crates.io。
```sh
cargo build --workspace --locked
cargo run -p aster-cli -- bootstrap
```
预期输出含 `runtime=EndorObject type_system=disabled`、`objects_distinct=true heap_objects=2` 和 `released heap_objects=0`。这条命令不读取数据库配置、不打开数据库;即使设置了错误的 `DATABASE_URL` 也不影响运行时演示。
```rust
use aster_core::{Value, Vm};
fn main() -> Result<(), aster_core::VmError> {
let mut vm = Vm::new()?;
let object = vm.new_object()?; // 分配返回一个宿主持有的强引用。
vm.set_attr(object, "x", Value::Number(42_i64.into()))?;
assert_eq!(vm.get_attr(object, "x")?, Value::Number(42_i64.into()));
vm.retain(object)?;
vm.release(object)?;
assert_eq!(vm.ref_count(object)?, 1);
vm.release(object)?; // 最后一个强引用释放,对象立即清理。
assert_eq!(vm.object_count(), 0);
Ok(())
}
```
嵌入时将 `aster-core` 加为 Cargo 路径依赖即可。上例与 crate 文档中的可运行示例语义相同。
## 宿主存储
不设置 `DATABASE_URL` 时,使用 `SQLITE_PATH` 指定的 SQLite 文件,默认 `data/aster.db`,相对于进程工作目录;自动创建父目录及文件。部署时必须把该目录放在持久卷上。`SQLITE_PATH=:memory:` 仅用于临时运行/测试,不持久化。
```sh
cargo run -p aster-cli -- metadata put project 'Aster VM'
cargo run -p aster-cli -- metadata get project
SQLITE_PATH=work/custom/aster.db cargo run -p aster-cli -- metadata put stage bootstrap
```
PostgreSQL 通过导出的 `DATABASE_URL=postgres://...` 或 `postgresql://...` 配置,优先于 `SQLITE_PATH`。显式空值、错误协议、连接失败都会返回错误并以非零状态退出,绝不静默降级。`DATABASE_URL` 不接受 SQLite URL;SQLite 使用独立路径配置。
连接成功后执行 `schema.sql` 中幂等的 v1 建表语句,提供 `Storage::put/get/ping/close`。读写使用参数绑定,重复键执行更新,缺失键返回 `None`。尚无复杂迁移版本管理。
Cargo 程序**不自动加载 `.env`**,请导出需要的环境变量。`.env.example` 是配置示例;Docker Compose 会自动读取复制后的 `.env`。连接 URL 可能含密码,不要贴到日志或提交到 Git。命令行只显示脱敏错误,底层诊断作为 Rust error source 保留给宿主。
## 常驻宿主
```sh
cargo run -p aster-server
curl --fail http://127.0.0.1:8080/health
curl --fail http://127.0.0.1:8080/bootstrap
```
`ASTER_LISTEN` 默认 `127.0.0.1:8080`。宿主启动时创建 VM、连接数据库、初始化表并写入 `host.version=0.1.0`;初始化失败即退出。`/health` 实际探测数据库,成功返回 200,失联返回 503;`/bootstrap` 返回 `object_model=EndorObject`、`type_system=false`、`heap_objects=0`,多次读取不分配对象。支持 Ctrl-C/Unix SIGTERM 优雅退出。
这两个端点不提供执行语言、对象写入或业务 API。服务没有认证层;示例 Compose 端口仅绑定宿主回环地址,外部访问由后续宿主部署决定。
## Docker:默认 SQLite
```sh
cp .env.example .env
docker compose up --build -d
curl --fail http://127.0.0.1:8080/health
docker compose exec aster aster metadata put project 'Aster VM'
docker compose exec aster aster metadata get project
docker compose down
docker compose up -d
docker compose exec aster aster metadata get project
```
容器以 UID 10001 运行,`aster-sqlite` 命名卷挂载到 `/data`,保存数据库和 WAL 文件。`down` 保留卷;`down -v` 会删除持久数据。构建采用多阶段 Dockerfile,运行镜像同时含 `aster-server` 和 `aster`。Compose 的 SQLite 方案不会透传宿主 `DATABASE_URL`,以保证默认方案明确使用 SQLite。
## Docker:选择 PostgreSQL
先将 `.env` 中的 `POSTGRES_PASSWORD` 换成随机十六进制字符串(可用 `openssl rand -hex 24` 生成)。该值同时用于数据库和连接 URL;使用其他字符时需要自行正确处理 URL 编码。
```sh
docker compose -f docker-compose.postgres.yml up --build -d
docker compose -f docker-compose.postgres.yml exec aster aster metadata put project 'Aster VM'
docker compose -f docker-compose.postgres.yml exec aster aster metadata get project
docker compose -f docker-compose.postgres.yml down
```
这是**独立替代方案**,只传入这一个 Compose 文件,不与默认文件叠加;两个方案不要同时占用同一个宿主端口。PostgreSQL 17 使用 `aster-postgres` 命名卷,数据库不向宿主公开端口。示例内部网络禁用数据库 TLS;跨不可信网络的部署应使用 PostgreSQL TLS 验证配置,例如 URL 参数 `sslmode=verify-full`。更改已有 PostgreSQL 卷的密码需要数据库管理操作,修改 `.env` 不会修改旧卷中的用户密码。
## ces-control CI/CD 与持久化部署
正式部署选择 PostgreSQL;默认 SQLite Compose 仍保留作为独立运行选项。
`.gitea/workflows/ci-deploy.yml` 在 `ces-control` Runner 上响应 `main` push 和手动触发:先在远端 Docker 容器中执行 fmt、workspace 测试、严格 Clippy、真实 PostgreSQL 集成测试和 SQLite/HTTP 冒烟检查,通过后构建生产镜像,再部署。使用独立的临时 PostgreSQL 容器做测试,不连接生产数据库。测试容器与网络在退出时清理,编译缓存仅保留在远端专属 Docker 卷中。
正式服务由 `docker-compose.ces-control.yml` 管理,默认仅绑定 `127.0.0.1:18081`,内部连接 PostgreSQL 17。应用/数据库使用 `unless-stopped` 自动重启,日志轮换;数据库不发布端口。部署使用 commit SHA 标记镜像与 release 目录,健康检查或元数据读写失败时尝试恢复上一版应用,保留数据库。
| 服务器路径 | 用途 |
| 要做的事 | 入口 |
| --- | --- |
| `/opt/endorphin-aster-vm/releases/<sha>/` | 固定版本的 Compose 与镜像配置 |
| `/opt/endorphin-aster-vm/current` | 最近健康版本的符号链接 |
| `/etc/endorphin-aster-vm/production.env` | 首次自动生成的数据库密码和端口,权限 0600 |
| `/var/lib/endorphin-aster-vm/postgres` | PostgreSQL 持久数据,替换应用/数据库容器后保留 |
| 查一个功能、设计或代码位置 | [文档总索引](docs/README.md) |
| 看源码目录与 crate 边界 | [crates](crates/README.md) |
| 看指令、代码对象、执行帧和执行循环 | [字节码 VM](docs/runtime/bytecode.md) |
| 看 VM/帧/对象如何存活和销毁 | [生命周期](docs/runtime/lifecycle.md) |
| 看配置、UTF-8 与 locale 边界 | [配置](docs/operations/configuration.md) |
| 运行 CLI/HTTP 宿主 | [运行指南](docs/operations/README.md) |
| 看 ces-control CI 与 PostgreSQL 部署 | [部署](docs/operations/deployment.md) |
| 修改代码并同步文档 | [开发与文档维护](docs/contributing/README.md) |
无需在 Gitea 中设置生产数据库密码。不要删除或重新生成已有数据库对应的 `production.env`;修改密码需先通过 PostgreSQL 管理操作完成同步。首次部署没有可用的旧版可以回退;数据库 schema 的未来升级也需要另行设计兼容性,应用回退不等于回退数据库。
## 根目录内容
在服务器查看/停止当前部署(停止不删除数据):
| 文件或目录 | 职责 |
| --- | --- |
| Cargo.toml / Cargo.lock | 工作区、包依赖、Rust 版本要求及依赖锁定;目录名无 aster- 前缀,包名保留 |
| crates/ | core、storage、cli、server 四个包,每个目录的 README 解释本目录 |
| docs/ | 跨目录的架构、运行时契约、配置、运行、部署与维护说明 |
| scripts/ | 指定远端环境中的检查、冒烟、CI 与部署脚本 |
| .gitea/ | Gitea Actions 工作流 |
| Dockerfile | source、ci、build、runtime 多阶段镜像 |
| docker-compose.yml | 独立 SQLite 运行方案 |
| docker-compose.postgres.yml | 独立 PostgreSQL 运行方案 |
| docker-compose.ces-control.yml | ces-control 正式 PostgreSQL 部署 |
| .env.example / .gitignore / .dockerignore | 配置样例、Git 与 Docker 上下文排除规则 |
| LICENSE | MIT 许可 |
```sh
docker compose -p endorphin-aster-vm \
--env-file /etc/endorphin-aster-vm/production.env \
--env-file /opt/endorphin-aster-vm/current/release.env \
-f /opt/endorphin-aster-vm/current/compose.yml ps
curl --fail http://127.0.0.1:18081/health
curl --fail http://127.0.0.1:18081/bootstrap
# 同样参数接 stop 停服,接 up -d --wait 恢复。
```
本地不编译、不测试、不下载依赖;可做文档和静态文件检查。运行验证交给 ces-control;按用户约定不主动跟踪 CI/部署结果。命令和使用条件见 [验证](docs/operations/validation.md)。
本机不执行上述 CI 测试,也不为其安装 Rust/SQLite/PostgreSQL。正式部署与测试只运行在用户指定的 `ces-control`。该工作流不改动 `autodl-gpu-runner-controller`、rednote 或 TiktokUI。工作流语义依据 [Gitea Actions 文档](https://docs.gitea.com/usage/actions/),环境文件加载依据 [Docker Compose 文档](https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/)。
## 验证(在用户指定的环境执行)
当前工作约定禁止在本地运行测试,也不为本地测试下载依赖。下面的脚本和示例在指定的 ces-control CI/部署环境执行,不代表每次修改后已全部执行。实际结果及此前清理情况见 [验证记录](docs/validation.md)。
```sh
sh scripts/check.sh
python3 scripts/smoke.py
```
检查脚本运行 fmt、workspace 测试和拒绝所有 warning 的 Clippy;冒烟脚本使用临时 SQLite 文件,验证独立引导、跨进程持久化、配置错误、HTTP 端点和 SIGTERM 退出,不需要外部数据库。
PostgreSQL 集成测试默认显式忽略,需要测试数据库。以下是指定验证环境中的 Docker 示例;仅操作专用测试容器,不使用生产库,当前不在本机执行:
```sh
docker run --rm -d --name aster-pg-test \
-e POSTGRES_PASSWORD=aster-test -e POSTGRES_USER=aster -e POSTGRES_DB=aster \
-p 127.0.0.1:55432:5432 postgres:17-bookworm
# 等待以下命令成功后再运行测试:
docker exec aster-pg-test pg_isready -U aster -d aster
ASTER_TEST_POSTGRES_URL='postgres://aster:aster-test@127.0.0.1:55432/aster?sslmode=disable' \
cargo test -p aster-storage --test postgres --locked -- --ignored
docker stop aster-pg-test
```
测试会留下唯一键的元数据,专用测试库可随容器删除。缺少测试 URL 时显式运行该测试会失败,不会假装通过。真实 PostgreSQL 成功路径及容器结果以 [验证记录](docs/validation.md) 为准。
## 当前语义与后续开发
- `EndorObject` 是 Rust 运行时公共对象结构,包含身份、引用计数、原生载荷和托管属性。没有语言类或类型指针;`ObjectPayload` 是 Rust 操作接口,诊断名称不参与语言类型判断。
- `ObjectRef` 包含私有 VM 身份、槽位和代数。`Box<EndorObject>` 保持存活对象地址稳定;槽位复用更新代数,旧句柄不会指向新对象。外部不构造堆索引,也不操作裸指针。
- 分配返回一个外部强引用;复制句柄或含句柄的 `Value` 不增加计数。额外持有使用 `retain`,结束持有使用 `release`。对象属性和载荷声明的边各自持有内部强引用;读属性返回借用值,需长期持有时显式 retain;call/execute 的对象结果持有一份强引用,结束使用调用 release_value。
- 最后一个强引用释放时,迭代清理依赖对象并调用 Rust Drop。`collect_cycles()` 从外部强引用追踪,显式回收不可达环;不会自动定时执行。销毁 VM 时统一释放剩余对象。
- 支持 repr/str、身份与内容比较、哈希、真值、属性读写删除、原生函数调用。操作由 Rust 载荷实现;不支持的操作返回错误。原生回调可返回标量或已有对象的借用句柄,由 VM 保活后作为持有式结果返回;当前不能在回调中修改 VM。语言函数通过显式执行帧运行 wordcode。
- 标量与字符串可直接保存在 `Value`,数字统一为 `Value::Number`;需要对象身份时可分配 String/Number 对象。字典和 Cell 支持 VM 管理的可变引用;代码对象和函数共享不可变代码并参与 GC,执行栈管理对象根。数值比较/哈希、异常继承与传播、语言根类、元类、描述符及 VM 堆持久化留待以后实现。
下一阶段可扩充指令和源码编译入口,并按需求增加可变 VM 宿主调用上下文;GC 已有手动全堆版本,尚无分代、增量、并行收集或语言终结器/复活语义。详见 [架构](docs/architecture.md)、[对象基础层](docs/endor-object.md) 与 [开发交接](docs/development.md)。
依赖 API 依据 [SQLx 0.8.6 官方文档](https://docs.rs/sqlx/0.8.6/sqlx/) 和 [Axum 官方文档](https://docs.rs/axum/0.8.9/axum/) 实现。SQLx 0.9 要求更高 Rust 版本,本阶段使用与本机工具链兼容的 0.8.6;确切解析版本以锁文件为准。
文档随代码版本更新:就地解释放各目录 README,跨目录契约放 docs,对外阅读副本以仓库文档为准。忽略的 work、outputs、target、data 和 Git 内部目录不属于需要维护 README 的源码目录。
+12
View File
@@ -0,0 +1,12 @@
# Cargo 工作区
本目录包含四个 crate。目录名不用 aster- 前缀,Cargo 包名和二进制名保留现有名称;路径依赖定义在根 Cargo.toml。
| 目录 | 包名/产物 | 职责 |
| --- | --- | --- |
| [core](core/README.md) | aster-core | 零外部依赖的对象运行时、堆与字节码 VM |
| [storage](storage/README.md) | aster-storage | 宿主元数据与 SQLite/PostgreSQL 适配,不依赖 core |
| [cli](cli/README.md) | aster-cli,二进制 aster | 引导演示和 metadata 命令 |
| [server](server/README.md) | aster-server | 最小常驻 HTTP 宿主 |
CLI/Server 依赖 Core 和 Storage,Core 与 Storage 互不依赖。网络、数据库、浏览器、进程配置和信号留在宿主。全局说明见 [架构](../docs/architecture/README.md),查功能位置从 [文档索引](../docs/README.md) 进入。返回 [项目入口](../README.md)。
+10
View File
@@ -0,0 +1,10 @@
# aster-cli / aster
命令行宿主。bootstrap 演示两个运行时对象并释放;metadata put/get 使用独立 Storage,不让数据库配置进入 Core。
| 内容 | 职责 |
| --- | --- |
| Cargo.toml | CLI 包、aster 二进制名称、core/storage/Tokio 依赖 |
| [src](src/README.md) | 参数路由、命令执行和退出码 |
用法见 [运行指南](../../docs/operations/README.md),文本参数与环境边界见 [配置](../../docs/operations/configuration.md)。此 CLI 当前不是 Endor 源码解释命令。返回 [工作区](../README.md)。
+7
View File
@@ -0,0 +1,7 @@
# CLI 入口
[main.rs](main.rs) 是唯一入口:main 将 run 的结果转成进程退出码;run 解析 bootstrap、metadata put/get 和 help,错误打印到 stderr 并返回失败。
bootstrap 创建并显式释放两个对象,不打开数据库。metadata 每次读取 StorageConfig、连接、操作并关闭池。参数目前由 std::env::args 读取,非 Unicode 参数可能 panic,现状及后续边界见 [配置](../../../docs/operations/configuration.md)。
新增命令时更新用法、[运行指南](../../../docs/operations/README.md) 和 [smoke.py 的导航](../../../scripts/README.md)。返回 [cli](../README.md)。
+13
View File
@@ -0,0 +1,13 @@
# aster-core
Endor VM 内核,保持零外部依赖。负责 EndorObject、堆、代际句柄、引用计数/循环回收、基本载荷和小型 wordcode 执行器。公共 Rust API 从 src/lib.rs 导出。
| 内容 | 入口 |
| --- | --- |
| Cargo.toml | 包元数据和工作区 lint;没有外部依赖 |
| [src](src/README.md) | 文件职责、公开接口和常见修改位置 |
| [tests](tests/README.md) | 对象、基本载荷、执行语义与生命周期检查 |
内核通过 Vm::new 从空堆启动;代码对象和全局字典由宿主显式创建,再 execute/call。没有环境变量探测、数据库、HTTP、Python type 系统或源码前端。
对象结果所有权见 [生命周期](../../docs/runtime/lifecycle.md),对象/字节码设计见 [运行时](../../docs/runtime/README.md),限额与 UTF-8 边界见 [配置](../../docs/operations/configuration.md)。本地不构建或运行测试,验证入口见 [验证](../../docs/operations/validation.md)。返回 [工作区](../README.md)。
+29
View File
@@ -0,0 +1,29 @@
# Core 源码导航
本目录解释实现文件如何分工;跨文件的设计规则只在 [运行时文档](../../../docs/runtime/README.md) 维护。公开 API 的最终导出入口是 lib.rs。
| 文件 | 内容 |
| --- | --- |
| [lib.rs](lib.rs) | 声明模块、公开类型导出、对象和字节码 rustdoc 示例 |
| [object.rs](object.rs) | Value、CompareOp、ObjectPayload、ObjectHeader、EndorObject、Record、NativeFunction |
| [heap.rs](heap.rs) | ObjectRef、稳定 Box 槽位、代际验证、引用事务、迭代释放、显式循环收集;含私有边界测试 |
| [vm.rs](vm.rs) | Vm/VmError、构造和对象操作入口、retain/release、循环回收入口 |
| [builtins.rs](builtins.rs) | String、Dict、Cell、Function、BaseException 载荷与 trace 边 |
| [number.rs](number.rs) | EndorNumber、Integer/Decimal/Rational/BinaryFloat/Complex 表示及装箱载荷 |
| [vm_builtins.rs](vm_builtins.rs) | 内置载荷校验、代码/函数工厂、Dict/Cell 操作、键语义和可变引用更新 |
| [instruction.rs](instruction.rs) | Opcode、逻辑指令、EXTENDED_ARG 编解码、指令位置与比较参数 |
| [code.rs](code.rs) | CodeLayout、EndorCode、共享 EndorCodeObject、操作数/控制流/栈校验 |
| [frame.rs](frame.rs) | EndorFrame、栈条目、执行位置元数据、ExecutionLimits |
| [eval.rs](eval.rs) | execute/call、参数绑定、帧切换、取指分派、结果根转移与失败清理 |
## 常见修改入口
| 要修改什么 | 涉及文件 | 同步说明 |
| --- | --- | --- |
| 对象公共操作或头部 | object.rs、vm.rs、heap.rs | [对象模型](../../../docs/runtime/objects.md) |
| 内置载荷、无损数字或容器行为 | builtins.rs/number.rs、vm_builtins.rs | [基本对象](../../../docs/runtime/builtins.md) |
| 新操作码 | instruction.rs、code.rs 的参数和栈验证、eval.rs 分派 | [字节码 VM](../../../docs/runtime/bytecode.md)、execution 测试 |
| 调用、闭包或返回值根管理 | frame.rs、eval.rs、函数 trace | [生命周期](../../../docs/runtime/lifecycle.md)、builtins 与 execution 测试 |
| 新公开 API | 对应实现模块与 lib.rs 导出 | 所属主题、示例、[tests 导航](../tests/README.md) |
复制 ObjectRef/Value 不增加引用;不能在自定义载荷中擅自改变已登记的托管边。对象 API 不暴露原始指针或句柄索引构造。返回 [core](../README.md)。
+11
View File
@@ -0,0 +1,11 @@
# Core 公开行为检查
本目录通过 aster_core 公共 API 检查宿主可观察的语义,不为检查开放内部句柄构造器。
| 文件 | 覆盖 |
| --- | --- |
| [runtime.rs](runtime.rs) | 空堆启动、默认操作、引用/借用、属性原子性、稳定地址、长链/循环、原生回调、VM 销毁 |
| [builtins.rs](builtins.rs) | 无损 Number、Unicode 字符串、Dict 顺序与键、Cell、函数环境/共享代码、异常数据 |
| [execution.rs](execution.rs) | wordcode 与校验、控制流、参数/默认值、嵌套帧、闭包、返回值存活、失败清理、限额 |
堆内部伪造句柄与计数溢出检查在 src/heap.rs,公开示例由 src/lib.rs rustdoc 提供。文件职责见 [src](../src/README.md),检查方式和结果边界见 [验证](../../../docs/operations/validation.md)。本地不执行测试;存在测试文件不代表某次提交已通过。返回 [core](../README.md)。
+10
View File
@@ -0,0 +1,10 @@
# aster-server
最小常驻 HTTP 宿主,提供数据库健康检查和只读 VM 引导信息。它通过 Arc 共享 Vm,没有语言执行接口或可变 VM 请求上下文。
| 内容 | 职责 |
| --- | --- |
| Cargo.toml | core/storage、Axum、Tokio、Serde 依赖 |
| [src](src/README.md) | 启动顺序、AppState、路由、信号与退出 |
HTTP 返回内容和运行方式见 [运行指南](../../docs/operations/README.md),变量见 [配置](../../docs/operations/configuration.md),生产端口/数据库见 [部署](../../docs/operations/deployment.md)。返回 [工作区](../README.md)。
+14
View File
@@ -0,0 +1,14 @@
# Server 入口
[main.rs](main.rs) 包含当前所有宿主启动代码:
| 部分 | 职责 |
| --- | --- |
| AppState | 共享只读 Arc<Vm> 与可克隆的 Storage 连接池 |
| main/run | 创建 VM、连接数据库、写 host.version、绑定地址、组装路由、服务结束后关闭存储 |
| health | ping 数据库,返回 200/503 及后端标签 |
| bootstrap | 返回对象模型、type_system=false 和堆对象数量,不分配对象 |
| shutdown_signal | Ctrl-C;Unix 上同时接受 SIGTERM,触发 Axum 优雅退出 |
| tests | 关闭数据库池后健康状态变为 unavailable |
改变端点时同步 [运行指南](../../../docs/operations/README.md) 与 [smoke.py](../../../scripts/README.md);改变启动变量时同步 [配置](../../../docs/operations/configuration.md)。网络、信号和数据库逻辑不进入 Core。返回 [server](../README.md)。
+11
View File
@@ -0,0 +1,11 @@
# aster-storage
宿主元数据存储,依赖 SQLx/Tokio,不依赖 VM 内核。一个 Storage 选择 SQLite 或 PostgreSQL;明确配置 PostgreSQL 后失败不回退。
| 内容 | 入口 |
| --- | --- |
| Cargo.toml | SQLx、Tokio 和检查所需依赖 |
| [src](src/README.md) | 配置解析、连接池、元数据表和操作 |
| [tests](tests/README.md) | 配置、持久化、隔离与专用 PostgreSQL 检查 |
Storage 仅保存 key/value 元数据,不保存 EndorObject 或执行帧。运行与连接规则见 [运行指南](../../docs/operations/README.md),环境优先级与错误规则见 [配置](../../docs/operations/configuration.md),生产数据与迁移边界见 [部署](../../docs/operations/deployment.md)。返回 [工作区](../README.md)。
+10
View File
@@ -0,0 +1,10 @@
# Storage 源码导航
| 文件 | 职责 |
| --- | --- |
| [lib.rs](lib.rs) | StorageConfig::from_values/from_env、StorageError、SQLite/PostgreSQL 连接池、schema 初始化、ping/put/get/close |
| [schema.sql](schema.sql) | 当前 aster_metadata 文本 key/value 表,幂等建表 |
配置改动同步 [配置表](../../../docs/operations/configuration.md) 与 storage 测试。schema 改动须考虑已有数据库,当前没有版本化迁移;不得将 CREATE IF NOT EXISTS 当作已有结构升级。Storage 克隆共享连接池,close 影响所有克隆。
连接与持久化契约见 [运行指南](../../../docs/operations/README.md),测试定位见 [tests](../tests/README.md)。返回 [storage](../README.md)。
+8
View File
@@ -0,0 +1,8 @@
# Storage 行为检查
| 文件 | 覆盖与前提 |
| --- | --- |
| [storage.rs](storage.rs) | 配置默认值/优先级/错误、内存共享与实例隔离、临时文件重连、目录错误;不处理 PostgreSQL 协议的 socket 仅检查连接超时和不降级 |
| [postgres.rs](postgres.rs) | 真实 PostgreSQL 写入/覆盖/缺失键/重连;默认 ignore,显式运行需 ASTER_TEST_POSTGRES_URL 指向专用测试库 |
SQLite 文件检查使用临时目录;PostgreSQL 成功路径不使用生产数据库。ces-control 脚本提供一次性的测试数据库,见 [验证](../../../docs/operations/validation.md) 和 [scripts](../../../scripts/README.md)。不在本地运行或为测试下载 SQLite 依赖。返回 [storage](../README.md)。
+41
View File
@@ -0,0 +1,41 @@
# 文档索引
这里保存与源码一起版本管理的全局说明。当前页面负责找入口;局部文件职责由对应代码目录 README 维护。阅读次序:系统架构 → 运行时主题 → 对应源码目录。运行部署则从 operations 进入。
## 按问题查找
| 想知道什么 | 权威说明 | 代码入口 |
| --- | --- | --- |
| 四个 crate 如何分层,哪些能力已实现 | [架构](architecture/README.md) | [crates](../crates/README.md) |
| EndorObject、对象头、句柄、默认操作 | [对象模型](runtime/objects.md) | [core/src](../crates/core/src/README.md) |
| Number、String、Dict、Cell、Function、异常数据 | [基本对象](runtime/builtins.md) | [core/src](../crates/core/src/README.md) |
| 操作码、代码验证、局部槽、调用协议 | [字节码 VM](runtime/bytecode.md) | instruction.rs / code.rs / frame.rs / eval.rs;见 [文件表](../crates/core/src/README.md) |
| VM、对象与执行帧何时创建/释放,循环如何回收 | [生命周期](runtime/lifecycle.md) | heap.rs / eval.rs;见 [文件表](../crates/core/src/README.md) |
| LC_CTYPE、UTF-8、环境变量和执行限额 | [配置](operations/configuration.md) | [core](../crates/core/README.md)、[storage](../crates/storage/README.md)、[server](../crates/server/README.md) |
| CLI/HTTP 怎么运行,存储保存什么 | [运行](operations/README.md) | [cli](../crates/cli/README.md)、[server](../crates/server/README.md) |
| Docker、PostgreSQL、ces-control、回退与数据目录 | [部署](operations/deployment.md) | [scripts](../scripts/README.md)、[工作流](../.gitea/workflows/README.md) |
| 检查覆盖什么,结果如何确认 | [验证](operations/validation.md) | [core/tests](../crates/core/tests/README.md)、[storage/tests](../crates/storage/tests/README.md) |
| 改代码后文档写在哪里、怎样避免失效 | [维护规则](contributing/README.md) | 各目录 README 与本索引 |
## 多层目录
| 目录 | 负责的内容 |
| --- | --- |
| [architecture](architecture/README.md) | 系统边界、依赖方向和跨组件约束 |
| [runtime](runtime/README.md) | 对象、基本载荷、字节码和生命周期 |
| [operations](operations/README.md) | 运行、配置、部署和验证 |
| [contributing](contributing/README.md) | 开发定位方法与随代码更新文档的规则 |
原 outputs/bytecode-vm.md 与 endor-builtins-design.md 已核对并融合到 runtime 下的对应主题;不再在 outputs 维护独立版本。原 docs 平铺文档已迁入这些主题。过时启动模型、旧测试通过数量和机器资源快照已移除;历史内容需要时查 Git 历史,不混入当前行为说明。
## 仓库内查询
在仓库根目录使用静态文本检索:
```sh
rg -n 'LC_CTYPE|UTF-8|DATABASE_URL' docs crates --glob '*.md'
rg -n 'EndorFrame|ReturnValue|collect_cycles' crates/core/src
rg --files -g README.md -g '!work/**' -g '!outputs/**' -g '!target/**'
```
没有额外的文档服务器或搜索依赖。新增主题时更新此表;新增受版本管理的目录时增加 README,并从父目录 README 链接过去。
-59
View File
@@ -1,59 +0,0 @@
# 架构与语义
## 内核
当前任务是 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、堆序列化和浏览器通信。不得在交接中把这些当成已实现契约。
+44
View File
@@ -0,0 +1,44 @@
# 系统架构
Endor 的目标是云原生语言;本仓库当前实现运行时和宿主演示,尚未提供源码编译器、云调度器或多租户执行沙箱。云原生方向在当前阶段落实为内核与进程配置分离、可预测的文本/数字表示、明确的数据持久化边界。
## 组件与依赖方向
```text
CLI ────→ Core
└─────→ Storage
Server ─→ Core
└─────→ Storage
```
Core 与 Storage 互不依赖。HTTP、Tokio、SQLx、数据库、网络和后续浏览器能力属于宿主,不进入零外部依赖的 aster-core。源码包导航见 [crates](../../crates/README.md)。
| 组件 | 当前职责 | 全局契约 |
| --- | --- | --- |
| Core | EndorObject、对象堆、Number、wordcode、执行帧、求值器 | [运行时](../runtime/README.md) |
| Storage | 一个确定后端上的宿主元数据读写 | [运行与存储](../operations/README.md) |
| CLI | 对象引导示例、元数据 put/get | [CLI](../../crates/cli/README.md) |
| Server | 数据库健康检查、只读运行时信息、进程退出 | [Server](../../crates/server/README.md) |
| Gitea/Docker | 远端验证、镜像构建、PostgreSQL 持久部署 | [部署](../operations/deployment.md) |
## 当前契约
- Vm 从空堆启动。EndorObject 是实现层公共结构,不是语言根类;当前没有 Python type、继承或元类。
- 堆中对象地址稳定,公开句柄校验 VM 身份和槽位代数;句柄不是跨进程 ID。
- 引用计数负责通常的释放,显式全堆追踪负责不可达循环;执行帧中的对象值也作为根。
- 函数共享不可变代码对象,语言调用使用显式帧向量;字节码参考 CPython 的职责划分,不兼容其操作码 ABI。
- Number 优先保存不同数字的无损表示;没有统一转 f64,也没有定义数值算术、相等和哈希。
- 异常对象保存数据;执行错误使用 VmError,没有 raise/try/except 传播。
- 内核不读取进程环境或修改 locale。宿主负责配置解析、I/O 和信号;详见 [配置](../operations/configuration.md)。
## 数据与服务边界
Storage 仅保存 `aster_metadata(key, value)`;连接时幂等创建当前 schema。文件与数据库中的元数据可以跨进程保留,VM 堆、执行帧、句柄不会因此恢复。宿主重启会创建新的空 VM。
HTTP 宿主通过 Arc 共享只读 Vm。GET /bootstrap 只读对象模型与堆数量,GET /health 实际探测数据库。当前没有语言执行端点、对象写入端点或认证协议;不能把现有服务称为远程执行平台。
## 后续扩展边界
源码前端、函数定义/迭代器指令、可变 VM 宿主回调、异常传播、自动 GC、内存配额、执行取消、能力隔离、VM 快照及语言类型体系都需要各自的设计。现有帧数/指令预算只是部分执行限额,不构成完整沙箱。
主题的详细规则只在对应文档维护,本页保留组件关系与摘要。返回 [文档索引](../README.md)。
+49
View File
@@ -0,0 +1,49 @@
# 开发定位与文档维护
文档与代码使用同一 Git 版本,不按日期复制成多个相互竞争的说明。新增行为时更新其权威主题;删除或替换行为时直接改写/删除失效说明。当前文档只描述当前契约,历史背景需要时查 Git 历史。
## 开始修改前
从 [文档索引](../README.md) 按问题查到主题,再进入对应目录 README 找源文件。工作区边界见 [架构](../architecture/README.md);本地禁测、远端验证与结果边界见 [验证](../operations/validation.md)。本地工作约定若有额外限制也须遵守;AGENTS.md 自身保持本地忽略,不将个人环境说明当成公开项目文档。
## 什么写到哪里
| 内容 | 唯一主要位置 | 同步入口 |
| --- | --- | --- |
| 目录职责、直接子文件/子目录、常见修改位置 | 该目录 README.md | 父目录 README |
| 各 crate 的依赖方向和系统边界 | [architecture/README](../architecture/README.md) | 根 README、docs 索引摘要 |
| 对象头、句柄、引用协议与 GC 算法 | [objects.md](../runtime/objects.md) | core/src 文件表 |
| Number、String、Dict、Cell、函数环境和异常数据 | [builtins.md](../runtime/builtins.md) | core/src 与 tests 文件表 |
| 指令编码、栈效果、调用协议与执行限制 | [bytecode.md](../runtime/bytecode.md) | instruction/code/frame/eval 文件职责 |
| VM/帧/对象的创建、错误退出与销毁 | [lifecycle.md](../runtime/lifecycle.md) | 对象与字节码主题链接 |
| 环境变量、默认值、优先级、UTF-8、locale 和配置边界 | [configuration.md](../operations/configuration.md) | 相关 crate README 链接 |
| 运行命令、端点和宿主存储行为 | [operations/README](../operations/README.md) | cli/server/storage README |
| 镜像、CI、部署、回退和数据目录 | [deployment.md](../operations/deployment.md) | scripts/工作流 README |
| 检查入口、覆盖范围、结果边界 | [validation.md](../operations/validation.md) | 各 tests README |
| 不可变研究表、聊天导出、临时脚本或构建产物 | 工作区外输出或忽略目录 | 不作为当前项目契约、不提交研究 HTML |
相同规则不要整段复制到多个 README。局部 README 解释本地内容并指向全局契约;全局主题用链接定位实现,不再维护第二份完整源文件表。
## 每次修改时同步
1. 新增、删除、重命名文件或目录:更新本目录和父目录 README 的文件表;新增受版本管理目录必须有 README。该规则不覆盖 .git、target、data、work、outputs 或其他忽略产物。
2. 修改 API、默认值、所有权、操作码或错误处理:同时修改上表对应主题、示例与相关检查;区分已实现行为和未来设计。
3. 改主题路径:修复仓库所有内部链接,更新 docs 总索引;失效旧页直接删除,不长久保留一份旧契约。
4. 改宿主配置/部署:同步配置表、运行命令和脚本 README;不把更换应用容器写成恢复 VM 堆或数据库回退。
5. 检查链接、目录覆盖、旧路径和差异空白;仅在指定远端运行行为测试。没有查询 CI 结果就写未核实,不能继承上次通过的结论。
不是每次提交都要给所有文档改日期或复制版本号;只有相关内容发生变化时更新相应文档。提交及代码审查描述说明目的和验证,文档保留长期可查询的当前行为。
## 当前实现边界
Core 保持零外部依赖、不引入 Python type/元类、源码前端或宿主库。句柄构造保持私有;非法句柄检查放堆内部单元测试,不能为测试开放非法构造器。容器更新须经过 VM 的引用事务;call/execute 对象结果持有根,借用读取仍须由使用方按需 retain。
Rust 实现依赖模块可见性、trait 和 rustfmt,不照搬 CPython C 符号命名规范。公开导出集中在 core/src/lib.rs,内部协作用 pub(crate) 或私有方法区分。当前没有 Endor 源码风格规范或通用配置框架。
后续优先按实际需求扩充函数定义/迭代器等指令、宿主回调、错误传播和资源边界;新增前端或云服务机制需要相应设计,不写成既有功能。
## 导出与迁移
本轮把 outputs 的字节码及基本对象说明核对后融合入 runtime 主题。以后只维护仓库正文;需要分享时导出副本,副本注明仓库权威位置,避免继续平行编辑。CPython 284 项研究 HTML/Markdown 仍是独立研究材料,不上传 Git。
旧平铺路径 docs/architecture.md、development.md、endor-object.md、endor-builtins.md、bytecode-vm.md、validation.md 已由新的多层主题替代;当前入口以 [文档索引](../README.md) 为准。
-41
View File
@@ -1,41 +0,0 @@
# 开发交接
## 入口与验证
先阅读根目录 AGENTS.md。工作区四个 crate 的边界必须保持,内核无数据库/HTTP 依赖。用户明确禁止本地测试,不在本机运行检查/冒烟脚本,也不为此下载依赖。指定的远端验证环境是 ces-control;CI 在 Docker 中运行 `sh scripts/check.sh`、`python3 scripts/smoke.py` 和真实 PostgreSQL 集成测试。正式持久部署选择 PostgreSQL。检查记录见 validation.md。
核心 API 示例在 `crates/core/src/lib.rs`,由 rustdoc 实际编译执行。公开语义测试在 `crates/core/tests/runtime.rs`;伪造句柄仅在私有堆单元测试中构造,禁止为测试添加公开非法构造器。
用户已澄清:当前实现 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/dict_get/cell_get 返回借用值;call/execute 的对象结果现在持有一份强引用,用后 release_value。执行帧中的每个对象槽也持有根,返回时转移结果,失败时逐帧清理。长链释放使用迭代队列;循环回收以外部引用为根。内置字典和 Cell 已使用 VM 管理的载荷替换协议:先验证、保留新边,再更新载荷及快照,最后释放旧边;自定义载荷不能只改 trace 的返回结果。
基础载荷见 builtins.rs,统一无损 Number 见 number.rs,容器 API 和函数/异常关联校验见 vm_builtins.rs。功能与未实现边界详见 endor-builtins.md。新增 builtins 测试交由远端 CI 执行,不在本机运行;数字只完成表示,异常仍为实例数据;语言函数已经接入小型 wordcode 执行器。指令在 instruction.rs,代码验证在 code.rs,执行帧在 frame.rs,执行循环在 eval.rs;详见 bytecode-vm.md。不能将当前子集称为完整 Python 执行器或数值引擎。
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`,不操作外部服务器。端口通过临时绑定选择,释放后启动间存在很小的端口竞争窗口;在指定验证环境中若冲突,可重试整个脚本。
依赖使用 Cargo.lock。此前编译器为 1.91.1;更换依赖后须在指定环境检查 MSRV 和容器构建版本。核心测试无需数据库,但 Cargo 首次依赖解析仍可能访问注册表;不要因此在本机恢复下载或测试。
## 下一阶段建议
1. 在已完成的执行根管理上扩展宿主调用上下文;保持结果所有权一致,并继续补远端生命周期测试。
2. 基于已完成的指令、代码对象、执行帧和取指循环,按需求补函数定义、迭代器等指令,再讨论源码前端;不自动开始解析器或渲染器。
3. 按实际分配负载设计 GC 触发、资源限额与暂停策略;现有手动循环回收不等于分代或增量 GC。
4. 浏览器能力与外部对象桥接使用原生载荷;保持 core 无网络或宿主库依赖。
5. VM 快照需要另行定义持久身份与格式,不能序列化当前进程句柄。语言类型、根类和继承以后再确定。
## Git 状态与后续远程
origin 为用户提供的 `ssh://git@gitea.entroia.com:22222/Zayden-Jung/endorphin-aster-vm.git`。仓库级 author 和隔离 SSH 命令已配置;不得复制其他身份或猜测 owner。发布 CI/CD 按任务授权提交和推送;用户已要求不主动跟踪 CI/部署结果,有问题由用户反馈。遵守 AGENTS.md 的授权及中文逐文件报告要求。初始开发阶段记录与后续部署记录在 validation.md 中分开说明。
## 远端 CI 与部署
`scripts/ci-ces-control.sh` 构建 Dockerfile 的 ci 阶段,创建专属网络与临时 PostgreSQL,再依次运行 workspace 检查、显式 PostgreSQL 测试、SQLite/宿主端到端冒烟。Docker daemon 为 legacy builder,工作流显式设置 `DOCKER_BUILDKIT=0`;没有依赖 buildx。远端内存约 1.7 GiB,Rust 单任务编译、调试构建不生成 debug 信息。
`scripts/deploy-ces-control.sh` 仅部署已经构建的 SHA 镜像;使用文件锁防止并行切换生产版本,第一次自动生成服务器密码文件,后续保留。Compose 固定项目名,独占 PostgreSQL 数据目录,与其他项目隔离。部署通过健康检查和真实元数据写入/读取后才切换 current;失败时尝试恢复上一版健康 Compose,不删除数据库或镜像。
更换应用容器不应重建密码或清理数据目录。PostgreSQL major 版本升级与 schema 变更须另外计划迁移;禁止把应用镜像回退当作数据库回退。修改此链路后,验证必须继续放在 ces-control,不要恢复本机依赖下载。
+61
View File
@@ -0,0 +1,61 @@
# 运行与宿主存储
这里说明跨 CLI、Server 和 Storage 的运行流程。当前本机禁止构建、测试、冒烟与依赖下载;以下命令供用户指定的远端环境执行。环境变量见 [配置](configuration.md),容器和生产服务见 [部署](deployment.md),检查见 [验证](validation.md)。
## 构建与 CLI
工作区要求 Rust 1.91 或兼容更高版本;构建镜像固定 rust:1.91.1-bookworm,依赖由 Cargo.lock 锁定。
```sh
cargo build --workspace --locked
cargo run -p aster-cli -- bootstrap
```
bootstrap 创建两个 Record,输出 runtime=EndorObject、type_system=disabled、objects_distinct=true 和 heap_objects=2,显式释放后输出 released heap_objects=0。它不读取数据库配置,因此无效 DATABASE_URL 也不影响该命令。
```sh
cargo run -p aster-cli -- metadata put project 'Aster VM'
cargo run -p aster-cli -- metadata get project
```
每次 metadata 命令读取宿主存储配置、连接、操作并关闭连接池;缺失键显示 (missing)。命令和退出码的源码位置见 [cli/src](../../crates/cli/src/README.md)。
## HTTP 宿主
```sh
cargo run -p aster-server
curl --fail http://127.0.0.1:8080/health
curl --fail http://127.0.0.1:8080/bootstrap
```
Server 创建空 VM、连接存储、初始化元数据表并写入 host.version,随后绑定监听地址。连接、初始化或绑定失败会退出,不降级换库。默认地址为 127.0.0.1:8080。
| 端点 | 行为 |
| --- | --- |
| GET /health | 实际 ping 数据库;成功返回 200,失联返回 503,含 status 和 storage 后端名 |
| GET /bootstrap | 返回 object_model=EndorObject、type_system=false、heap_objects=0;只读,不分配对象 |
当前未提供语言执行、对象写入或认证 API。Unix 上 Ctrl-C/SIGTERM 触发 Axum 优雅退出,等待服务结束后关闭共享 Storage;最后的 Arc 释放时销毁 Vm。没有执行中脚本的取消协议。
## 存储契约
Storage 不依赖 Core,只保存宿主元数据。连接时执行 schema.sql:aster_metadata 的 key 为非空文本主键,value 为非空文本。put 使用参数绑定并更新重复键,get 对不存在的键返回 None。当前只有幂等建表,没有版本化迁移;CREATE IF NOT EXISTS 不能升级已有列结构。
| 后端 | 当前行为 |
| --- | --- |
| SQLite 文件 | 默认 data/aster.db,相对当前工作目录;创建父目录和文件,开启 WAL,最多 5 个连接,busy timeout 5 秒 |
| SQLite 内存 | SQLITE_PATH=:memory:;单连接、不做空闲回收和最大存活轮换;不同 Storage 实例隔离 |
| PostgreSQL | DATABASE_URL 显式选择;最多 5 个连接,连接池获取超时 5 秒,启动即真实连接;失败不回退 SQLite |
Storage 克隆共享连接池,close 会关闭所有克隆使用的池。元数据可跨进程保存;VM 堆、句柄、全局对象和执行帧不持久化。重启服务创建新的空 VM。
## 出错时先查哪里
| 现象 | 定位 |
| --- | --- |
| bootstrap 正常,metadata/服务启动失败 | [存储配置](configuration.md)、[storage/src](../../crates/storage/src/README.md) |
| /health 返回 503 | 当前数据库连接;该端点不检测语言执行能力 |
| 容器替换后元数据丢失 | [卷与生产数据路径](deployment.md),先确认是否使用内存库 |
| 重启后找不到原 VM 对象 | 当前未实现 VM 快照,见 [生命周期](../runtime/lifecycle.md) |
返回 [文档索引](../README.md)。
+67
View File
@@ -0,0 +1,67 @@
# 配置、UTF-8 与进程边界
## 当前选择
当前先明确少量稳定边界,不引入 PyPreConfig/PyConfig 式两阶段初始化,也不新增 VM 配置文件、环境变量前缀或配置框架。Vm::new 从空堆启动;需要环境、数据库或网络的是宿主。
| 配置层 | 当前入口 | 责任 |
| --- | --- | --- |
| 内核 | Vm::new、显式创建对象/函数;call_with_limits/execute_with_limits | 可预测的运行时语义,不读取环境变量、修改 locale、注册信号或加载 .env |
| 宿主 | StorageConfig::from_env、Server 启动代码 | 将环境变量转成存储配置和监听地址,再启动服务 |
| 部署 | Compose 与 ces-control 脚本 | 端口映射、数据库密码、持久卷和镜像版本 |
| 构建/CI | Cargo.toml、Cargo.lock、Dockerfile、工作流 | 工具链、依赖锁定与验证环境;不作为语言运行语义配置 |
内核不直接依赖 HTTP、SQLx 或操作系统 locale 接口。这个隔离来自现有代码与分层,不是已经实现的独立安全沙箱。
## 为什么不照搬 CPython 的 locale 初始化
CPython 的 PyPreConfig 支持选择分配器、UTF-8 模式以及是否配置 LC_CTYPE,以处理它自己的启动和兼容性需求。Python 官方也提供忽略环境、保留 LC_CTYPE、不注册信号处理的隔离初始化。当前 Endor 通过 Rust 宿主接收已构造的值,没有需要复刻这套预初始化的依赖环。[CPython 初始化文档](https://docs.python.org/3.14/c-api/init_config.html)。
LC_CTYPE 处理字符分类、大小写和多字节字符相关约定;小数点和数字分组属于 LC_NUMERIC。只调整 LC_CTYPE 不能直接等同于修改数字小数点,这两类配置应区分。[locale 类别说明](https://docs.python.org/3.14/library/locale.html#locale.LC_CTYPE)。
云原生环境仍会遇到进程变量、终端、文件路径与外部工具的问题;较少跨语言互操作不代表可以省略输入边界。当前解决方式是让核心语义独立于进程区域设置,把必要的解码留在宿主入口。
## 当前文本和数字行为
- Value::Text 与 EndorStringObject 使用 Rust String,内容始终是有效 UTF-8。字符访问按 Unicode 标量值,不按字节或字形簇;不支持孤立代理码点。[Rust String](https://doc.rust-lang.org/std/string/struct.String.html#utf-8)。
- Number 的整数与十进制构造器明确检查 ASCII 数字和语法;十进制点为句点,不根据 LANG/LC_NUMERIC 切换为逗号。当前数值比较、哈希和算术尚未实现。
- 内核没有 setlocale 调用;不会为了启动 VM 修改 LANG、LC_ALL 或 LC_CTYPE。外部库、终端、字体或下游工具自身的行为不在这项保证之内。
- 尚无源码加载器、标识符语法、通用字节串对象或文件系统 API,因此不能宣称已完成源码编码、路径编码和全部 I/O 策略。
## 已有宿主环境变量
| 名称 | 使用位置与默认值 | 错误和优先级 |
| --- | --- | --- |
| DATABASE_URL | StorageConfig;未设置时才选择 SQLite | 仅 postgres:// 或 postgresql://;显式空值、错误协议和非 Unicode 值报错;连接失败不降级 |
| SQLITE_PATH | 无 DATABASE_URL 时使用;默认 data/aster.db | 空值和非 Unicode 值报错;:memory: 为临时库;设置 DATABASE_URL 后忽略此项 |
| ASTER_LISTEN | Server;默认 127.0.0.1:8080 | 当前 var(...).unwrap_or_else:缺失或非 Unicode 都用默认值;有效 Unicode 的错误地址在绑定时失败 |
| ASTER_PORT | Compose 宿主端口映射 | 独立方案默认 8080,ces-control 默认 18081;不是 Vm 配置 |
| POSTGRES_PASSWORD | PostgreSQL Compose/生产环境文件 | Compose 必填;示例使用随机十六进制以避免 URL 转义歧义;不写入仓库 |
| ASTER_IMAGE | ces-control Compose | 必填已构建的版本镜像,通常由 release.env 提供 |
| ASTER_RELEASE_SHA | CI 与部署脚本 | 必须为 40 位小写十六进制提交 SHA;用于镜像和版本目录 |
| ASTER_TEST_POSTGRES_URL | 指定环境显式运行 PostgreSQL 集成测试 | 专用测试库 URL;不是生产数据库配置 |
程序本身不自动加载 .env,Cargo 运行时须显式导出变量。Compose 会按其规则读取 .env;显式 --env-file 的生产文件由部署脚本管理。模板见 [.env.example](../../.env.example),执行链见 [部署](deployment.md)。
数据库 URL 可能包含密码;当前 StorageConfig 的 Debug 脱敏,公开数据库错误不回显 URL。底层错误 source 仍保留给宿主,不应不加处理地作为公开日志输出。
## 已有执行限额
ExecutionLimits 是每次调用显式传入的普通 Rust 值,不是进程全局开关;字段定义见 [frame.rs](../../crates/core/src/frame.rs)。
| 字段 | 默认值 | 作用 |
| --- | --- | --- |
| max_frames | 1000 | 本次执行允许的活动语言帧数量 |
| max_instructions | usize::MAX | 本次执行共享的逻辑指令预算;默认不设实用的有限预算 |
call_with_limits/execute_with_limits 使用自定义值;嵌套语言调用共用计数。预算不能抢占正在执行的 Native 回调,也没有超时、内存上限或异步取消能力;不能据此视为安全执行不可信代码的完整方案。
## 保留的入口缺口与以后再做的内容
CLI 当前使用 std::env::args,遇到非 Unicode 命令行参数可能 panic,并没有统一转换成友好的配置错误。[Rust args 文档](https://doc.rust-lang.org/std/env/fn.args.html#panics)。Server 的非 Unicode ASTER_LISTEN 也尚未像存储变量一样严格报错;宿主输入错误接口尚未统一。
以后增加宿主输入时,应明确使用 OsString/PathBuf 保留系统路径,或显式拒绝不符合文本协议的输入;不要通过修改整个进程 locale 让隐式解码暂时成功。未来源码文本默认 UTF-8、协议数字固定格式是设计方向,只有加载器实现后才能写成已实现行为。
现在不新增通用 VmConfig。出现构造期策略时再集中为实例级不可变配置;进程参数由宿主解析后传入,不让 Vm::new 自动读取一组隐含开关。时区、模块路径、凭据注入、内存配额和网络能力也按实际实现分别设计,不预设已存在。
返回 [运行指南](README.md) 或 [文档索引](../README.md)。
+69
View File
@@ -0,0 +1,69 @@
# Docker 与 ces-control 部署
这里描述仓库当前工作流和脚本行为,不代表正在运行的服务器状态。正式持久部署选择 PostgreSQL,SQLite Compose 保留为独立运行选项。配置字段见 [配置](configuration.md),CI 检查细节见 [验证](validation.md)。
## 镜像与独立运行
Dockerfile 的 source 阶段复制 Cargo 工作区及脚本;ci 阶段安装远端检查工具,build 阶段构建 aster-server 和 aster,runtime 阶段使用 Debian slim、UID 10001、/data 工作目录及 HTTP 健康检查。
指定环境的 SQLite 方案:
```sh
cp .env.example .env
docker compose up --build -d
curl --fail http://127.0.0.1:8080/health
docker compose exec aster aster metadata put project 'Aster VM'
```
默认 docker-compose.yml 只向应用提供 SQLITE_PATH,使用 aster-sqlite 命名卷;不透传 DATABASE_URL。down 保留数据,down -v 会删除卷。
指定环境的 PostgreSQL 独立替代方案:先在 .env 中将 POSTGRES_PASSWORD 改为随机十六进制字符串,例如 openssl rand -hex 24,再运行:
```sh
docker compose -f docker-compose.postgres.yml up --build -d
docker compose -f docker-compose.postgres.yml exec aster aster metadata get project
```
仅使用该 Compose 文件,不与默认 SQLite 文件叠加。此方案使用 aster-postgres 命名卷、PostgreSQL 17;数据库不发布宿主端口。两个独立方案不要同时占用相同应用端口。内部示例连接禁用 TLS,跨不可信网络使用 PostgreSQL TLS 验证。
## CI 到正式发布
[工作流](../../.gitea/workflows/README.md) 响应 main push 和 workflow_dispatch,在 ces-control 上串行运行:
1. 检出代码,调用 ci-ces-control.sh;在 Docker 中检查 workspace、SQLite/HTTP 冒烟及专用真实 PostgreSQL。
2. 检查成功后构建 endorphin-aster-vm:<sha> 生产镜像。
3. main 上调用 deploy-ces-control.sh 更新 PostgreSQL 宿主。
临时检查容器与网络退出时清理,Cargo 下载与构建缓存保留在专属远端卷。生产镜像、已有发布目录与数据库数据保留;没有自动 image prune 或数据库卷清理。工作流不改动其他项目或共享 Runner 配置。
## 生产目录与数据
| 路径 | 用途 |
| --- | --- |
| /opt/endorphin-aster-vm/releases/<sha>/ | 固定版本的 compose.yml 与 release.env |
| /opt/endorphin-aster-vm/current | 最近通过部署检查的版本符号链接 |
| /etc/endorphin-aster-vm/production.env | 首次生成的数据库密码和默认端口,权限 0600 |
| /var/lib/endorphin-aster-vm/postgres | 独立的 PostgreSQL 持久数据目录 |
| /var/lock/endorphin-aster-vm-deploy.lock | 避免并行切换生产版本的文件锁 |
生产 Compose 固定项目名 endorphin-aster-vm,默认应用入口仅绑定 127.0.0.1:18081;数据库不发布端口,两个服务使用 unless-stopped 自动重启和日志轮换。端口绑定回环地址不等于已实现应用认证。
部署脚本只接受已构建镜像。启动容器、健康检查、真实元数据 put/get 及 PostgreSQL 后端确认都成功后才切换 current。失败时保留数据并尝试恢复上一个健康版本;首次部署没有旧版本可恢复。脚本内容导航见 [scripts](../../scripts/README.md)。
## 查看、停止与恢复
以下命令在服务器上使用已有生产配置,不重新生成密码:
```sh
docker compose -p endorphin-aster-vm --env-file /etc/endorphin-aster-vm/production.env --env-file /opt/endorphin-aster-vm/current/release.env -f /opt/endorphin-aster-vm/current/compose.yml ps
curl --fail http://127.0.0.1:18081/health
curl --fail http://127.0.0.1:18081/bootstrap
```
同样的 Compose 参数接 stop 停服保留数据,接 up -d --wait 恢复。不要删除数据目录或使用删除卷的操作代替停服。
更改环境文件密码不会自动修改已有 PostgreSQL 数据库用户密码;需另行同步数据库凭据。数据库 major 升级、schema 变更需要迁移设计,恢复旧应用镜像不等于回退数据库。
Compose 配置依据 [Docker 环境变量说明](https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/),触发方式依据 [Gitea Actions](https://docs.gitea.com/usage/actions/)。按用户约定不主动追踪运行状态;需要排障时针对具体提交核实,不能用旧成功记录推断新版本结果。
返回 [运行指南](README.md) 或 [文档索引](../README.md)。
+44
View File
@@ -0,0 +1,44 @@
# 验证范围与结果边界
本页维护当前检查入口和覆盖范围,不累计已失效的测试数量、机器资源快照或旧对象模型的通过记录。历史行为查 Git 历史;某个版本是否通过,只能依据该提交对应的实际结果。
## 执行约定
本地不运行 Cargo 构建、测试、Clippy、冒烟、Docker 或依赖下载;允许阅读、格式化和静态文件/文档检查。指定运行验证环境为 ces-control。按用户要求不主动追踪 CI/部署结果,有问题由用户反馈后再查。
## 检查入口
以下命令只在指定的验证环境执行,不表示已经运行:
```sh
sh scripts/check.sh
cargo test -p aster-storage --test postgres --locked -- --ignored
python3 scripts/smoke.py
```
check.sh 执行 cargo fmt --all --check、cargo test --workspace --locked 和 cargo clippy --workspace --all-targets --locked -- -D warnings。workspace 测试也包含 rustdoc 示例;显式忽略的 PostgreSQL 测试需要单独执行。
ci-ces-control.sh 负责准备专用临时 PostgreSQL 和测试 URL;成功路径使用真实数据库,不用模拟 socket 代替。测试库与生产数据不共享挂载。没有 ASTER_TEST_POSTGRES_URL 时,显式执行 PostgreSQL 测试会失败,不标记为通过。
## 覆盖范围与文件位置
| 范围 | 当前检查内容 | 入口 |
| --- | --- | --- |
| 堆内部边界 | 非法索引、槽位代数、代数耗尽、计数溢出原子性 | [core/src](../../crates/core/src/README.md) 的 heap.rs 私有单元测试 |
| 对象运行时 | 地址稳定、引用/借用、属性、长链、循环、原生操作、VM 销毁 | [core/tests](../../crates/core/tests/README.md) 的 runtime.rs |
| 基本对象 | Number 表示、字符串、Dict/Cell 引用与更新、函数环境、异常数据 | 同目录 builtins.rs |
| 字节码 | 编解码、非法指令/控制流、跳转、调用、闭包、结果存活、失败清理与限额 | 同目录 execution.rs |
| 公开示例 | 对象操作、手写指令执行与释放 | [core/src](../../crates/core/src/README.md) 的 lib.rs rustdoc |
| 存储 | 配置优先级、内存隔离/共享、文件重连、失败不降级、真实 PostgreSQL | [storage/tests](../../crates/storage/tests/README.md) |
| HTTP 状态 | 数据库关闭后 health 返回 unavailable | [server/src](../../crates/server/src/README.md) 的单元测试 |
| 端到端宿主 | CLI 跨进程元数据、独立 bootstrap、错误配置、HTTP 与 SIGTERM | [scripts](../../scripts/README.md) 的 smoke.py |
测试源码说明验证意图,不能证明已通过;现有测试也不代表完整 Python 兼容、panic 恢复、云沙箱或 VM 快照已经实现。
## 文档修改的静态检查
修改后核对所有受版本管理目录存在 README、父目录有入口、相对链接指向现存文件;运行 git diff --check 检查差异空白。静态文本检索可以查出旧路径和过时 API;维护规则见 [开发与文档维护](../contributing/README.md)。这些检查不执行 VM 或数据库。
仅修改文档时也不据此宣称运行检查通过。推送触发现有 CI 时,仍由原流程检查和部署;报告应分别说明静态检查、运行测试与推送结果。
返回 [运行指南](README.md) 或 [文档索引](../README.md)。
+12
View File
@@ -0,0 +1,12 @@
# 运行时设计
本目录解释跨越 core 多个模块的运行时契约。文件与函数定位见 [core/src README](../../crates/core/src/README.md),测试定位见 [core/tests README](../../crates/core/tests/README.md)。
| 主题 | 内容 |
| --- | --- |
| [对象模型](objects.md) | EndorObject/ObjectHeader、代际句柄、操作协议、托管引用与循环回收 |
| [基本对象与 Number](builtins.md) | 函数、原生函数、字典、Cell、异常数据、字符串与无损数字 |
| [字节码 VM](bytecode.md) | 指令编码、代码校验、局部槽、执行帧、调用/返回与边界 |
| [生命周期](lifecycle.md) | 宿主持有 Vm、帧执行根、结果所有权、错误退出和整体销毁 |
文本/环境约束和执行限额集中在 [配置](../operations/configuration.md);四个组件之间的关系见 [系统架构](../architecture/README.md)。返回 [文档索引](../README.md)。
@@ -1,6 +1,6 @@
# Endor 基本对象与统一 Number
本次增加的是虚拟机实现层的数据结构与宿主 API,不增加 Python type、语言根类或解析器;后续已接入 [小型字节码执行器](bytecode-vm.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。
这些对象属于虚拟机实现层的数据结构与宿主 API,不定义 Python type、语言根类或解析器;语言函数已接入 [小型字节码执行器](bytecode.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。
## 对应关系
@@ -14,7 +14,7 @@
| `PyLongObject`、`PyFloatObject` 等 | `EndorNumberObject` / `EndorNumber` | 一个对外数字接口,内部保留不同数字的无损表示 |
| `PyCellObject` | `EndorCellObject` | 可被多个闭包共享的可变槽;区分空槽与 Nil |
`EndorCode` 保存经过验证的 Endor wordcode 和静态元数据,`EndorCodeObject` 将它放进受管理的堆;函数持有代码对象句柄并可共享同一份代码。指令格式、执行帧与入口见 [字节码 VM](bytecode-vm.md)。不是 CPython 字节码格式。
`EndorCode` 保存经过验证的 Endor wordcode 和静态元数据,`EndorCodeObject` 将它放进受管理的堆;函数持有代码对象句柄并可共享同一份代码。指令格式、执行帧与入口见 [字节码 VM](bytecode.md)。不是 CPython 字节码格式。
## Function 与 NativeFunction
@@ -32,20 +32,22 @@ fn echo(arguments: &[Value]) -> Result<Value, VmError> {
}
}
let mut vm = Vm::new()?;
let function = vm.allocate(EndorNativeFunctionObject::new("echo", echo))?;
let value = vm.call(function, &[Value::Text("hello".into())])?;
assert_eq!(value, Value::Text("hello".into()));
vm.release_value(value)?;
vm.release(function)?;
# Ok::<(), VmError>(())
fn main() -> Result<(), VmError> {
let mut vm = Vm::new()?;
let function = vm.allocate(EndorNativeFunctionObject::new("echo", echo))?;
let value = vm.call(function, &[Value::Text("hello".into())])?;
assert_eq!(value, Value::Text("hello".into()));
vm.release_value(value)?;
vm.release(function)?;
Ok(())
}
```
Native 函数和语言函数均通过 `Vm::call(&mut self, ...)` 调用。语言函数用显式帧绑定位置参数、默认值和闭包,再由取指循环执行。Native 回调仍接收借用参数、返回借用对象值,不能在回调里修改当前 VM;VM 会将对象结果 retain 后返回,因此宿主需对 call/execute 的对象结果配对 release_value。
## Number:优先表示,不先做重计算
原来的 `Value::Integer` / `Value::Float` 合并为 `Value::Number(EndorNumber)`。宿主仍可直接保存数字值;需要对象身份时才使用 `vm.new_number(number)` 装箱成 `EndorNumberObject`。
数字统一表示为 `Value::Number(EndorNumber)`。宿主仍可直接保存数字值;需要对象身份时才使用 `vm.new_number(number)` 装箱成 `EndorNumberObject`。
| 内部表示 | 适用数据 | 保留的内容 |
| --- | --- | --- |
@@ -58,13 +60,15 @@ Native 函数和语言函数均通过 `Vm::call(&mut self, ...)` 调用。语言
```rust
use aster_core::{EndorNumber, Value};
let id = EndorNumber::integer("1234567890123456789012345678901234567890")?;
let amount = EndorNumber::decimal("12345678901234567890.000100")?;
let ratio = EndorNumber::rational("1", "3")?;
let tiny = EndorNumber::decimal("1e-999999999999999999999999999999")?;
let host_float = EndorNumber::binary_float(f64::INFINITY);
let value = Value::Number(amount);
# Ok::<(), aster_core::VmError>(())
fn main() -> Result<(), aster_core::VmError> {
let id = EndorNumber::integer("1234567890123456789012345678901234567890")?;
let amount = EndorNumber::decimal("12345678901234567890.000100")?;
let ratio = EndorNumber::rational("1", "3")?;
let tiny = EndorNumber::decimal("1e-999999999999999999999999999999")?;
let host_float = EndorNumber::binary_float(f64::INFINITY);
let value = Value::Number(amount);
Ok(())
}
```
`EndorInteger`、`EndorDecimal`、`EndorRational` 是内部表示结构,不是三种语言数字类。构造器验证输入;分数不能有零分母;十进制指数不转换为机器整数,所以不会因指数很长而溢出。构造任意大整数或精确小数时传文本;已经被宿主转换为 f64 而丢失的精度无法恢复。整数的 `as_i64` 是显式、可失败的窄化读取,不会静默截断。
@@ -102,14 +106,17 @@ print(read()) # updated
```rust
use aster_core::{Value, Vm};
let mut vm = Vm::new()?;
let cell = vm.new_cell(None)?; // 空槽,不是 Nil。
vm.cell_set(cell, Value::Text("first".into()))?;
vm.cell_set(cell, Value::Text("updated".into()))?;
assert_eq!(vm.cell_get(cell)?, Value::Text("updated".into()));
vm.cell_clear(cell)?;
vm.release(cell)?;
# Ok::<(), aster_core::VmError>(())
fn main() -> Result<(), aster_core::VmError> {
let mut vm = Vm::new()?;
let cell = vm.new_cell(None)?; // 空槽,不是 Nil。
vm.cell_set(cell, Value::Text("first".into()))?;
vm.cell_set(cell, Value::Text("updated".into()))?;
assert_eq!(vm.cell_get(cell)?, Value::Text("updated".into()));
vm.cell_clear(cell)?;
vm.release(cell)?;
Ok(())
}
```
当前已能通过字节码读取、写入和删除共享 Cell,并创建捕获参数的 Cell。上面的 Python 闭包示例说明语义目标;Endor 尚无 Python 源码解析器,函数对象目前由宿主工厂建立。
@@ -124,8 +131,6 @@ Endor 当前用 `EndorBaseExceptionObject` 保存公共数据,名称只是错
来源:[CPython 函数对象](https://docs.python.org/3.14/c-api/function.html)、[Cell 对象](https://docs.python.org/3.14/c-api/cell.html)、[异常说明](https://docs.python.org/3.14/library/exceptions.html)、[Unicode 对象](https://docs.python.org/3.14/c-api/unicode.html)。实现不承诺 CPython ABI 或默认 Python 兼容性。
## 验证状态
## 验证入口
新增 `crates/core/tests/builtins.rs`,检查数字无损表示、非法输入、字符串内容语义、字典顺序和引用所有权、更新失败的原子性、Cell 共享、循环回收、函数环境及异常链。已有 runtime 测试和 rustdoc 示例迁移到统一 Number。
遵守本机禁测约定:未在本地运行测试、构建或冒烟脚本;未下载依赖。远端结果必须以本次代码对应的 ces-control CI 为准,未推送前不标记通过。HTML 研究表仅为工作区外阅读材料,不提交 Git。
对象行为检查在 [core/tests](../../crates/core/tests/README.md) 的 builtins.rs;执行行为在 execution.rs;验证方式见 [验证](../operations/validation.md)。文档描述当前契约,不代表某次 CI 已通过。CPython 284 项研究表保持为工作区外的研究材料,不纳入项目文档或 Git。
@@ -1,6 +1,6 @@
# 小型字节码 VM
本阶段完成指令与代码对象、内部执行帧、取指执行循环,并接通语言函数。参考 CPython 3.14 的职责划分和栈式执行设计;不实现 lexer/parser,不读取 Python `.pyc`,不承诺 CPython 操作码编号、ABI 或完整语言兼容性。
当前提供指令与代码对象、内部执行帧、取指执行循环,语言函数已接入执行器。参考 CPython 3.14 的职责划分和栈式执行设计;不实现 lexer/parser,不读取 Python `.pyc`,不承诺 CPython 操作码编号、ABI 或完整语言兼容性。
## 与 CPython 的对应
@@ -96,23 +96,25 @@ Vm::execute(code, globals)
```rust
use aster_core::{CodeLayout, EndorCode, Instruction, Opcode, Value, Vm};
let mut vm = Vm::new()?;
let globals = vm.new_dict()?;
let code = EndorCode::from_instructions(
&[
Instruction::new(Opcode::LoadConst, 0),
Instruction::simple(Opcode::ReturnValue),
],
vec![Value::Text("Hello Endor".into())],
CodeLayout::default(),
)?;
let code = vm.new_code(code)?;
let result = vm.execute(code, globals)?;
assert_eq!(result, Value::Text("Hello Endor".into()));
vm.release_value(result)?;
vm.release(code)?;
vm.release(globals)?;
# Ok::<(), aster_core::VmError>(())
fn main() -> Result<(), aster_core::VmError> {
let mut vm = Vm::new()?;
let globals = vm.new_dict()?;
let code = EndorCode::from_instructions(
&[
Instruction::new(Opcode::LoadConst, 0),
Instruction::simple(Opcode::ReturnValue),
],
vec![Value::Text("Hello Endor".into())],
CodeLayout::default(),
)?;
let code = vm.new_code(code)?;
let result = vm.execute(code, globals)?;
assert_eq!(result, Value::Text("Hello Endor".into()));
vm.release_value(result)?;
vm.release(code)?;
vm.release(globals)?;
Ok(())
}
```
该示例直接提供指令,不表示已经支持 Endor 或 Python 源码解析。函数和嵌套函数目前由宿主工厂创建,尚无 MAKE_FUNCTION 指令;后续编译器接入时再补函数定义所需的操作。
@@ -127,8 +129,8 @@ vm.release(globals)?;
## 验证
`crates/core/tests/execution.rs` 覆盖 wordcode、控制流校验、变量、循环、嵌套调用与默认参数、闭包、容器和返回值所有权、错误清理及帧/指令限额;已有 builtins 测试已迁移共享代码对象和持有式调用返回值。lib.rs 另有可供远端 rustdoc 执行的例子。
[execution.rs](../../crates/core/tests/execution.rs) 覆盖 wordcode、控制流校验、变量、循环、嵌套调用与默认参数、闭包、容器和返回值所有权、错误清理及帧/指令限额;已有 builtins 测试已迁移共享代码对象和持有式调用返回值。lib.rs 另有可供远端 rustdoc 执行的例子。
本地仅格式化和静态审阅,不构建、不执行测试、不下载依赖。远端 CI 执行工作区检查和双数据库验证;按用户要求不主动跟踪 CI/部署结果,不能把未查询的结果标为通过。
验证命令与结果边界统一见 [验证](../operations/validation.md)。限额与环境隔离见 [配置](../operations/configuration.md),文件职责见 [core 源码导航](../../crates/core/src/README.md)。
参考:[CPython 解释器设计](https://github.com/python/cpython/blob/v3.14.0/InternalDocs/interpreter.md)、[执行帧设计](https://github.com/python/cpython/blob/v3.14.0/InternalDocs/frames.md)、[帧结构](https://github.com/python/cpython/blob/v3.14.0/Include/internal/pycore_interpframe_structs.h)、[指令定义](https://github.com/python/cpython/blob/v3.14.0/Python/bytecodes.c)、[求值入口与执行器](https://github.com/python/cpython/blob/v3.14.0/Python/ceval.c)。
+35
View File
@@ -0,0 +1,35 @@
# VM、执行帧与对象的生命周期
VM 由宿主持有,VM 持有对象堆,每次调用临时创建执行帧。一次执行结束不等于 VM 销毁;宿主可以保留全局字典并在同一 VM 中执行多次。
| 层级 | 创建 | 结束 |
| --- | --- | --- |
| Vm | 宿主调用 Vm::new,创建空堆并取得独立堆身份 | Rust 所有者销毁;使用 Arc 时等最后一份 Arc 被释放 |
| EndorFrame | 每次语言函数调用建立,加入本次调用的帧向量 | 函数返回或 Result::Err 时,从内到外清理 |
| EndorObject | new_*/allocate 分配,返回一份外部强引用 | 总强引用归零、显式循环回收,或整体 VM 销毁 |
## 运行期间的根
每个执行帧持有函数的一份根引用,函数通过堆边继续保活代码、globals、builtins、默认参数和闭包。局部变量与求值栈的对象槽分别持有根;Cell 通过自己的堆边持有捕获内容。
堆计数关系是:总强引用数 = 宿主/执行根引用数 + 属性/载荷内部入边数。对象头 external_refs 同时统计宿主和执行根,不只是宿主变量。
ObjectRef 拷贝、Value::clone 和句柄变量离开 Rust 作用域,都不会自动 retain/release。宿主为每份显式持有配对 retain/release。借用读取在所有者删除内容前需要 retain,详见 [对象模型](objects.md)。
## 返回与失败
call/execute 返回 Value::Object 时,结果拥有一份根。执行器将这份根从帧移交给调用者或宿主,避免帧清理回收结果;使用后须 release_value,或取出对象句柄后 release。Native 回调原始返回值仍是借用值,由 VM 在移除输入根之前保活。
Result::Err 会触发活动帧清理;last_traceback 仅保存函数名、文件、代码首行与指令位置,不保活已经销毁的帧。此前已经完成的全局字典或属性写入不会回滚。
Rust panic 尚无专门的捕获恢复或执行根 RAII 清理机制。宿主捕获 panic 后不能据此假定当前 VM 已恢复全部执行根计数;整机进程终止与正常错误返回也不是同一条清理路径。
## 回收和整体销毁
通常释放在总引用数归零时立即执行。堆使用显式工作列表递减出边,长链不会递归析构。不可达循环通过手动 collect_cycles 从根标记后批量移除;没有自动阈值、分代或增量 GC。
Vm 没有独立后台线程、暂停状态或显式 shutdown 状态机。Rust 所有权销毁它所拥有的堆和剩余载荷,包括没有先做循环回收的对象;原生共享资源继续遵循各自的 Rust 所有权。外部 ObjectRef 不保活 Vm。
Server 的 Tokio 运行时、监听器、信号处理和 Storage 由宿主管理;优雅退出见 [运行](../operations/README.md)。数据库持久化只保留宿主元数据,不能延长或恢复 VM 对象生命周期。
源码定位:[core/src](../../crates/core/src/README.md) 中的 vm.rs、heap.rs、frame.rs、eval.rs。返回 [运行时索引](README.md)。
@@ -2,16 +2,9 @@
当前实现的是 Endor 的 VM 运行时基础对象,角色类似 CPython 的公共对象结构。这里没有语言根类、元类、继承或 Python type 系统。标量仍然可以直接保存在 Value 中,不要求一切装箱。
基本对象及统一数字表示已在此基础上加入,详见 [基本对象与 Number](endor-builtins.md)。`builtins.rs` 定义字符串、字典、Cell、函数和异常数据;`number.rs` 定义无损数字表示;`vm_builtins.rs` 提供容器操作及关联对象校验。
基本对象及统一数字表示已在此基础上加入,详见 [基本对象与 Number](builtins.md)。`builtins.rs` 定义字符串、字典、Cell、函数和异常数据;`number.rs` 定义无损数字表示;`vm_builtins.rs` 提供容器操作及关联对象校验。
## 代码分布
| 文件 | 职责 |
| --- | --- |
| `crates/core/src/object.rs` | EndorObject、对象头、Value、操作接口和内置原生载荷 |
| `crates/core/src/heap.rs` | 稳定分配、句柄验证、引用计数、槽位复用、循环回收 |
| `crates/core/src/vm.rs` | 对外 API、错误处理和操作分发 |
| `crates/core/tests/runtime.rs` | 宿主可观察的对象行为和生命周期检查 |
代码文件及职责见 [core 源码导航](../../crates/core/src/README.md),对象及执行的生命周期见 [生命周期](lifecycle.md)。
## EndorObject 保存什么
@@ -82,7 +75,7 @@ ObjectPayload 提供以下默认行为,载荷可以逐项覆盖:
Record 使用身份语义并开启属性。NativeFunction 保存名称和 Rust 函数指针,覆盖 repr 和 call。原生函数收到 Value 参数并返回 Result<Value, VmError>;VM 在调用前后检查参数和返回值中的对象句柄。
原生回调当前不能修改 VM 或直接在该 VM 中分配新对象;其参数和原始返回对象值是借用句柄。Vm::call 会验证并 retain 对象结果,对外统一返回一份持有式结果。语言函数已通过 [wordcode 执行器](bytecode-vm.md) 使用显式帧、参数槽、闭包和求值栈执行;call/execute 的结果完成使用后须 release_value。
原生回调当前不能修改 VM 或直接在该 VM 中分配新对象;其参数和原始返回对象值是借用句柄。Vm::call 会验证并 retain 对象结果,对外统一返回一份持有式结果。语言函数已通过 [wordcode 执行器](bytecode.md) 使用显式帧、参数槽、闭包和求值栈执行;call/execute 的结果完成使用后须 release_value。
compare 的反向规则是 Lt 对应 Gt、Le 对应 Ge,Eq/Ne 保持不变。它不实现 Python 的子类型优先规则。内容比较由扩展定义;如果两个对象内容相等,它们必须给出相同哈希,或者明确拒绝哈希。
@@ -109,4 +102,4 @@ compare 的反向规则是 Lt 对应 Gt、Le 对应 Ge,Eq/Ne 保持不变。
尚未实现:Python type 系统、语言根类、继承和描述符、源码解析与编译器、可变 VM 调用上下文、自动/分代/增量 GC、语言终结器/复活、完整资源限额和 VM 快照;执行器已有帧数和指令数限额。数据库仍然只保存宿主元数据。
实际测试结果以本次提交对应的远端 Gitea CI 为准,不在本地构建、测试或下载依赖。
验证范围与执行约定见 [验证](../operations/validation.md)。
-71
View File
@@ -1,71 +0,0 @@
# 实现与验证记录
日期:2026-10-17(Asia/Shanghai)。
## 初始开发阶段记录
用户已明确禁止在本地测试,并要求清理下载内容。停止继续下载、构建与测试;后续检查脚本仅供用户指定的 CI/部署环境使用。测试结果是此前已启动进程返回的历史记录,不代表此后又运行了测试。
尝试中断此前的 `cargo test --workspace` 时,该进程已经自然结束,返回状态 0,SQLite 构建依赖也已下载。随后按用户要求清理了本次新增缓存和构建产物,不再运行测试。
## 已观察结果
| 检查 | 实际结果 |
| --- | --- |
| `cargo fmt --all --check` | 通过;此后只修改文档与约定,没有修改 Rust 代码 |
| `cargo test -p aster-core --offline` | 3 项私有单元测试、6 项公开语义测试、1 项文档测试通过 |
| `cargo clippy -p aster-core --all-targets --offline -- -D warnings` | 通过,仅覆盖内核 |
| 此前启动的 `cargo test --workspace` | 最终返回 16 项通过、0 失败、1 项明确忽略;含核心 9 项、宿主 1 项、存储 5 项、文档 1 项 |
| PostgreSQL 失败路径 | 使用不处理协议的本地 TCP socket,连接超时并报错、不降级;已包含于上述存储测试 |
| 真实 PostgreSQL 成功路径 | 未验证;集成测试显式忽略,需专用测试库 |
| 两个 Compose 文件 | Ruby YAML 静态解析通过,未执行 Compose |
| `scripts/check.sh`/`scripts/smoke.py` | 分别通过 Shell/Python 语法检查,未运行完整脚本 |
| `cargo clippy --workspace --all-targets -- -D warnings` | 未执行,不宣称全工作区无 Clippy 警告 |
| CLI/HTTP/SIGTERM 端到端冒烟 | 未执行;宿主健康状态函数测试已在上述 workspace 测试通过 |
| Docker 构建及容器运行 | 未执行,本机无 Docker |
以上为初始阶段记录,当时没有外部部署、PostgreSQL 服务、commit 或 push。
## 清理结果
- 删除本项目创建后新增、且属于 Cargo.lock 依赖列表的 150 个 Cargo 下载归档与 150 个解压目录,包括 SQLite/SQLx 本次新增依赖。
- 删除项目 `target/` 构建产物及 Python 字节码缓存。
- 保留此前已存在的 Cargo 缓存、共享注册表索引、项目源码与 Cargo.lock;不清空其他项目的既有依赖。
- 没有运行 CLI 创建默认 `data/aster.db`。此前文件数据库测试使用的临时目录随测试自动释放。
## Git
初始阶段本地分支 main 尚无提交。origin 是用户提供的 `ssh://git@gitea.entroia.com:22222/Zayden-Jung/endorphin-aster-vm.git`。
只读 `ls-remote --symref origin HEAD` 使用专用 codex-bot 密钥、隔离 SSH 配置、禁用其他身份及交互式凭据,返回状态 0,未公布 HEAD 引用。只读成功不代表已验证写权限。
仓库级身份:`Codex Bot` / `codex-bot@gitea.entroia.local`;`core.sshCommand` 使用项目约定的隔离命令。没有读取密码或切换为仓库 owner 身份。
## ces-control CI/CD 准备
用户随后指定在 ces-control 上持久部署,并要求同时测试 SQLite 与 PostgreSQL、正式部署使用 PostgreSQL。新增的工作流和脚本将这些检查放在远端 Docker 中,不在本机下载依赖或运行测试。
准备阶段只读检查:Docker 29.1.3、Compose 2.40.3、x86_64、约 1.7 GiB 内存/4 GiB swap、73 GiB 可用磁盘;没有主机 Rust 或 buildx,已缓存 PostgreSQL 17 镜像。选用单任务 Rust 构建、legacy Docker builder,默认生产端口 18081(服务器回环地址),PostgreSQL 数据目录独立。
此处不预先宣称远端 CI 或部署已成功;最终结果以 Gitea Actions 对应运行与服务器状态核验为准。
## 统一对象头变更(2026-10-18)
本次把堆条目改为 `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 已成功,但不代表本次修正通过。
## 小型字节码 VM(2026-10-18)
新增 wordcode 指令与代码校验、共享代码对象、内部执行帧及显式帧切换求值器。语言函数已接入执行入口;call/execute 的对象返回值统一持有一份根引用,用后须 release_value。
新增 20 项执行测试,覆盖扩展参数、非法代码与控制流、变量及循环、嵌套调用和默认参数、闭包 Cell、容器与属性、返回值存活、报错清理、帧数与指令限额;已有 builtins 测试迁移到共享代码对象及持有式返回值,crate 文档新增直接执行指令示例。
本地仅运行 rustfmt 格式化和 git diff --check,并静态审阅代码与文档;未编译、未执行测试或冒烟、未下载依赖。远端仍由现有 ces-control CI 执行检查与双数据库测试。按用户要求不主动跟踪 CI/部署状态,本记录不宣称当前版本已通过运行验证。
+14
View File
@@ -0,0 +1,14 @@
# 检查与发布脚本
所有构建、测试、冒烟和部署脚本只在用户指定的远端环境运行。本目录 README 负责定位与副作用边界;全局流程见 [验证](../docs/operations/validation.md) 和 [部署](../docs/operations/deployment.md)。
| 文件 | 入口与副作用 |
| --- | --- |
| [check.sh](check.sh) | fmt 检查、workspace 测试、严格 Clippy;会使用 Cargo 依赖和构建产物 |
| [smoke.py](smoke.py) | 标准库 Python 脚本;构建后用 Cargo metadata 定位 debug 二进制,支持 CARGO_TARGET_DIR;临时 SQLite 数据、HTTP 探测和 SIGTERM 退出,不改生产配置 |
| [ci-ces-control.sh](ci-ces-control.sh) | 需要 ASTER_RELEASE_SHA;构建 ci 镜像、创建临时网络/PostgreSQL/检查容器,运行全部检查;退出清理临时容器和网络,保留专属远端 Cargo 缓存卷 |
| [deploy-ces-control.sh](deploy-ces-control.sh) | ces-control root 执行;校验已构建镜像、锁定发布、保留密码和数据、验证新服务后切换 current,失败尝试恢复旧版;保留镜像 |
CI/部署使用的镜像与路径仅属于本项目,禁止改动其他项目和共享 Runner。smoke.py 临时选择端口后再启动宿主,存在小的端口竞争窗口,指定环境遇到冲突可重试。
调整变量同步 [配置](../docs/operations/configuration.md),调整端点/命令同步 [运行指南](../docs/operations/README.md),调整脚本名同步 [工作流](../.gitea/workflows/README.md)。返回 [项目入口](../README.md)。