refactor: extract core objects into a standalone package
Verify databases and deploy Aster VM / verify-and-deploy (push) Successful in 7m34s

This commit is contained in:
2026-10-19 13:33:24 +08:00
parent 78fc859825
commit f77b8370ea
40 changed files with 631 additions and 202 deletions
Generated
+7
View File
@@ -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"
+2 -1
View File
@@ -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" }
+4 -3
View File
@@ -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 工作流 |
+3 -2
View File
@@ -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 执行字节码目前仍是两段独立能力,后续需要连接内部字节码生成;版本锁、平台资源后端和持久化字节码缓存均未实现。
+3
View File
@@ -9,5 +9,8 @@ license.workspace = true
[lib]
name = "endor_core"
[dependencies]
objects.workspace = true
[lints]
workspace = true
+8 -6
View File
@@ -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)。
+12 -20
View File
@@ -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)。
+12 -8
View File
@@ -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<ObjectRef, VmError> {
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)));
}
+9 -22
View File
@@ -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;
+106 -106
View File
@@ -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<ExecutionLocation>,
}
impl Vm {
pub fn new() -> Result<Self, VmError> {
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<ObjectRef, VmError> {
self.validate_builtin(&payload)?;
self.heap.allocate(Box::new(payload))
self.objects.allocate(payload)
}
pub fn new_object(&mut self) -> Result<ObjectRef, VmError> {
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<usize, VmError> {
Ok(self.heap.get(reference)?.header().ref_count())
self.objects.ref_count(reference)
}
pub fn is_same(&self, left: ObjectRef, right: ObjectRef) -> Result<bool, VmError> {
self.heap.get(left)?;
self.heap.get(right)?;
Ok(left == right)
self.objects.is_same(left, right)
}
pub fn repr(&self, reference: ObjectRef) -> Result<String, VmError> {
self.heap.get(reference)?.payload().repr(reference)
self.objects.repr(reference)
}
pub fn str(&self, reference: ObjectRef) -> Result<String, VmError> {
self.heap.get(reference)?.payload().str(reference)
self.objects.str(reference)
}
pub fn is_true(&self, reference: ObjectRef) -> Result<bool, VmError> {
self.heap.get(reference)?.payload().is_true()
self.objects.is_true(reference)
}
pub fn hash(&self, reference: ObjectRef) -> Result<u64, VmError> {
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<bool, VmError> {
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<Value, VmError> {
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<String>,
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<T: ObjectPayload>(
&self,
reference: ObjectRef,
expected: &'static str,
) -> Result<&T, VmError> {
self.objects.payload::<T>(reference, expected)
}
pub fn new_string(&mut self, text: impl Into<String>) -> Result<ObjectRef, VmError> {
self.objects.new_string(text)
}
pub fn new_code(&mut self, code: EndorCode) -> Result<ObjectRef, VmError> {
self.objects.new_code(code)
}
pub fn new_function(
&mut self,
name: impl Into<String>,
code: ObjectRef,
globals: ObjectRef,
defaults: Vec<Value>,
closure: Vec<ObjectRef>,
) -> Result<ObjectRef, VmError> {
self.objects
.new_function(name, code, globals, defaults, closure)
}
pub fn new_number(&mut self, number: EndorNumber) -> Result<ObjectRef, VmError> {
self.objects.new_number(number)
}
pub fn new_dict(&mut self) -> Result<ObjectRef, VmError> {
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<Option<Value>, 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<bool, VmError> {
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<Value>) -> Result<ObjectRef, VmError> {
self.objects.new_cell(value)
}
/// Reads a borrowed value; an empty cell is not Value::Nil.
pub fn cell_get(&self, cell: ObjectRef) -> Result<Value, VmError> {
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)
}
}
+1 -1
View File
@@ -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)。
+1 -1
View File
@@ -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)。
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "objects"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[lints]
workspace = true
+15
View File
@@ -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)。
+20
View File
@@ -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)。
@@ -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<DecodedInstruction, VmError> {
/// Look up a logical instruction by its code-unit offset.
pub fn instruction_at(&self, offset: usize) -> Result<DecodedInstruction, VmError> {
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<EndorCode> {
/// 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<EndorCode> {
Arc::clone(&self.code)
}
}
+66
View File
@@ -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 {}
@@ -127,6 +127,16 @@ impl Instruction {
)
}
/// Decode a comparison operand; None denotes an invalid operand.
pub fn compare_operation(argument: u32) -> Option<CompareOp> {
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<Vec<u8>, 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<usize, VmError> {
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<Vec<DecodedInstruction>, VmError> {
if bytes.is_empty() || !bytes.len().is_multiple_of(2) {
return Err(invalid(
+41
View File
@@ -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;
+125
View File
@@ -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<Self, VmError> {
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<ObjectRef, VmError> {
self.validate_builtin(&payload)?;
self.heap.allocate(Box::new(payload))
}
pub fn new_object(&mut self) -> Result<ObjectRef, VmError> {
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<usize, VmError> {
Ok(self.heap.get(reference)?.header().ref_count())
}
pub fn is_same(&self, left: ObjectRef, right: ObjectRef) -> Result<bool, VmError> {
self.heap.get(left)?;
self.heap.get(right)?;
Ok(left == right)
}
pub fn repr(&self, reference: ObjectRef) -> Result<String, VmError> {
self.heap.get(reference)?.payload().repr(reference)
}
pub fn str(&self, reference: ObjectRef) -> Result<String, VmError> {
self.heap.get(reference)?.payload().str(reference)
}
pub fn is_true(&self, reference: ObjectRef) -> Result<bool, VmError> {
self.heap.get(reference)?.payload().is_true()
}
pub fn hash(&self, reference: ObjectRef) -> Result<u64, VmError> {
self.heap.get(reference)?.payload().hash(reference)
}
pub fn compare(
&self,
left: ObjectRef,
right: ObjectRef,
operation: CompareOp,
) -> Result<bool, VmError> {
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<Value, VmError> {
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<String>,
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()
}
}
@@ -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::<EndorFunctionObject>() {
let code = self
+9
View File
@@ -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)。
+96
View File
@@ -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::<EndorFunctionObject>(first, "function")
.unwrap()
.code();
let second_code = store
.payload::<EndorFunctionObject>(second, "function")
.unwrap()
.code();
let first_executable = store
.payload::<EndorCodeObject>(first_code, "code")
.unwrap()
.shared_code();
let second_executable = store
.payload::<EndorCodeObject>(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);
}
+6 -6
View File
@@ -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/**'
```
+7 -2
View File
@@ -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、继承或元类。
+4 -4
View File
@@ -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)。
+5 -1
View File
@@ -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)。
## 已实现
+1 -1
View File
@@ -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 的就绪状态。
| 路径 | 用途 |
| --- | --- |
+3 -2
View File
@@ -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 |
+1 -1
View File
@@ -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)。
| 主题 | 内容 |
| --- | --- |
+2 -2
View File
@@ -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。
+3 -1
View File
@@ -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)。
+1 -1
View File
@@ -4,7 +4,7 @@
## 当前分层
- Core 负责对象、堆和执行,不依赖 SQLx 或数据库。
- Objects 负责核心对象与私有堆,Core 负责 VM 和执行;两者不依赖 SQLx 或数据库。
- Storage 仅暴露 DatabaseConfig、DatabaseConnection 和 DatabaseError。
- CLI/Server 不依赖 Storage,启动、bootstrap 和 health 不访问数据库;基础信息由独立 host 文件层处理。
- 特殊类以后由宿主实现按需取得数据库连接;尚未自动挂入 Vm 或对象工厂。
+1 -1
View File
@@ -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)。
+3 -2
View File
@@ -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)。
+4 -2
View File
@@ -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 保存什么