From f77b8370eaa48e3e54d7505360ccc25ff134a26a Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Mon, 19 Oct 2026 13:33:24 +0800 Subject: [PATCH] refactor: extract core objects into a standalone package --- Cargo.lock | 7 + Cargo.toml | 3 +- README.md | 7 +- crates/README.md | 5 +- crates/core/Cargo.toml | 3 + crates/core/README.md | 14 +- crates/core/src/README.md | 32 +-- crates/core/src/eval.rs | 20 +- crates/core/src/lib.rs | 31 +-- crates/core/src/vm.rs | 212 +++++++++--------- crates/core/tests/README.md | 2 +- crates/frontend/README.md | 2 +- crates/objects/Cargo.toml | 9 + crates/objects/README.md | 15 ++ crates/objects/src/README.md | 20 ++ crates/{core => objects}/src/builtins.rs | 0 crates/{core => objects}/src/code.rs | 17 +- crates/objects/src/error.rs | 66 ++++++ crates/{core => objects}/src/heap.rs | 0 crates/{core => objects}/src/instruction.rs | 26 +++ crates/objects/src/lib.rs | 41 ++++ crates/{core => objects}/src/number.rs | 0 crates/{core => objects}/src/object.rs | 0 crates/objects/src/store.rs | 125 +++++++++++ .../src/store_builtins.rs} | 4 +- crates/objects/tests/README.md | 9 + crates/objects/tests/store.rs | 96 ++++++++ docs/README.md | 12 +- docs/architecture/README.md | 9 +- docs/contributing/README.md | 8 +- docs/contributing/handoff.md | 6 +- docs/operations/deployment.md | 2 +- docs/operations/validation.md | 5 +- docs/runtime/README.md | 2 +- docs/runtime/builtins.md | 4 +- docs/runtime/bytecode.md | 4 +- docs/runtime/database-objects.md | 2 +- docs/runtime/frontend.md | 2 +- docs/runtime/lifecycle.md | 5 +- docs/runtime/objects.md | 6 +- 40 files changed, 631 insertions(+), 202 deletions(-) create mode 100644 crates/objects/Cargo.toml create mode 100644 crates/objects/README.md create mode 100644 crates/objects/src/README.md rename crates/{core => objects}/src/builtins.rs (100%) rename crates/{core => objects}/src/code.rs (94%) create mode 100644 crates/objects/src/error.rs rename crates/{core => objects}/src/heap.rs (100%) rename crates/{core => objects}/src/instruction.rs (88%) create mode 100644 crates/objects/src/lib.rs rename crates/{core => objects}/src/number.rs (100%) rename crates/{core => objects}/src/object.rs (100%) create mode 100644 crates/objects/src/store.rs rename crates/{core/src/vm_builtins.rs => objects/src/store_builtins.rs} (99%) create mode 100644 crates/objects/tests/README.md create mode 100644 crates/objects/tests/store.rs diff --git a/Cargo.lock b/Cargo.lock index 3e8a66a..1b61110 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,6 +138,9 @@ dependencies = [ [[package]] name = "core" version = "0.1.0" +dependencies = [ + "objects", +] [[package]] name = "cpufeatures" @@ -787,6 +790,10 @@ dependencies = [ "autocfg", ] +[[package]] +name = "objects" +version = "0.1.0" + [[package]] name = "once_cell" version = "1.21.4" diff --git a/Cargo.toml b/Cargo.toml index bbee1c7..8af5007 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/core", "crates/frontend", "crates/storage", "crates/cli", "crates/server", "crates/host"] +members = ["crates/core", "crates/objects", "crates/frontend", "crates/storage", "crates/cli", "crates/server", "crates/host"] resolver = "2" [workspace.package] @@ -10,6 +10,7 @@ license = "MIT" [workspace.dependencies] core = { path = "crates/core" } +objects = { path = "crates/objects" } storage = { path = "crates/storage" } host = { path = "crates/host" } frontend = { path = "crates/frontend" } diff --git a/README.md b/README.md index 4bb3bcf..26bfca0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Endor / Aster VM -Rust 编写的 Endor 虚拟机实现。当前具备 EndorObject、稳定对象分配、代际句柄、引用计数、显式循环回收,以及可执行语言函数的小型 wordcode VM。数字统一为无损表示优先的 Number。 +Rust 编写的 Endor 虚拟机实现。核心对象与对象存储位于独立 objects 包,core 专注 VM、执行帧与求值循环。当前具备 EndorObject、稳定对象分配、代际句柄、引用计数、显式循环回收,以及可执行语言函数的小型 wordcode VM。数字统一为无损表示优先的 Number。 Endor 采用平台解释执行模型;AST 到字节码的生成属于解释器内部准备步骤,使用者无需先构建独立程序。资源修订与依赖锁用于选择用户代码,解释器由平台统一升级;相关设计方向见 [解释执行与平台资源版本](docs/architecture/execution-and-versions.md),资源版本管理尚未实现。 @@ -12,6 +12,7 @@ Endor 采用平台解释执行模型;AST 到字节码的生成属于解释器 | --- | --- | | 查一个功能、设计或代码位置 | [文档总索引](docs/README.md) | | 看解释执行、资源修订、依赖锁和平台升级的关系 | [执行与版本模型](docs/architecture/execution-and-versions.md) | +| 看核心对象与对象存储 | [objects](crates/objects/README.md) | | 看源码目录与 crate 边界 | [crates](crates/README.md) | | 看字符串、流、文件与平台 URI 如何进入 lexer | [Endor 阅读器](docs/runtime/reader.md) | | 看源码词法、语法、AST 和解析命令 | [源码前端](docs/runtime/frontend.md) | @@ -27,8 +28,8 @@ Endor 采用平台解释执行模型;AST 到字节码的生成属于解释器 | 文件或目录 | 职责 | | --- | --- | -| Cargo.toml / Cargo.lock | 工作区、包依赖、Rust 版本要求及依赖锁定;包名与目录一致:core、frontend、storage、host、cli、server | -| crates/ | core、frontend、storage、host、cli、server 六个包,每个目录的 README 解释本目录 | +| Cargo.toml / Cargo.lock | 工作区、包依赖、Rust 版本要求及依赖锁定;包名与目录一致:core、objects、frontend、storage、host、cli、server | +| crates/ | core、objects、frontend、storage、host、cli、server 七个包,每个目录的 README 解释本目录 | | docs/ | 跨目录的架构、运行时契约、配置、运行、部署与维护说明 | | scripts/ | 指定远端环境中的检查、冒烟、CI 与部署脚本 | | .gitea/ | Gitea Actions 工作流 | diff --git a/crates/README.md b/crates/README.md index 9efe1d8..2654673 100644 --- a/crates/README.md +++ b/crates/README.md @@ -4,14 +4,15 @@ | 目录 | 包名/产物 | 职责 | | --- | --- | --- | -| [core](core/README.md) | core,库 endor_core | 零外部依赖的对象运行时、堆与字节码 VM | +| [core](core/README.md) | core,库 endor_core | VM、执行帧与字节码求值循环,仅依赖本地 objects | +| [objects](objects/README.md) | objects | 零依赖的核心对象、对象存储、代码对象与指令数据/静态验证 | | [frontend](frontend/README.md) | frontend | Endor 阅读器、手写 lexer、token PEG parser、基础函数/表达式 AST,不执行源码 | | [host](host/README.md) | host | UTF-8 JSON 文件基础信息,与数据库分离 | | [storage](storage/README.md) | storage | 为数据库特殊类保留的显式连接适配,不提供基础信息存储 | | [cli](cli/README.md) | cli,二进制 cli | 引导演示、源码解析与文件信息命令,不使用数据库 | | [server](server/README.md) | server,二进制 server | HTTP 宿主与文件版本信息,不使用数据库 | -CLI 依赖 Core、Frontend 与 Host,Server 依赖 Core 与 Host,二者不依赖 Storage;Frontend 依赖 peg,不依赖 Core;Core 与 Storage 也互不依赖。未来特殊类的宿主实现按需依赖 Storage,见 [数据库对象](../docs/runtime/database-objects.md)。 +CLI 依赖 Core、Frontend 与 Host,Server 依赖 Core 与 Host,二者不依赖 Storage;Frontend 依赖 peg,不依赖 Core;Core 只依赖本地 Objects;Objects 不依赖 Core 或第三方包,二者均不依赖 Storage。未来特殊类的宿主实现按需依赖 Storage,见 [数据库对象](../docs/runtime/database-objects.md)。 Endor 的解释执行流程与平台资源版本方向见 [执行与版本模型](../docs/architecture/execution-and-versions.md)。Frontend 到 AST 与 Core 执行字节码目前仍是两段独立能力,后续需要连接内部字节码生成;版本锁、平台资源后端和持久化字节码缓存均未实现。 diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 5481ffa..9146b1b 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -9,5 +9,8 @@ license.workspace = true [lib] name = "endor_core" +[dependencies] +objects.workspace = true + [lints] workspace = true diff --git a/crates/core/README.md b/crates/core/README.md index cd2a819..e075266 100644 --- a/crates/core/README.md +++ b/crates/core/README.md @@ -1,13 +1,15 @@ # core -Endor VM 内核,保持零外部依赖。负责 EndorObject、堆、代际句柄、引用计数/循环回收、基本载荷和小型 wordcode 执行器。公共 Rust API 从 src/lib.rs 导出。Cargo 包名为 core,Rust 引用名为 endor_core,由 Cargo.toml 的 [lib] 设置,避免与标准库 core 重名。 +Endor VM 包,仅负责 Vm、执行帧、调用协议和 wordcode 求值循环。依赖本地 [objects](../objects/README.md) 管理对象与堆,没有第三方依赖。Cargo 包名为 core,Rust 引用名为 endor_core,避免与标准库 core 重名。 | 内容 | 入口 | | --- | --- | -| Cargo.toml | 包元数据、endor_core 库目标和工作区 lint;没有外部依赖 | -| [src](src/README.md) | 文件职责、公开接口和常见修改位置 | -| [tests](tests/README.md) | 对象、基本载荷、执行语义与生命周期检查 | +| Cargo.toml | endor_core 库目标、本地 objects 依赖和工作区 lint | +| [src](src/README.md) | VM、内部执行帧、求值循环和对象操作委托 | +| [tests](tests/README.md) | 原有对象入口的兼容行为、调用/执行语义与生命周期 | -内核通过 Vm::new 从空堆启动;代码对象和全局字典由宿主显式创建,再 execute/call。内核没有环境变量探测、数据库、HTTP、Python type 系统或源码解析;源码阅读器与 lexer/parser 在独立 [frontend](../frontend/README.md) 包中,AST 到字节码生成尚未实现。解释执行与平台版本方向见 [执行与版本模型](../../docs/architecture/execution-and-versions.md)。 +Vm::new 创建独立 ObjectStore 和空执行状态;代码对象、全局字典和函数仍按宿主需要显式创建,再 execute/call。EndorObject、Number、ObjectRef、内置载荷、代码对象/指令定义与 VmError 在 objects 中实现;endor_core 保留这些类型的兼容重导出,既有 Vm 对象方法委托到 ObjectStore。 -对象结果所有权见 [生命周期](../../docs/runtime/lifecycle.md),对象/字节码设计见 [运行时](../../docs/runtime/README.md),限额与 UTF-8 边界见 [配置](../../docs/operations/configuration.md)。本地不构建或运行测试,验证入口见 [验证](../../docs/operations/validation.md)。返回 [工作区](../README.md)。 +没有 Python type/元类或语言根类。网络、数据库和进程环境属于宿主,源码阅读器与 lexer/parser 属于 [frontend](../frontend/README.md),AST 到字节码生成尚未实现,见 [执行与版本模型](../../docs/architecture/execution-and-versions.md)。 + +返回值所有权见 [生命周期](../../docs/runtime/lifecycle.md),执行契约见 [字节码 VM](../../docs/runtime/bytecode.md)。本地不构建或测试,见 [验证](../../docs/operations/validation.md)。返回 [工作区](../README.md)。 diff --git a/crates/core/src/README.md b/crates/core/src/README.md index baf1177..a1b6839 100644 --- a/crates/core/src/README.md +++ b/crates/core/src/README.md @@ -1,29 +1,21 @@ -# Core 源码导航 +# Core VM 源码导航 -本目录解释实现文件如何分工;跨文件的设计规则只在 [运行时文档](../../../docs/runtime/README.md) 维护。公开 API 的最终导出入口是 lib.rs。 +本目录只实现 VM、内部执行帧和 wordcode 求值循环。核心对象、对象堆、内置载荷、指令与代码对象数据属于独立 [objects/src](../../objects/src/README.md)。 -| 文件 | 内容 | +| 文件 | 职责 | | --- | --- | -| [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 编解码、MakeFunction 计数构造、指令位置与比较参数 | -| [code.rs](code.rs) | CodeLayout、EndorCode、共享 EndorCodeObject、操作数/控制流/栈校验 | +| [lib.rs](lib.rs) | VM 类型导出、objects 类型兼容重导出、宿主调用与字节码 rustdoc 示例 | +| [vm.rs](vm.rs) | Vm 拥有 ObjectStore 与错误位置记录;对象操作显式委托给 objects | | [frame.rs](frame.rs) | EndorFrame、栈条目、执行位置元数据、ExecutionLimits | -| [eval.rs](eval.rs) | execute/call、参数绑定、帧切换、取指分派、字节码创建函数、结果根转移与失败清理 | +| [eval.rs](eval.rs) | execute/call、参数绑定、帧切换、取指分派、MakeFunction、结果根转移与失败清理 | ## 常见修改入口 -| 要修改什么 | 涉及文件 | 同步说明 | +| 要修改什么 | 涉及位置 | 同步说明 | | --- | --- | --- | -| 对象公共操作或头部 | 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) | +| 对象表示、分配、引用或内置对象行为 | objects/src;兼容方法需要时同步 vm.rs 委托 | [对象模型](../../../docs/runtime/objects.md)、[基本对象](../../../docs/runtime/builtins.md) | +| 新操作码或代码布局 | objects 的 instruction.rs/code.rs、core 的 eval.rs | [字节码 VM](../../../docs/runtime/bytecode.md)、execution 检查 | +| 调用、闭包或返回值根管理 | frame.rs、eval.rs、objects 的函数 trace | [生命周期](../../../docs/runtime/lifecycle.md) | +| 新 VM 公开 API | vm.rs/eval.rs 与 lib.rs 导出 | 所属主题、示例、[tests 导航](../tests/README.md) | -复制 ObjectRef/Value 不增加引用;不能在自定义载荷中擅自改变已登记的托管边。对象 API 不暴露原始指针或句柄索引构造。返回 [core](../README.md)。 +core 不访问对象私有字段或 Heap;执行器只通过受校验的对象存储操作维护帧根。返回 [core](../README.md)。 diff --git a/crates/core/src/eval.rs b/crates/core/src/eval.rs index 2618fb3..570f17b 100644 --- a/crates/core/src/eval.rs +++ b/crates/core/src/eval.rs @@ -1,11 +1,14 @@ -use crate::code::jump_target; use crate::frame::StackEntry; -use crate::instruction::{compare_operation, function_counts, invalid}; use crate::{ CompareOp, EndorCellObject, EndorCodeObject, EndorFrame, EndorFunctionObject, - EndorStringObject, ExecutionLimits, ExecutionLocation, ObjectRef, Opcode, Value, Vm, VmError, + EndorStringObject, ExecutionLimits, ExecutionLocation, Instruction, ObjectRef, Opcode, Value, + Vm, VmError, }; +fn invalid(offset: usize, reason: &'static str) -> VmError { + VmError::InvalidBytecode { offset, reason } +} + impl Vm { /// Execute a code object in a globals dictionary. Like CPython's EvalCode /// entry path, this constructs a temporary function and enters its frame. @@ -315,7 +318,7 @@ impl Vm { } fn make_function(&mut self, frame: &EndorFrame, argument: u32) -> Result { - let (default_count, closure_count) = function_counts(argument); + let (default_count, closure_count) = Instruction::function_counts(argument); let Value::Object(code) = Self::top_value(frame)? else { return Err(VmError::ObjectKindMismatch("code")); }; @@ -494,13 +497,13 @@ impl Vm { } } Opcode::JumpForward | Opcode::JumpBackward => { - frame.instruction_offset = jump_target(decoded)? + frame.instruction_offset = decoded.jump_target()? } Opcode::PopJumpIfFalse | Opcode::PopJumpIfTrue => { let truth = self.value_is_true(&Self::top_value(frame)?)?; self.discard(frame, 1); if truth == (opcode == Opcode::PopJumpIfTrue) { - frame.instruction_offset = jump_target(decoded)?; + frame.instruction_offset = decoded.jump_target()?; } } Opcode::ToBool | Opcode::UnaryNot => { @@ -520,7 +523,7 @@ impl Vm { let result = self.compare_values( &left, &right, - compare_operation(decoded.instruction.argument).unwrap(), + Instruction::compare_operation(decoded.instruction.argument).unwrap(), )?; self.discard(frame, 2); frame.stack.push(StackEntry::Value(Value::Bool(result))); @@ -555,7 +558,8 @@ impl Vm { } Opcode::MakeFunction => { let function = self.make_function(frame, decoded.instruction.argument)?; - let (defaults, closure) = function_counts(decoded.instruction.argument); + let (defaults, closure) = + Instruction::function_counts(decoded.instruction.argument); self.discard(frame, defaults + closure + 1); frame.stack.push(StackEntry::Value(Value::Object(function))); } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 80411ba..464a00c 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,4 +1,4 @@ -//! Embeddable object runtime and a small wordcode interpreter. No parser or +//! Embeddable wordcode VM backed by the independent objects crate. No parser or //! Python language type system; wordcode is not CPython bytecode compatible. //! //! ``` @@ -38,30 +38,17 @@ //! # Ok::<(), endor_core::VmError>(()) //! ``` -mod builtins; -mod code; mod eval; mod frame; -mod heap; -mod instruction; -mod number; -mod object; mod vm; -mod vm_builtins; -pub use builtins::{ - EndorBaseExceptionObject, EndorCellObject, EndorDictObject, EndorFunctionObject, - EndorStringObject, EndorTracebackEntry, -}; -pub use code::{CodeLayout, EndorCode, EndorCodeObject}; pub use frame::{EndorFrame, ExecutionLimits, ExecutionLocation}; -pub use heap::ObjectRef; -pub use instruction::{DecodedInstruction, Instruction, Opcode}; -pub use number::{ - EndorDecimal, EndorInteger, EndorNumber, EndorNumberObject, EndorRational, EndorReal, +// Preserve existing embedding imports; objects owns these definitions. +pub use objects::{ + CodeLayout, CompareOp, DecodedInstruction, EndorBaseExceptionObject, EndorCellObject, + EndorCode, EndorCodeObject, EndorDecimal, EndorDictObject, EndorFunctionObject, EndorInteger, + EndorNativeCall, EndorNativeFunctionObject, EndorNumber, EndorNumberObject, EndorObject, + EndorRational, EndorReal, EndorStringObject, EndorTracebackEntry, Instruction, NativeCall, + NativeFunction, ObjectHeader, ObjectPayload, ObjectRef, Opcode, Record, Value, VmError, }; -pub use object::{ - CompareOp, EndorNativeCall, EndorNativeFunctionObject, EndorObject, NativeCall, NativeFunction, - ObjectHeader, ObjectPayload, Record, Value, -}; -pub use vm::{Vm, VmError}; +pub use vm::Vm; diff --git a/crates/core/src/vm.rs b/crates/core/src/vm.rs index 0ea2e61..cb31da5 100644 --- a/crates/core/src/vm.rs +++ b/crates/core/src/vm.rs @@ -1,138 +1,74 @@ -use std::fmt; +use objects::ObjectStore; -use crate::heap::Heap; -use crate::{CompareOp, EndorObject, ExecutionLocation, ObjectPayload, ObjectRef, Record, Value}; +use crate::{ + CompareOp, EndorCode, EndorNumber, EndorObject, ExecutionLocation, ObjectPayload, ObjectRef, + Value, VmError, +}; -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum VmError { - ForeignHandle, - InvalidHandle, - IdentityExhausted, - RefCountOverflow, - NoOwnedReference, - AttributeNotFound(String), - UnsupportedOperation(&'static str), - InvalidArgument(&'static str), - ObjectKindMismatch(&'static str), - EmptyCell, - InvalidBytecode { - offset: usize, - reason: &'static str, - }, - UnboundLocal(String), - NameNotFound(String), - ArgumentCount { - name: String, - required: usize, - maximum: usize, - provided: usize, - }, - ExecutionLimit(&'static str), -} - -impl fmt::Display for VmError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::ForeignHandle => f.write_str("handle belongs to another VM"), - Self::InvalidHandle => f.write_str("handle does not identify a live object"), - Self::IdentityExhausted => f.write_str("process-local VM identities exhausted"), - Self::RefCountOverflow => f.write_str("object reference count exhausted"), - Self::NoOwnedReference => f.write_str("no external strong reference to release"), - Self::AttributeNotFound(name) => write!(f, "attribute not found: {name}"), - Self::UnsupportedOperation(operation) => { - write!(f, "object does not support {operation}") - } - Self::InvalidArgument(message) => f.write_str(message), - Self::ObjectKindMismatch(expected) => write!(f, "expected {expected} object"), - Self::EmptyCell => f.write_str("cell is empty"), - Self::InvalidBytecode { offset, reason } => { - write!(f, "invalid bytecode at code unit {offset}: {reason}") - } - Self::UnboundLocal(name) => write!(f, "local variable is unbound: {name}"), - Self::NameNotFound(name) => write!(f, "name not found: {name}"), - Self::ArgumentCount { - name, - required, - maximum, - provided, - } => write!( - f, - "{name} expects {required}..={maximum} positional arguments, received {provided}" - ), - Self::ExecutionLimit(limit) => write!(f, "execution limit reached: {limit}"), - } - } -} - -impl std::error::Error for VmError {} - -/// Runtime owner. Construction creates an empty heap, without language classes, -/// metaclasses or a Python type system. Dropping it releases all host resources. +/// Owns the object store and execution state. Object representation, allocation, +/// reference accounting and builtin operations live in the objects crate. #[derive(Debug)] pub struct Vm { - pub(crate) heap: Heap, + objects: ObjectStore, pub(crate) last_traceback: Vec, } impl Vm { pub fn new() -> Result { Ok(Self { - heap: Heap::new()?, + objects: ObjectStore::new()?, last_traceback: Vec::new(), }) } pub fn object_count(&self) -> usize { - self.heap.len() + self.objects.object_count() } /// Returns one external strong reference. Copies of the returned handle /// are borrowed; use retain/release to manage additional owned references. pub fn allocate(&mut self, payload: impl ObjectPayload) -> Result { - self.validate_builtin(&payload)?; - self.heap.allocate(Box::new(payload)) + self.objects.allocate(payload) } pub fn new_object(&mut self) -> Result { - self.allocate(Record) + self.objects.new_object() } pub fn object(&self, reference: ObjectRef) -> Result<&EndorObject, VmError> { - self.heap.get(reference) + self.objects.object(reference) } pub fn retain(&mut self, reference: ObjectRef) -> Result<(), VmError> { - self.heap.retain(reference) + self.objects.retain(reference) } pub fn release(&mut self, reference: ObjectRef) -> Result<(), VmError> { - self.heap.release(reference) + self.objects.release(reference) } pub fn ref_count(&self, reference: ObjectRef) -> Result { - Ok(self.heap.get(reference)?.header().ref_count()) + self.objects.ref_count(reference) } pub fn is_same(&self, left: ObjectRef, right: ObjectRef) -> Result { - self.heap.get(left)?; - self.heap.get(right)?; - Ok(left == right) + self.objects.is_same(left, right) } pub fn repr(&self, reference: ObjectRef) -> Result { - self.heap.get(reference)?.payload().repr(reference) + self.objects.repr(reference) } pub fn str(&self, reference: ObjectRef) -> Result { - self.heap.get(reference)?.payload().str(reference) + self.objects.str(reference) } pub fn is_true(&self, reference: ObjectRef) -> Result { - self.heap.get(reference)?.payload().is_true() + self.objects.is_true(reference) } pub fn hash(&self, reference: ObjectRef) -> Result { - self.heap.get(reference)?.payload().hash(reference) + self.objects.hash(reference) } pub fn compare( @@ -141,25 +77,13 @@ impl Vm { right: ObjectRef, operation: CompareOp, ) -> Result { - let left_payload = self.heap.get(left)?.payload(); - let right_payload = self.heap.get(right)?.payload(); - if let Some(result) = left_payload.compare(right_payload, operation)? { - return Ok(result); - } - if let Some(result) = right_payload.compare(left_payload, operation.reflected())? { - return Ok(result); - } - match operation { - CompareOp::Eq => Ok(left == right), - CompareOp::Ne => Ok(left != right), - _ => Err(VmError::UnsupportedOperation("ordering")), - } + self.objects.compare(left, right, operation) } /// Reads a borrowed value. An Object result needs retain() if the host /// wants it to survive removal of the attribute that currently owns it. pub fn get_attr(&self, reference: ObjectRef, name: &str) -> Result { - self.heap.get_attr(reference, name) + self.objects.get_attr(reference, name) } /// Object-valued attributes own internal strong references independently @@ -170,23 +94,99 @@ impl Vm { name: impl Into, value: Value, ) -> Result<(), VmError> { - self.heap.set_attr(reference, name.into(), value) + self.objects.set_attr(reference, name, value) } pub fn del_attr(&mut self, reference: ObjectRef, name: &str) -> Result<(), VmError> { - self.heap.del_attr(reference, name) + self.objects.del_attr(reference, name) } pub(crate) fn validate_value(&self, value: &Value) -> Result<(), VmError> { - if let Value::Object(reference) = value { - self.heap.get(*reference)?; - } - Ok(()) + self.objects.validate_value(value) } /// Explicit full collection. Execution slots use owned roots, so values /// on live execution stacks participate in the same root accounting. pub fn collect_cycles(&mut self) -> usize { - self.heap.collect_cycles() + self.objects.collect_cycles() + } + + pub fn payload( + &self, + reference: ObjectRef, + expected: &'static str, + ) -> Result<&T, VmError> { + self.objects.payload::(reference, expected) + } + + pub fn new_string(&mut self, text: impl Into) -> Result { + self.objects.new_string(text) + } + + pub fn new_code(&mut self, code: EndorCode) -> Result { + self.objects.new_code(code) + } + + pub fn new_function( + &mut self, + name: impl Into, + code: ObjectRef, + globals: ObjectRef, + defaults: Vec, + closure: Vec, + ) -> Result { + self.objects + .new_function(name, code, globals, defaults, closure) + } + + pub fn new_number(&mut self, number: EndorNumber) -> Result { + self.objects.new_number(number) + } + + pub fn new_dict(&mut self) -> Result { + self.objects.new_dict() + } + + /// Returns borrowed keys and values in insertion order. + pub fn dict_items(&self, dictionary: ObjectRef) -> Result<&[(Value, Value)], VmError> { + self.objects.dict_items(dictionary) + } + + pub fn dict_get(&self, dictionary: ObjectRef, key: &Value) -> Result, VmError> { + self.objects.dict_get(dictionary, key) + } + + pub fn dict_set( + &mut self, + dictionary: ObjectRef, + key: Value, + value: Value, + ) -> Result<(), VmError> { + self.objects.dict_set(dictionary, key, value) + } + + pub fn dict_delete(&mut self, dictionary: ObjectRef, key: &Value) -> Result { + self.objects.dict_delete(dictionary, key) + } + + pub fn dict_clear(&mut self, dictionary: ObjectRef) -> Result<(), VmError> { + self.objects.dict_clear(dictionary) + } + + pub fn new_cell(&mut self, value: Option) -> Result { + self.objects.new_cell(value) + } + + /// Reads a borrowed value; an empty cell is not Value::Nil. + pub fn cell_get(&self, cell: ObjectRef) -> Result { + self.objects.cell_get(cell) + } + + pub fn cell_set(&mut self, cell: ObjectRef, value: Value) -> Result<(), VmError> { + self.objects.cell_set(cell, value) + } + + pub fn cell_clear(&mut self, cell: ObjectRef) -> Result<(), VmError> { + self.objects.cell_clear(cell) } } diff --git a/crates/core/tests/README.md b/crates/core/tests/README.md index e0eeec5..6e611d3 100644 --- a/crates/core/tests/README.md +++ b/crates/core/tests/README.md @@ -8,4 +8,4 @@ | [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)。 +核心对象现由 objects 定义,本目录保留既有 endor_core 导入与 Vm 方法的兼容性检查。独立对象存储检查见 [objects/tests](../../objects/tests/README.md)。堆内部伪造句柄与计数溢出检查在 objects 的 src/heap.rs,公开示例由 src/lib.rs rustdoc 提供。文件职责见 [src](../src/README.md),检查方式和结果边界见 [验证](../../../docs/operations/validation.md)。本地不执行测试;存在测试文件不代表某次提交已通过。返回 [core](../README.md)。 diff --git a/crates/frontend/README.md b/crates/frontend/README.md index 830f162..f14ed33 100644 --- a/crates/frontend/README.md +++ b/crates/frontend/README.md @@ -9,4 +9,4 @@ | [tests](tests/README.md) | Unicode、优先级、函数、完整输入与错误位置验证 | | [examples](examples/README.md) | 可交给 cli parse 的源码样例 | -宿主通过 EndorReader 读取字符串、流、文件或注入的 URI provider,取得 SourceUnit 后调用 lex_source、parse_source、parse_source_expression;低层 lex、parse、parse_expression 仍保留。读取契约见 [Endor 阅读器](../../docs/runtime/reader.md)。Core 仍为零外部依赖;后续把 AST 转成已有字节码并交给 VM 解释执行;这是平台内部准备步骤,尚未接通。资源修订、依赖锁和解释器统一升级的设计方向见 [执行与版本模型](../../docs/architecture/execution-and-versions.md)。返回 [工作区](../README.md)。 +宿主通过 EndorReader 读取字符串、流、文件或注入的 URI provider,取得 SourceUnit 后调用 lex_source、parse_source、parse_source_expression;低层 lex、parse、parse_expression 仍保留。读取契约见 [Endor 阅读器](../../docs/runtime/reader.md)。Objects 保持零依赖,Core 仅依赖本地 Objects;后续把 AST 转成已有字节码并交给 VM 解释执行;这是平台内部准备步骤,尚未接通。资源修订、依赖锁和解释器统一升级的设计方向见 [执行与版本模型](../../docs/architecture/execution-and-versions.md)。返回 [工作区](../README.md)。 diff --git a/crates/objects/Cargo.toml b/crates/objects/Cargo.toml new file mode 100644 index 0000000..7ecfd7c --- /dev/null +++ b/crates/objects/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "objects" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true + +[lints] +workspace = true diff --git a/crates/objects/README.md b/crates/objects/README.md new file mode 100644 index 0000000..33bd769 --- /dev/null +++ b/crates/objects/README.md @@ -0,0 +1,15 @@ +# objects + +Endor 核心对象与对象存储包,零外部依赖,不依赖 core。包含 EndorObject、ObjectPayload、Value、ObjectRef、Number、字符串/字典/Cell/函数/异常载荷,以及代码对象与 wordcode 数据定义、编解码和静态验证。没有 Python type/元类或语言根类。 + +| 内容 | 职责 | +| --- | --- | +| Cargo.toml | objects 包元数据和工作区 lint;无依赖 | +| [src](src/README.md) | 对象定义、私有堆、ObjectStore、对象工厂、字节码数据与共享错误 | +| [tests](tests/README.md) | 不依赖 VM 的对象存储、事务失败、回收和共享代码检查 | + +ObjectStore 独立拥有空堆,负责对象分配、代际校验、引用计数、循环回收、属性与内置容器操作;底层 Heap、句柄字段和可变载荷替换仍为私有实现。代码对象只保存及验证执行数据,求值循环与调用帧属于 [core](../core/README.md)。共享 VmError 定义在本包,保留现有名称以避免对象操作接口依赖 VM。 + +新代码可以直接从 objects 引用对象类型;endor_core 继续重导出现有类型,Vm 的对象方法委托给本包,原有宿主入口保持可用。复制句柄不增加引用;独立 ObjectStore 的对象句柄不能交给另一存储或 VM 使用。 + +设计与所有权见 [对象模型](../../docs/runtime/objects.md)、[基本对象](../../docs/runtime/builtins.md) 和 [生命周期](../../docs/runtime/lifecycle.md)。本地不构建或测试,运行检查交给指定远端,见 [验证](../../docs/operations/validation.md)。返回 [工作区](../README.md)。 diff --git a/crates/objects/src/README.md b/crates/objects/src/README.md new file mode 100644 index 0000000..ed39b9e --- /dev/null +++ b/crates/objects/src/README.md @@ -0,0 +1,20 @@ +# Objects 源码导航 + +公开导出集中在 lib.rs;本目录只负责对象与执行数据,不包含 VM 调度或取指分派。全局契约见 [运行时文档](../../../docs/runtime/README.md)。 + +| 文件 | 职责 | +| --- | --- | +| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出与独立存储 rustdoc 示例 | +| [object.rs](object.rs) | Value、CompareOp、ObjectPayload、ObjectHeader、EndorObject、Record、NativeFunction | +| [number.rs](number.rs) | Integer/Decimal/Rational/BinaryFloat/Complex 的无损表示和装箱载荷 | +| [builtins.rs](builtins.rs) | String、Dict、Cell、Function、BaseException 载荷与 trace 边 | +| [heap.rs](heap.rs) | 私有 Heap、不可伪造 ObjectRef、稳定 Box、引用事务、迭代释放、循环回收及私有边界测试 | +| [store.rs](store.rs) | ObjectStore、空堆创建、对象操作、retain/release 和回收入口 | +| [store_builtins.rs](store_builtins.rs) | 载荷校验、代码/函数工厂、Dict/Cell 操作及键语义 | +| [instruction.rs](instruction.rs) | Opcode、Instruction、wordcode 编解码、MakeFunction/比较参数与跳转读取接口 | +| [code.rs](code.rs) | CodeLayout、EndorCode/EndorCodeObject、共享不可变代码和操作数/控制流/栈验证 | +| [error.rs](error.rs) | 对象、代码与执行共用的 VmError,避免反向依赖 core | + +对象操作变更同步 [对象模型](../../../docs/runtime/objects.md),内置载荷同步 [基本对象](../../../docs/runtime/builtins.md)。新增操作码需要同时修改本包 instruction.rs/code.rs 和 core 的 eval.rs 分派,见 [字节码 VM](../../../docs/runtime/bytecode.md)。 + +Heap、ObjectRef 字段、对象头计数和可变容器字段不向包外开放;公开代码读取接口不允许改写已登记的常量和托管边。检查入口见 [tests](../tests/README.md)。返回 [objects](../README.md)。 diff --git a/crates/core/src/builtins.rs b/crates/objects/src/builtins.rs similarity index 100% rename from crates/core/src/builtins.rs rename to crates/objects/src/builtins.rs diff --git a/crates/core/src/code.rs b/crates/objects/src/code.rs similarity index 94% rename from crates/core/src/code.rs rename to crates/objects/src/code.rs index 8c5feb6..cef74e1 100644 --- a/crates/core/src/code.rs +++ b/crates/objects/src/code.rs @@ -121,17 +121,23 @@ impl EndorCode { pub fn slot_count(&self) -> usize { self.layout.locals.len() + self.layout.free_variables.len() } - pub(crate) fn is_cell_slot(&self, slot: usize) -> bool { + /// Whether a local/free slot holds a managed closure cell. + pub fn is_cell_slot(&self, slot: usize) -> bool { is_cell_slot(&self.layout, slot) } - pub(crate) fn slot_name(&self, slot: usize) -> &str { + /// Name of a local/free slot used by validated instructions. + /// + /// # Panics + /// Panics if the slot is outside this code's local/free-variable layout. + pub fn slot_name(&self, slot: usize) -> &str { if slot < self.layout.locals.len() { &self.layout.locals[slot] } else { &self.layout.free_variables[slot - self.layout.locals.len()] } } - pub(crate) fn instruction_at(&self, offset: usize) -> Result { + /// Look up a logical instruction by its code-unit offset. + pub fn instruction_at(&self, offset: usize) -> Result { instruction_index(&self.instructions, offset) .map(|index| self.instructions[index]) .ok_or_else(|| { @@ -159,7 +165,10 @@ impl EndorCodeObject { pub fn code(&self) -> &EndorCode { &self.code } - pub(crate) fn shared_code(&self) -> Arc { + /// Share immutable executable data without copying constants or wordcode. + /// This Rust Arc does not retain heap objects referenced by constants; + /// callers must keep the managed code object rooted while using them. + pub fn shared_code(&self) -> Arc { Arc::clone(&self.code) } } diff --git a/crates/objects/src/error.rs b/crates/objects/src/error.rs new file mode 100644 index 0000000..fe7f11f --- /dev/null +++ b/crates/objects/src/error.rs @@ -0,0 +1,66 @@ +use std::fmt; + +/// Shared object/bytecode/execution errors. The established VmError name is +/// retained for embedders; defining it here avoids a dependency on the VM. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum VmError { + ForeignHandle, + InvalidHandle, + IdentityExhausted, + RefCountOverflow, + NoOwnedReference, + AttributeNotFound(String), + UnsupportedOperation(&'static str), + InvalidArgument(&'static str), + ObjectKindMismatch(&'static str), + EmptyCell, + InvalidBytecode { + offset: usize, + reason: &'static str, + }, + UnboundLocal(String), + NameNotFound(String), + ArgumentCount { + name: String, + required: usize, + maximum: usize, + provided: usize, + }, + ExecutionLimit(&'static str), +} + +impl fmt::Display for VmError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ForeignHandle => f.write_str("handle belongs to another VM"), + Self::InvalidHandle => f.write_str("handle does not identify a live object"), + Self::IdentityExhausted => f.write_str("process-local VM identities exhausted"), + Self::RefCountOverflow => f.write_str("object reference count exhausted"), + Self::NoOwnedReference => f.write_str("no external strong reference to release"), + Self::AttributeNotFound(name) => write!(f, "attribute not found: {name}"), + Self::UnsupportedOperation(operation) => { + write!(f, "object does not support {operation}") + } + Self::InvalidArgument(message) => f.write_str(message), + Self::ObjectKindMismatch(expected) => write!(f, "expected {expected} object"), + Self::EmptyCell => f.write_str("cell is empty"), + Self::InvalidBytecode { offset, reason } => { + write!(f, "invalid bytecode at code unit {offset}: {reason}") + } + Self::UnboundLocal(name) => write!(f, "local variable is unbound: {name}"), + Self::NameNotFound(name) => write!(f, "name not found: {name}"), + Self::ArgumentCount { + name, + required, + maximum, + provided, + } => write!( + f, + "{name} expects {required}..={maximum} positional arguments, received {provided}" + ), + Self::ExecutionLimit(limit) => write!(f, "execution limit reached: {limit}"), + } + } +} + +impl std::error::Error for VmError {} diff --git a/crates/core/src/heap.rs b/crates/objects/src/heap.rs similarity index 100% rename from crates/core/src/heap.rs rename to crates/objects/src/heap.rs diff --git a/crates/core/src/instruction.rs b/crates/objects/src/instruction.rs similarity index 88% rename from crates/core/src/instruction.rs rename to crates/objects/src/instruction.rs index 0d09756..35b3aad 100644 --- a/crates/core/src/instruction.rs +++ b/crates/objects/src/instruction.rs @@ -127,6 +127,16 @@ impl Instruction { ) } + /// Decode a comparison operand; None denotes an invalid operand. + pub fn compare_operation(argument: u32) -> Option { + compare_operation(argument) + } + + /// Decode the default-value and closure-cell counts of MAKE_FUNCTION. + pub fn function_counts(argument: u32) -> (usize, usize) { + function_counts(argument) + } + /// Encodes 8-bit operands directly and prefixes up to three EXTENDED_ARG /// code units for larger operands. Each code unit is [opcode, operand]. pub fn encode_all(instructions: &[Self]) -> Result, VmError> { @@ -157,6 +167,22 @@ pub struct DecodedInstruction { pub instruction: Instruction, } +impl DecodedInstruction { + /// Resolve a jump relative to the end of this logical instruction. + pub fn jump_target(self) -> Result { + if !matches!( + self.instruction.opcode, + Opcode::JumpForward + | Opcode::JumpBackward + | Opcode::PopJumpIfFalse + | Opcode::PopJumpIfTrue + ) { + return Err(invalid(self.offset, "instruction is not a jump")); + } + crate::code::jump_target(self) + } +} + pub(crate) fn decode(bytes: &[u8]) -> Result, VmError> { if bytes.is_empty() || !bytes.len().is_multiple_of(2) { return Err(invalid( diff --git a/crates/objects/src/lib.rs b/crates/objects/src/lib.rs new file mode 100644 index 0000000..cc16e9c --- /dev/null +++ b/crates/objects/src/lib.rs @@ -0,0 +1,41 @@ +//! Core Endor objects, values, validated executable data and object storage. +//! This crate has no VM evaluator or external dependencies. Handles, reference +//! accounting and mutable payload replacement remain encapsulated here. +//! +//! ``` +//! use objects::{ObjectStore, Value}; +//! let mut store = ObjectStore::new()?; +//! let object = store.new_object()?; +//! store.set_attr(object, "name", Value::Text("Endor".into()))?; +//! assert_eq!(store.get_attr(object, "name")?, Value::Text("Endor".into())); +//! store.release(object)?; +//! assert_eq!(store.object_count(), 0); +//! # Ok::<(), objects::VmError>(()) +//! ``` + +mod builtins; +mod code; +mod error; +mod heap; +mod instruction; +mod number; +mod object; +mod store; +mod store_builtins; + +pub use builtins::{ + EndorBaseExceptionObject, EndorCellObject, EndorDictObject, EndorFunctionObject, + EndorStringObject, EndorTracebackEntry, +}; +pub use code::{CodeLayout, EndorCode, EndorCodeObject}; +pub use error::VmError; +pub use heap::ObjectRef; +pub use instruction::{DecodedInstruction, Instruction, Opcode}; +pub use number::{ + EndorDecimal, EndorInteger, EndorNumber, EndorNumberObject, EndorRational, EndorReal, +}; +pub use object::{ + CompareOp, EndorNativeCall, EndorNativeFunctionObject, EndorObject, NativeCall, NativeFunction, + ObjectHeader, ObjectPayload, Record, Value, +}; +pub use store::ObjectStore; diff --git a/crates/core/src/number.rs b/crates/objects/src/number.rs similarity index 100% rename from crates/core/src/number.rs rename to crates/objects/src/number.rs diff --git a/crates/core/src/object.rs b/crates/objects/src/object.rs similarity index 100% rename from crates/core/src/object.rs rename to crates/objects/src/object.rs diff --git a/crates/objects/src/store.rs b/crates/objects/src/store.rs new file mode 100644 index 0000000..39b0b63 --- /dev/null +++ b/crates/objects/src/store.rs @@ -0,0 +1,125 @@ +use crate::heap::Heap; +use crate::{CompareOp, EndorObject, ObjectPayload, ObjectRef, Record, Value, VmError}; + +/// Object storage independent of the VM evaluator. Construction creates an empty +/// heap; dropping it releases all objects and their host resources. Mutable +/// access is limited to operations that validate and account for owned edges. +#[derive(Debug)] +pub struct ObjectStore { + pub(crate) heap: Heap, +} + +impl ObjectStore { + pub fn new() -> Result { + Ok(Self { heap: Heap::new()? }) + } + + pub fn object_count(&self) -> usize { + self.heap.len() + } + + /// Returns one external strong reference. Copies of the returned handle + /// are borrowed; use retain/release to manage additional owned references. + pub fn allocate(&mut self, payload: impl ObjectPayload) -> Result { + self.validate_builtin(&payload)?; + self.heap.allocate(Box::new(payload)) + } + + pub fn new_object(&mut self) -> Result { + self.allocate(Record) + } + + pub fn object(&self, reference: ObjectRef) -> Result<&EndorObject, VmError> { + self.heap.get(reference) + } + + pub fn retain(&mut self, reference: ObjectRef) -> Result<(), VmError> { + self.heap.retain(reference) + } + + pub fn release(&mut self, reference: ObjectRef) -> Result<(), VmError> { + self.heap.release(reference) + } + + pub fn ref_count(&self, reference: ObjectRef) -> Result { + Ok(self.heap.get(reference)?.header().ref_count()) + } + + pub fn is_same(&self, left: ObjectRef, right: ObjectRef) -> Result { + self.heap.get(left)?; + self.heap.get(right)?; + Ok(left == right) + } + + pub fn repr(&self, reference: ObjectRef) -> Result { + self.heap.get(reference)?.payload().repr(reference) + } + + pub fn str(&self, reference: ObjectRef) -> Result { + self.heap.get(reference)?.payload().str(reference) + } + + pub fn is_true(&self, reference: ObjectRef) -> Result { + self.heap.get(reference)?.payload().is_true() + } + + pub fn hash(&self, reference: ObjectRef) -> Result { + self.heap.get(reference)?.payload().hash(reference) + } + + pub fn compare( + &self, + left: ObjectRef, + right: ObjectRef, + operation: CompareOp, + ) -> Result { + let left_payload = self.heap.get(left)?.payload(); + let right_payload = self.heap.get(right)?.payload(); + if let Some(result) = left_payload.compare(right_payload, operation)? { + return Ok(result); + } + if let Some(result) = right_payload.compare(left_payload, operation.reflected())? { + return Ok(result); + } + match operation { + CompareOp::Eq => Ok(left == right), + CompareOp::Ne => Ok(left != right), + _ => Err(VmError::UnsupportedOperation("ordering")), + } + } + + /// Reads a borrowed value. An Object result needs retain() if the host + /// wants it to survive removal of the attribute that currently owns it. + pub fn get_attr(&self, reference: ObjectRef, name: &str) -> Result { + self.heap.get_attr(reference, name) + } + + /// Object-valued attributes own internal strong references independently + /// of the caller's external references. Failure leaves the field unchanged. + pub fn set_attr( + &mut self, + reference: ObjectRef, + name: impl Into, + value: Value, + ) -> Result<(), VmError> { + self.heap.set_attr(reference, name.into(), value) + } + + pub fn del_attr(&mut self, reference: ObjectRef, name: &str) -> Result<(), VmError> { + self.heap.del_attr(reference, name) + } + + /// Validate a borrowed object handle without taking an owned reference. + pub fn validate_value(&self, value: &Value) -> Result<(), VmError> { + if let Value::Object(reference) = value { + self.heap.get(*reference)?; + } + Ok(()) + } + + /// Explicit full collection. Execution slots use owned roots, so values + /// on live execution stacks participate in the same root accounting. + pub fn collect_cycles(&mut self) -> usize { + self.heap.collect_cycles() + } +} diff --git a/crates/core/src/vm_builtins.rs b/crates/objects/src/store_builtins.rs similarity index 99% rename from crates/core/src/vm_builtins.rs rename to crates/objects/src/store_builtins.rs index 7fe663e..84e30ab 100644 --- a/crates/core/src/vm_builtins.rs +++ b/crates/objects/src/store_builtins.rs @@ -4,10 +4,10 @@ use std::hash::{Hash, Hasher}; use crate::{ CompareOp, EndorBaseExceptionObject, EndorCellObject, EndorCode, EndorCodeObject, EndorDictObject, EndorFunctionObject, EndorNumber, EndorNumberObject, EndorStringObject, - ObjectPayload, ObjectRef, Value, Vm, VmError, + ObjectPayload, ObjectRef, ObjectStore, Value, VmError, }; -impl Vm { +impl ObjectStore { pub(crate) fn validate_builtin(&self, payload: &dyn ObjectPayload) -> Result<(), VmError> { if let Some(function) = payload.as_any().downcast_ref::() { let code = self diff --git a/crates/objects/tests/README.md b/crates/objects/tests/README.md new file mode 100644 index 0000000..167ccbd --- /dev/null +++ b/crates/objects/tests/README.md @@ -0,0 +1,9 @@ +# Objects 独立行为检查 + +本目录只依赖 objects 公共 API,不依赖 core 或 VM 求值器。 + +| 文件 | 覆盖 | +| --- | --- | +| [store.rs](store.rs) | 稳定地址、跨存储句柄拒绝与更新原子性、Dict/Cell 循环回收、函数校验失败无泄漏、共享代码与常量生命周期 | + +非法句柄索引、槽位代数与计数溢出仍在 src/heap.rs 的私有单元测试内,不开放句柄构造器。VM 委托 API 与求值行为继续由 [core/tests](../../core/tests/README.md) 检查。随工作区在指定远端运行,本地不测试;现有测试源码不代表已通过,见 [验证](../../../docs/operations/validation.md)。返回 [objects](../README.md)。 diff --git a/crates/objects/tests/store.rs b/crates/objects/tests/store.rs new file mode 100644 index 0000000..82ede20 --- /dev/null +++ b/crates/objects/tests/store.rs @@ -0,0 +1,96 @@ +use std::sync::Arc; + +use objects::{ + CodeLayout, EndorCode, EndorCodeObject, EndorFunctionObject, Instruction, ObjectStore, Opcode, + Value, VmError, +}; + +#[test] +fn standalone_storage_preserves_identity_validates_edges_and_collects_cycles() { + let mut store = ObjectStore::new().unwrap(); + let mut other = ObjectStore::new().unwrap(); + let dictionary = store.new_dict().unwrap(); + let cell = store.new_cell(Some(Value::Object(dictionary))).unwrap(); + let foreign = other.new_object().unwrap(); + let address = store.object(dictionary).unwrap() as *const _; + let key = Value::Text("cell".into()); + + store + .dict_set(dictionary, key.clone(), Value::Object(cell)) + .unwrap(); + assert_eq!(store.object(dictionary).unwrap() as *const _, address); + assert_eq!( + store.dict_set(dictionary, key.clone(), Value::Object(foreign)), + Err(VmError::ForeignHandle) + ); + assert_eq!( + store.dict_get(dictionary, &key).unwrap(), + Some(Value::Object(cell)) + ); + assert_eq!(store.ref_count(cell).unwrap(), 2); + store.release(dictionary).unwrap(); + store.release(cell).unwrap(); + assert_eq!(store.collect_cycles(), 2); + assert_eq!(store.object_count(), 0); + assert!(matches!(store.object(cell), Err(VmError::InvalidHandle))); + other.release(foreign).unwrap(); +} + +#[test] +fn standalone_function_factories_validate_and_own_shared_executable_data() { + let mut store = ObjectStore::new().unwrap(); + let constant = store.new_string("kept by code").unwrap(); + let globals = store.new_dict().unwrap(); + let code = EndorCode::from_instructions( + &[ + Instruction::new(Opcode::LoadConst, 0), + Instruction::simple(Opcode::ReturnValue), + ], + vec![Value::Object(constant)], + CodeLayout::default(), + ) + .unwrap(); + let code = store.new_code(code).unwrap(); + let before = store.object_count(); + assert_eq!( + store.new_function("bad", code, globals, vec![Value::Nil], vec![]), + Err(VmError::InvalidArgument( + "function defaults or closure do not match code" + )) + ); + assert_eq!(store.object_count(), before); + assert_eq!(store.ref_count(code).unwrap(), 1); + let first = store + .new_function("first", code, globals, vec![], vec![]) + .unwrap(); + let second = store + .new_function("second", code, globals, vec![], vec![]) + .unwrap(); + let first_code = store + .payload::(first, "function") + .unwrap() + .code(); + let second_code = store + .payload::(second, "function") + .unwrap() + .code(); + let first_executable = store + .payload::(first_code, "code") + .unwrap() + .shared_code(); + let second_executable = store + .payload::(second_code, "code") + .unwrap() + .shared_code(); + assert!(Arc::ptr_eq(&first_executable, &second_executable)); + drop(first_executable); + drop(second_executable); + store.release(constant).unwrap(); + store.release(code).unwrap(); + store.release(globals).unwrap(); + assert_eq!(store.str(constant).unwrap(), "kept by code"); + store.release(first).unwrap(); + assert_eq!(store.ref_count(code).unwrap(), 1); + store.release(second).unwrap(); + assert_eq!(store.object_count(), 0); +} diff --git a/docs/README.md b/docs/README.md index 9da5ed0..caf0fdb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,19 +8,19 @@ | --- | --- | --- | | 各个 crate 如何分层,哪些能力已实现 | [架构](architecture/README.md) | [crates](../crates/README.md) | | 解释型为何生成字节码,资源修订与平台升级如何区分 | [执行与版本模型(设计方向)](architecture/execution-and-versions.md) | [frontend](../crates/frontend/README.md)、[core](../crates/core/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) | +| EndorObject、对象头、句柄、默认操作 | [对象模型](runtime/objects.md) | [objects/src](../crates/objects/src/README.md) | +| Number、String、Dict、Cell、Function、异常数据 | [基本对象](runtime/builtins.md) | [objects/src](../crates/objects/src/README.md) | | 字符串、文件、流与 endor URI 如何进入 lexer | [Endor 阅读器](runtime/reader.md) | [reader.rs](../crates/frontend/src/reader.rs)、[source.rs](../crates/frontend/src/source.rs) | | 源码如何分词、PEG 规则、AST 和诊断 | [源码前端](runtime/frontend.md) | [grammar.peg](../crates/frontend/src/grammar.peg)、[frontend](../crates/frontend/README.md)、[CLI](../crates/cli/README.md) | -| 操作码、代码验证、局部槽、函数定义与调用协议 | [字节码 VM](runtime/bytecode.md) | instruction.rs / code.rs / frame.rs / eval.rs;见 [文件表](../crates/core/src/README.md) | +| 操作码、代码验证、局部槽、函数定义与调用协议 | [字节码 VM](runtime/bytecode.md) | instruction.rs/code.rs 在 [objects/src](../crates/objects/src/README.md),frame.rs/eval.rs 在 [core/src](../crates/core/src/README.md) | | 基础信息保存在哪里、旧库如何迁移 | [宿主文件信息](operations/host-information.md) | [host](../crates/host/README.md) | | UTF-8 在日本地区是否适用,其他编码怎样进入 VM | [文本编码](runtime/text-encoding.md) | [core](../crates/core/README.md)、[host](../crates/host/README.md) | | 数据库连接给谁用,是否自动建表或保存基础信息 | [数据库特殊类](runtime/database-objects.md) | [storage](../crates/storage/README.md) | -| VM、对象与执行帧何时创建/释放,循环如何回收 | [生命周期](runtime/lifecycle.md) | heap.rs / eval.rs;见 [文件表](../crates/core/src/README.md) | +| VM、对象与执行帧何时创建/释放,循环如何回收 | [生命周期](runtime/lifecycle.md) | heap.rs 在 [objects/src](../crates/objects/src/README.md),eval.rs 在 [core/src](../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) | +| 检查覆盖什么,结果如何确认 | [验证](operations/validation.md) | [objects/tests](../crates/objects/tests/README.md)、[core/tests](../crates/core/tests/README.md)、[storage/tests](../crates/storage/tests/README.md) | | 改代码后文档写在哪里、怎样避免失效 | [维护规则](contributing/README.md) | 各目录 README 与本索引 | ## 多层目录 @@ -40,7 +40,7 @@ ```sh rg -n 'LC_CTYPE|UTF-8|DATABASE_URL' docs crates --glob '*.md' -rg -n 'EndorFrame|ReturnValue|collect_cycles' crates/core/src +rg -n 'EndorFrame|ReturnValue|collect_cycles' crates/core/src crates/objects/src rg --files -g README.md -g '!work/**' -g '!outputs/**' -g '!target/**' ``` diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 50808d7..d5ee66b 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -13,16 +13,19 @@ Endor 的目标是云原生语言;本仓库当前实现运行时、基础语 ```text CLI ────→ Core + Frontend + Host +Core ───→ Objects +Objects → 标准库 Frontend → peg Server ─→ Core + Host 未来数据库特殊类的宿主实现 ─→ Storage ``` -CLI、Server 依赖 Host 保存文件信息,不依赖 Storage;Core 与 Storage 也互不依赖。CLI 的 parse 命令通过 Frontend 的 EndorReader 读取文件/stdin,再解析 AST;Frontend 的外部依赖只有 peg,读取适配使用标准库。平台 URI 的存储与授权由宿主 provider 提供,Core 不依赖 Frontend。HTTP、Tokio、SQLx、数据库、网络和浏览器能力不进入零依赖的 core。包与文件定位见 [crates](../../crates/README.md)。 +CLI、Server 依赖 Host 保存文件信息,不依赖 Storage;Core 只依赖本地 Objects,Objects 无依赖且不反向依赖 Core;二者均不依赖 Storage。CLI 的 parse 命令通过 Frontend 的 EndorReader 读取文件/stdin,再解析 AST;Frontend 的外部依赖只有 peg,读取适配使用标准库。平台 URI 的存储与授权由宿主 provider 提供,Core 不依赖 Frontend。HTTP、Tokio、SQLx、数据库、网络和浏览器能力不进入 core 或 objects;两包均无第三方依赖。包与文件定位见 [crates](../../crates/README.md)。 | 组件 | 当前职责 | 全局契约 | | --- | --- | --- | -| Core | EndorObject、对象堆、Number、wordcode、执行帧、求值器 | [运行时](../runtime/README.md) | +| Core | Vm、执行帧、调用协议与取指求值循环;对象方法委托到 Objects | [字节码 VM](../runtime/bytecode.md) | +| Objects | 核心对象、私有堆、ObjectStore、Number、代码对象、指令数据和静态验证;无求值器 | [对象模型](../runtime/objects.md) | | Frontend | Endor 阅读器、手写 UTF-8 lexer、token PEG parser、基础函数/表达式 AST,不执行源码 | [源码前端](../runtime/frontend.md) | | Host | 基础信息的逐键 UTF-8 JSON 文件与旧库导入 | [宿主信息](../operations/host-information.md) | | Storage | 按显式配置建立 SQLite/PostgreSQL 连接池,不定义应用表 | [数据库特殊类](../runtime/database-objects.md) | @@ -30,6 +33,8 @@ CLI、Server 依赖 Host 保存文件信息,不依赖 Storage;Core 与 Stora | Server | HTTP 存活检查、只读 VM 信息及文件版本记录 | [Server](../../crates/server/README.md) | | Gitea/Docker | 双数据库适配验证、镜像构建、宿主与 PostgreSQL 资源部署 | [部署](../operations/deployment.md) | +core 保留既有对象类型的重导出与 Vm 方法,避免宿主导入方式随拆包改变。核心对象的定义和引用事务只在 objects 实现;ObjectRef 字段、Heap 和载荷替换不对包外开放。代码/指令属于可共享的执行数据,放在 objects;如何执行这些数据由 core 决定。定位见 [objects](../../crates/objects/README.md)。 + ## 当前契约 - Vm 从空堆启动。EndorObject 是实现层公共结构,不是语言根类;没有 Python type、继承或元类。 diff --git a/docs/contributing/README.md b/docs/contributing/README.md index 90ff5d8..6a0b0df 100644 --- a/docs/contributing/README.md +++ b/docs/contributing/README.md @@ -13,8 +13,8 @@ | 目录职责、直接子文件/子目录、常见修改位置 | 该目录 README.md | 父目录 README | | 各 crate 的依赖方向和系统边界 | [architecture/README](../architecture/README.md) | 根 README、docs 索引摘要 | | 解释执行、资源修订/依赖锁、解释器升级与缓存方向 | [execution-and-versions.md](../architecture/execution-and-versions.md) | 根 README、架构、reader/frontend/bytecode、运行与交接 | -| 对象头、句柄、引用协议与 GC 算法 | [objects.md](../runtime/objects.md) | core/src 文件表 | -| Number、String、Dict、Cell、函数环境和异常数据 | [builtins.md](../runtime/builtins.md) | core/src 与 tests 文件表 | +| 对象头、句柄、引用协议与 GC 算法 | [objects.md](../runtime/objects.md) | objects/src 文件表 | +| Number、String、Dict、Cell、函数环境和异常数据 | [builtins.md](../runtime/builtins.md) | objects/src、objects/tests 与 core/tests 文件表 | | lexer 前的输入适配、SourceUnit 与来源诊断 | [reader.md](../runtime/reader.md) | frontend 的 reader/source、CLI 与读取检查 | | token、PEG 规则、AST 与源码诊断 | [frontend.md](../runtime/frontend.md) | frontend 源码/测试/样例和 CLI | | 指令编码、栈效果、调用协议与执行限制 | [bytecode.md](../runtime/bytecode.md) | instruction/code/frame/eval 文件职责 | @@ -42,9 +42,9 @@ ## 当前实现边界 -Core 保持零外部依赖、不引入 Python type/元类、源码前端或宿主库。句柄构造保持私有;非法句柄检查放堆内部单元测试,不能为测试开放非法构造器。容器更新须经过 VM 的引用事务;call/execute 对象结果持有根,借用读取仍须由使用方按需 retain。 +Objects 保持零依赖;Core 仅依赖本地 Objects,负责 VM、执行帧和求值循环。两者不引入 Python type/元类、源码前端或宿主库。句柄构造保持私有;非法句柄检查放堆内部单元测试,不能为测试开放非法构造器。容器更新须经过 ObjectStore 的引用事务,Vm 方法只委托;call/execute 对象结果持有根,借用读取仍须由使用方按需 retain。 -Rust 实现依赖模块可见性、trait 和 rustfmt,不照搬 CPython C 符号命名规范。Core 公开导出集中在 core/src/lib.rs,Frontend 导出集中在 frontend/src/lib.rs,内部协作用 pub(crate) 或私有方法区分。当前基础源码解析契约在 frontend.md,尚无完整 Endor 源码风格规范或通用配置框架。 +Rust 实现依赖模块可见性、trait 和 rustfmt,不照搬 CPython C 符号命名规范。Objects 的对象定义与 ObjectStore 导出集中在 objects/src/lib.rs;Core 的 VM 导出与既有对象类型重导出集中在 core/src/lib.rs,Frontend 导出集中在 frontend/src/lib.rs,内部协作用 pub(crate) 或私有方法区分。当前基础源码解析契约在 frontend.md,尚无完整 Endor 源码风格规范或通用配置框架。 Endor 是由平台解释执行的语言,字节码生成是内部准备步骤。资源版本属于平台元数据,具体 URI 与持久化协议尚未定稿,不提前写进业务语法;首版暂不要求持久化字节码缓存,详见 [执行与版本模型](../architecture/execution-and-versions.md)。 diff --git a/docs/contributing/handoff.md b/docs/contributing/handoff.md index 988e375..fa568d6 100644 --- a/docs/contributing/handoff.md +++ b/docs/contributing/handoff.md @@ -4,7 +4,11 @@ ## 当前项目与命名 -仓库为 endorphin-aster-vm,归属 ProjectEndorphin。包与目录为 core、frontend、storage、host、cli、server;core 的 Rust 库名为 endor_core,命令为 cli/server。旧部署服务、数据库和路径保留既有标识以保护数据。 +仓库为 endorphin-aster-vm,归属 ProjectEndorphin。包与目录为 core、objects、frontend、storage、host、cli、server;core 的 Rust 库名为 endor_core,命令为 cli/server。旧部署服务、数据库和路径保留既有标识以保护数据。 + +## 当前包边界 + +Objects 零依赖,统一拥有核心对象、Number、代码对象/指令数据与静态验证、私有堆、ObjectStore 和共享 VmError;不依赖 Core。Core 仅依赖本地 Objects,保留 Vm、执行帧、取指求值循环;原有对象方法委托给 ObjectStore,并重导出既有对象类型。不要把对象定义或引用事务搬回 Core,也不开放私有句柄构造。源码导航见 [objects](../../crates/objects/README.md) 与 [core](../../crates/core/README.md)。 ## 已实现 diff --git a/docs/operations/deployment.md b/docs/operations/deployment.md index f2a5404..9eec568 100644 --- a/docs/operations/deployment.md +++ b/docs/operations/deployment.md @@ -38,7 +38,7 @@ docker compose -f docker-compose.postgres.yml exec aster cli bootstrap ## 生产目录与数据 -Cargo 包名为 core/frontend/storage/host/cli/server,core 的 Rust 库名为 endor_core,可执行文件为 cli/server。Compose 应用服务名 aster、数据库名、卷和生产路径是已有部署资源标识,运行命令为该服务中的 cli。应用不接收 DATABASE_URL、不依赖数据库健康才能启动;Compose 的整体 --wait 仍检查所预置 PostgreSQL 的就绪状态。 +Cargo 包名为 core/objects/frontend/storage/host/cli/server,core 的 Rust 库名为 endor_core,可执行文件为 cli/server。Compose 应用服务名 aster、数据库名、卷和生产路径是已有部署资源标识,运行命令为该服务中的 cli。应用不接收 DATABASE_URL、不依赖数据库健康才能启动;Compose 的整体 --wait 仍检查所预置 PostgreSQL 的就绪状态。 | 路径 | 用途 | | --- | --- | diff --git a/docs/operations/validation.md b/docs/operations/validation.md index e6557ab..546e3c0 100644 --- a/docs/operations/validation.md +++ b/docs/operations/validation.md @@ -24,14 +24,15 @@ ci-ces-control.sh 负责准备全新专用临时 PostgreSQL 和测试 URL;成 | 范围 | 当前检查内容 | 入口 | | --- | --- | --- | -| 堆内部边界 | 非法索引、槽位代数、代数耗尽、计数溢出原子性 | [core/src](../../crates/core/src/README.md) 的 heap.rs 私有单元测试 | +| 堆内部边界 | 非法索引、槽位代数、代数耗尽、计数溢出原子性 | [objects/src](../../crates/objects/src/README.md) 的 heap.rs 私有单元测试 | +| 独立对象存储 | 不依赖 VM 的稳定地址、跨存储拒绝、更新失败原子性、循环回收、函数校验与共享代码所有权 | [objects/tests](../../crates/objects/tests/README.md) 的 store.rs | | 对象运行时 | 地址稳定、引用/借用、属性、长链、循环、原生操作、VM 销毁 | [core/tests](../../crates/core/tests/README.md) 的 runtime.rs | | 基本对象 | Number 表示、字符串、Dict/Cell 引用与更新、函数环境、异常数据 | 同目录 builtins.rs | | 字节码 | 编解码、非法指令/控制流、跳转、调用、闭包、字节码创建函数/默认值/环境捕获、函数创建及后续失败清理、结果存活与限额 | 同目录 execution.rs | | Endor 阅读器 | 字符串、分块 UTF-8 流、文件、读取失败、URI provider、修订标签与来源诊断 | [frontend/tests](../../crates/frontend/tests/README.md) 的 reader.rs | | 源码前端 | Unicode 范围、数字原文、字符串与错误、优先级/结合性、函数/绑定、完整输入与错误位置、源码样例 | [frontend/tests](../../crates/frontend/tests/README.md) | | CLI 解析 | AST 输出、未执行源码、文件/stdin 来源和行列错误、EOF 读取与非法 UTF-8 退出 | [cli/tests](../../crates/cli/tests/README.md) | -| 公开示例 | 对象操作、手写指令执行与释放 | [core/src](../../crates/core/src/README.md) 的 lib.rs rustdoc | +| 公开示例 | 对象操作、手写指令执行与释放 | objects 与 core 的 src/lib.rs rustdoc,见 [objects/src](../../crates/objects/src/README.md)、[core/src](../../crates/core/src/README.md) | | 宿主文件信息 | UTF-8 值/路径、持久读取、同键替换、并发、坏记录、幂等迁移与保留新值 | [host/tests](../../crates/host/tests/README.md) | | 数据库连接 | 不自动建表、显式配置、隔离/共享、调用方数据重连、失败不降级、真实 PostgreSQL | [storage/tests](../../crates/storage/tests/README.md) | | 端到端宿主 | 文件 metadata 跨进程读写、日文值、Server 文件版本;忽略失效数据库变量且不创建 SQLite 文件、HTTP 与 SIGTERM | [scripts](../../scripts/README.md) 的 smoke.py | diff --git a/docs/runtime/README.md b/docs/runtime/README.md index 9fbbb50..1b70fe5 100644 --- a/docs/runtime/README.md +++ b/docs/runtime/README.md @@ -1,6 +1,6 @@ # 运行时设计 -本目录解释源码输入、前端及跨越 core 多个模块的运行时契约。文件与函数定位见 [core/src README](../../crates/core/src/README.md),测试定位见 [core/tests README](../../crates/core/tests/README.md)。 +本目录解释源码输入、前端及跨越 core/objects 的运行时契约。对象与存储定位见 [objects/src](../../crates/objects/src/README.md),VM 与执行帧见 [core/src](../../crates/core/src/README.md);检查分别见 [objects/tests](../../crates/objects/tests/README.md) 和 [core/tests](../../crates/core/tests/README.md)。 | 主题 | 内容 | | --- | --- | diff --git a/docs/runtime/builtins.md b/docs/runtime/builtins.md index ed6cfd8..eb293f0 100644 --- a/docs/runtime/builtins.md +++ b/docs/runtime/builtins.md @@ -1,6 +1,6 @@ # Endor 基本对象与统一 Number -这些对象属于虚拟机实现层的数据结构与宿主 API,不定义 Python type、语言根类或解析器;语言函数已接入 [小型字节码执行器](bytecode.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。 +这些对象统一定义在独立 [objects 包](../../crates/objects/README.md),属于实现层的数据结构与宿主 API,不定义 Python type、语言根类或解析器;语言函数已接入 [小型字节码执行器](bytecode.md)。它们通过 `ObjectPayload` 接入现有 `EndorObject`,共同使用对象头、稳定 Box、代际句柄、引用计数和循环回收。不是每种对象重新实现一套堆管理。 ## 对应关系 @@ -133,4 +133,4 @@ Endor 当前用 `EndorBaseExceptionObject` 保存公共数据,名称只是错 ## 验证入口 -对象行为检查在 [core/tests](../../crates/core/tests/README.md) 的 builtins.rs;执行行为在 execution.rs;验证方式见 [验证](../operations/validation.md)。文档描述当前契约,不代表某次 CI 已通过。CPython 284 项研究表保持为工作区外的研究材料,不纳入项目文档或 Git。 +独立存储行为在 [objects/tests](../../crates/objects/tests/README.md) 检查;既有 Vm 对象入口在 [core/tests](../../crates/core/tests/README.md) 的 builtins.rs 检查,执行行为在 execution.rs;验证方式见 [验证](../operations/validation.md)。文档描述当前契约,不代表某次 CI 已通过。CPython 284 项研究表保持为工作区外的研究材料,不纳入项目文档或 Git。 diff --git a/docs/runtime/bytecode.md b/docs/runtime/bytecode.md index 34e5bc6..b83191c 100644 --- a/docs/runtime/bytecode.md +++ b/docs/runtime/bytecode.md @@ -4,6 +4,8 @@ Endor 是解释型语言,字节码是内部执行表示;从 AST 生成字节码属于自动执行准备,不要求使用者发布编译产物。当前没有字节码持久化缓存或跨版本装载协议;现有不可变代码对象仍可在 VM 内共享复用。平台升级与源码修订的区别见 [执行与版本模型](../architecture/execution-and-versions.md)。 +指令、CodeLayout、EndorCode 与 EndorCodeObject 的定义、编解码和静态校验位于 objects 包;core 只拥有 VM、执行帧与求值循环。执行器通过公开的只读代码接口读取指令、槽名和共享代码,通过 ObjectStore 委托维护对象根;现有 endor_core 类型导入继续可用。 + ## 与 CPython 的对应 | 职责 | CPython 3.14 | Endor | @@ -164,6 +166,6 @@ fn main() -> Result<(), endor_core::VmError> { [execution.rs](../../crates/core/tests/execution.rs) 覆盖 wordcode、控制流校验、变量、循环、嵌套调用与默认参数、闭包、字节码函数定义/默认值/环境捕获、容器和返回值所有权、函数创建失败与后续错误清理及帧/指令限额;已有 builtins 测试已迁移共享代码对象和持有式调用返回值。lib.rs 另有可供远端 rustdoc 执行的例子。 -验证命令与结果边界统一见 [验证](../operations/validation.md)。限额与环境隔离见 [配置](../operations/configuration.md),文件职责见 [core 源码导航](../../crates/core/src/README.md)。 +验证命令与结果边界统一见 [验证](../operations/validation.md)。限额与环境隔离见 [配置](../operations/configuration.md),数据与校验见 [objects 源码导航](../../crates/objects/src/README.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)。 diff --git a/docs/runtime/database-objects.md b/docs/runtime/database-objects.md index ef97154..f775e87 100644 --- a/docs/runtime/database-objects.md +++ b/docs/runtime/database-objects.md @@ -4,7 +4,7 @@ ## 当前分层 -- Core 负责对象、堆和执行,不依赖 SQLx 或数据库。 +- Objects 负责核心对象与私有堆,Core 负责 VM 和执行;两者不依赖 SQLx 或数据库。 - Storage 仅暴露 DatabaseConfig、DatabaseConnection 和 DatabaseError。 - CLI/Server 不依赖 Storage,启动、bootstrap 和 health 不访问数据库;基础信息由独立 host 文件层处理。 - 特殊类以后由宿主实现按需取得数据库连接;尚未自动挂入 Vm 或对象工厂。 diff --git a/docs/runtime/frontend.md b/docs/runtime/frontend.md index 7aa19c2..497bd74 100644 --- a/docs/runtime/frontend.md +++ b/docs/runtime/frontend.md @@ -1,6 +1,6 @@ # 源码词法与 PEG 语法前端 -frontend 包提供 [Endor 阅读器](reader.md)、手写 Rust lexer 和 rust-peg 生成的 token parser。宿主通过 SourceUnit 调用带来源的词法/语法入口,原有 lex、parse、parse_expression 保留;CLI 提供 parse 文件/stdin 命令;结果是 AST,尚未实现 AST 到字节码生成、类型检查、名称解析或源码执行。Core 不依赖 frontend 或 peg,仍保持零外部依赖。 +frontend 包提供 [Endor 阅读器](reader.md)、手写 Rust lexer 和 rust-peg 生成的 token parser。宿主通过 SourceUnit 调用带来源的词法/语法入口,原有 lex、parse、parse_expression 保留;CLI 提供 parse 文件/stdin 命令;结果是 AST,尚未实现 AST 到字节码生成、类型检查、名称解析或源码执行。Core 仅依赖本地 Objects,不依赖 frontend 或 peg;Objects 保持零依赖。 首版以 Endorphin-Language 设计稿的基础词法、表达式与函数章节为起点,语法可以继续修改;设计稿中的未完成部分不作为阻塞前端实现的前置条件,也不被整体视作已经实现的语言能力。这里维护当前解析契约;源码位置见 [frontend](../../crates/frontend/README.md)。 diff --git a/docs/runtime/lifecycle.md b/docs/runtime/lifecycle.md index 0f6f9aa..ece481f 100644 --- a/docs/runtime/lifecycle.md +++ b/docs/runtime/lifecycle.md @@ -1,10 +1,11 @@ # VM、执行帧与对象的生命周期 -VM 由宿主持有,VM 持有对象堆,每次调用临时创建执行帧。一次执行结束不等于 VM 销毁;宿主可以保留全局字典并在同一 VM 中执行多次。 +VM 由宿主持有,VM 拥有 objects 包的 ObjectStore,由它持有私有对象堆;每次调用临时创建执行帧。一次执行结束不等于 VM 销毁;宿主可以保留全局字典并在同一 VM 中执行多次。 | 层级 | 创建 | 结束 | | --- | --- | --- | | Vm | 宿主调用 Vm::new,创建空堆并取得独立堆身份 | Rust 所有者销毁;使用 Arc 时等最后一份 Arc 被释放 | +| ObjectStore | 独立调用 ObjectStore::new,或由 Vm::new 内部创建;分配独立堆身份 | 所有者销毁时释放整个堆;没有共享可变堆入口 | | EndorFrame | 每次语言函数调用建立,加入本次调用的帧向量 | 函数返回或 Result::Err 时,从内到外清理 | | EndorObject | new_*/allocate 分配,返回一份外部强引用 | 总强引用归零、显式循环回收,或整体 VM 销毁 | @@ -34,4 +35,4 @@ Vm 没有独立后台线程、暂停状态或显式 shutdown 状态机。Rust Server 的 Tokio 运行时、监听器、信号处理和 Host 文件信息由宿主管理;优雅退出见 [运行](../operations/README.md)。当前宿主不持有数据库连接。数据库能力只为未来的特殊类按需提供,异步连接关闭与原生载荷生命周期仍需单独接入,见 [数据库特殊类](database-objects.md)。数据库不自动保存或恢复 VM 对象。 -源码定位:[core/src](../../crates/core/src/README.md) 中的 vm.rs、heap.rs、frame.rs、eval.rs。返回 [运行时索引](README.md)。 +源码定位:[objects/src](../../crates/objects/src/README.md) 中的 store.rs/heap.rs 负责对象生命周期,[core/src](../../crates/core/src/README.md) 中的 vm.rs/frame.rs/eval.rs 负责执行状态和帧根。返回 [运行时索引](README.md)。 diff --git a/docs/runtime/objects.md b/docs/runtime/objects.md index adf9df9..1f274ec 100644 --- a/docs/runtime/objects.md +++ b/docs/runtime/objects.md @@ -2,9 +2,11 @@ 当前实现的是 Endor 的 VM 运行时基础对象,角色类似 CPython 的公共对象结构。这里没有语言根类、元类、继承或 Python type 系统。标量仍然可以直接保存在 Value 中,不要求一切装箱。 -基本对象及统一数字表示已在此基础上加入,详见 [基本对象与 Number](builtins.md)。`builtins.rs` 定义字符串、字典、Cell、函数和异常数据;`number.rs` 定义无损数字表示;`vm_builtins.rs` 提供容器操作及关联对象校验。 +基本对象及统一数字表示已在此基础上加入,详见 [基本对象与 Number](builtins.md)。`builtins.rs` 定义字符串、字典、Cell、函数和异常数据;`number.rs` 定义无损数字表示;`store_builtins.rs` 提供容器操作及关联对象校验。 -代码文件及职责见 [core 源码导航](../../crates/core/src/README.md),对象及执行的生命周期见 [生命周期](lifecycle.md)。 +这些定义全部位于独立 objects 包。ObjectStore 拥有私有 Heap,负责对象操作与引用事务;core 的 Vm 拥有一个 ObjectStore,仅委托对象操作并管理执行状态。对象存储无需 VM 即可使用,仍不提供句柄字段或裸指针构造;不同存储的句柄互不通用。VmError 也在 objects 定义以避免反向依赖,endor_core 兼容重导出现有类型和保留 Vm 方法。 + +代码文件及职责见 [objects 源码导航](../../crates/objects/src/README.md),执行器见 [core 源码导航](../../crates/core/src/README.md),对象及执行的生命周期见 [生命周期](lifecycle.md)。 ## EndorObject 保存什么