Files
endorphin-aster-vm/crates/objects/src/builtins.rs
T

474 lines
22 KiB
Rust

//! 内置字符串、字典、闭包单元、源函数和异常实例的 Rust 载荷定义。
//! ObjectStore 的 store_builtins 创建/更新这些载荷;core VM 调用和容器操作通过同一对象存储访问它们。
//! 这里的构造器只组织 Rust 数据,不分配托管身份、不验证跨堆引用,也不因 Clone 自动 retain ObjectRef。
//! 分配/替换时由 Heap 按 trace 列出的每一条拥有边验证并计数;trace 必须报告重复引用,不能自行去重。
//! 载荷 trace 在分配/替换时生成边快照;循环回收读取 EndorObject 保存的快照,而非依赖 Clone 追踪引用。
//! kind_name 只是诊断标签;语言名义类型由对象头/类型注册表另行管理。
use std::any::Any;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use crate::{CompareOp, ObjectPayload, ObjectRef, Value, VmError};
#[derive(Debug, Clone, PartialEq, Eq)]
/// 堆内不可变字符串载荷;宿主可通过 ObjectStore 分配,字符索引按 Unicode 标量而非字节。
pub struct EndorStringObject {
// 自有 UTF-8 文本,不包含托管对象引用;与即时 Value::Text 是不同存储表示。
text: String,
}
impl EndorStringObject {
/// ObjectStore::new_string 调用,接收可转为 String 的文本并保存;此步尚未进入托管堆。
pub fn new(text: impl Into<String>) -> Self {
Self { text: text.into() }
}
/// 字典 key_equal 和宿主查询借用 UTF-8 内容;借用不创建托管根,也不修改字符串。
pub fn as_str(&self) -> &str {
&self.text
}
/// 宿主/内置对象测试查询 Unicode 标量数量;不是 UTF-8 字节数或用户可见字素数。
pub fn char_count(&self) -> usize {
self.text.chars().count()
}
/// 宿主/内置对象测试按零基 Unicode 标量索引查询;从头线性扫描,越界返回 None。
pub fn char_at(&self, index: usize) -> Option<char> {
self.text.chars().nth(index)
}
}
impl ObjectPayload for EndorStringObject {
/// ObjectStore::assign 调用,声明默认 assign_from 不会就地修改载荷;只读阶段可跳过写尝试,允许调用方重绑局部引用。
fn reference_assignment_only(&self) -> bool {
true
}
/// ObjectStore::payload、内置验证和比较通过此入口向下转换实际 Rust 载荷;不做语言类型判断。
fn as_any(&self) -> &dyn Any {
self
}
/// ObjectStore 的错误/观察信息调用,返回静态诊断标签,不声明名义类型或继承。
fn kind_name(&self) -> &'static str {
"string"
}
/// ObjectStore::repr 调用,用 Rust 字符串 Debug 生成带引号/转义的文本;不展开托管对象。
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(format!("{:?}", self.text))
}
/// ObjectStore::str 调用,返回原文的独立 String 副本,不带 repr 引号。
fn str(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(self.text.clone())
}
/// VM 条件或宿主真值查询调用;空字符串为假,其余为真,不修剪空白。
fn is_true(&self) -> Result<bool, VmError> {
Ok(!self.text.is_empty())
}
/// ObjectStore::hash/字典键校验调用,按完整文本求哈希;不是稳定跨版本的持久化摘要。
fn hash(&self, _identity: ObjectRef) -> Result<u64, VmError> {
// 每次从空哈希状态混入同样的文本内容,使相等字符串提供兼容哈希;这里不缓存哈希值。
let mut hasher = DefaultHasher::new();
self.text.hash(&mut hasher);
Ok(hasher.finish())
}
/// ObjectStore::compare 调用,仅与同种字符串载荷按 Rust 字符串词典序比较;不按 locale 或字素排序。
fn compare(&self, other: &dyn ObjectPayload, op: CompareOp) -> Result<Option<bool>, VmError> {
// 其他载荷返回“不支持”而不是 false,ObjectStore 才能继续尝试反向比较或其错误处理。
let Some(other) = other.as_any().downcast_ref::<Self>() else {
return Ok(None);
};
// 比较种类由调用方提供;六种操作都比较文本内容,ObjectRef 身份不参与结果。
Ok(Some(match op {
CompareOp::Eq => self.text == other.text,
CompareOp::Ne => self.text != other.text,
CompareOp::Lt => self.text < other.text,
CompareOp::Le => self.text <= other.text,
CompareOp::Gt => self.text > other.text,
CompareOp::Ge => self.text >= other.text,
}))
}
}
/// Insertion-ordered entries. Mutation is only exposed through Vm so every
/// key/value reference is validated, retained, traced, and released correctly.
/// Linear lookup is intentional for the initial representation layer.
#[derive(Debug, Clone, Default)]
/// 按插入顺序拥有键值的字典载荷;查询/写入语义由 store_builtins 统一实施。
pub struct EndorDictObject {
// 按首次插入顺序保存键值对;只允许存储层整体替换载荷,不能绕过 Heap 的拥有边和只读写屏障。
pub(crate) entries: Vec<(Value, Value)>,
}
impl EndorDictObject {
/// ObjectStore::new_dict/dict_clear 创建空的插入顺序容器,再交 Heap 管理实际身份与拥有边。
pub fn new() -> Self {
Self::default()
}
/// 容器 repr 和宿主查询获取键值对数量,不是托管引用数;同一对象可在多个项中重复出现。
pub fn len(&self) -> usize {
self.entries.len()
}
/// 字典真值查询复用此检查;只判断 entries 是否为空,不调用键或值的语言代码。
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
/// ObjectStore::dict_items 借出按插入顺序的切片;键值中的 ObjectRef 仍属借用,Clone 不会增根。
pub fn entries(&self) -> &[(Value, Value)] {
&self.entries
}
}
impl ObjectPayload for EndorDictObject {
/// ObjectStore::assign 调用,声明默认 assign_from 不会就地修改载荷;只读阶段可跳过写尝试,允许调用方重绑局部引用。
fn reference_assignment_only(&self) -> bool {
true
}
/// ObjectStore::payload、内置验证和比较通过此入口向下转换实际 Rust 载荷;不做语言类型判断。
fn as_any(&self) -> &dyn Any {
self
}
/// ObjectStore 的错误/观察信息调用,返回静态诊断标签,不声明名义类型或继承。
fn kind_name(&self) -> &'static str {
"dict"
}
/// Heap 分配/替换时收集拥有边,循环回收再使用保存的边快照;键和值中的每个 ObjectRef 都须报告,包括重复引用。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
// 键和值分别计一条边;同一对象同时作键和值时必须报告两次。
for (key, value) in &self.entries {
trace_value(key, visit);
trace_value(value, visit);
}
}
/// ObjectStore::repr 查询仅显示容器长度,不递归格式化键值,避免循环图导致无限展开。
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(format!("<dict len={}>", self.len()))
}
/// ObjectStore::is_true 查询字典是否非空;内容可变,所以每次取当前长度。
fn is_true(&self) -> Result<bool, VmError> {
Ok(!self.is_empty())
}
/// 字典键校验/ObjectStore::hash 调用时明确拒绝;可变容器没有内容稳定的键哈希契约。
fn hash(&self, _identity: ObjectRef) -> Result<u64, VmError> {
Err(VmError::UnsupportedOperation("dict hash"))
}
}
/// An empty cell differs from a cell containing Value::Nil.
#[derive(Debug, Clone, Default)]
/// 闭包共享单元载荷;VM 的局部捕获和 free-variable 操作通过 ObjectStore 更新其内容。
pub struct EndorCellObject {
// None 是未初始化单元;Some(Nil) 是合法已初始化值,二者在 cell_get/字节码闭包读取时必须区分。
pub(crate) value: Option<Value>,
}
impl EndorCellObject {
/// ObjectStore::new_cell/cell_set 创建单元载荷;Option 区分未初始化与已保存 Nil,Heap 才负责引用计数。
pub fn new(value: Option<Value>) -> Self {
Self { value }
}
/// ObjectStore::cell_get 借出已有值;空单元保持 None,由调用方转为 EmptyCell 错误。
pub fn value(&self) -> Option<&Value> {
self.value.as_ref()
}
}
impl ObjectPayload for EndorCellObject {
/// ObjectStore::assign 调用,声明默认 assign_from 不会就地修改载荷;只读阶段可跳过写尝试,允许调用方重绑局部引用。
fn reference_assignment_only(&self) -> bool {
true
}
/// ObjectStore::payload、内置验证和比较通过此入口向下转换实际 Rust 载荷;不做语言类型判断。
fn as_any(&self) -> &dyn Any {
self
}
/// ObjectStore 的错误/观察信息调用,返回静态诊断标签,不声明名义类型或继承。
fn kind_name(&self) -> &'static str {
"cell"
}
/// Heap 读取已初始化单元的拥有边;空单元无边,Some(Nil) 也不含对象边。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
// 未初始化单元不持有 Value;已初始化即时值由 trace_value 识别为没有托管边。
if let Some(value) = &self.value {
trace_value(value, visit);
}
}
/// 宿主 repr 只区分 occupied/empty,不把单元值展开,避免递归闭包或单元环。
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
// 只看 Option 是否占用,Some(Nil) 与其他已初始化值一样显示 occupied。
Ok(if self.value.is_some() {
"<cell occupied>"
} else {
"<cell empty>"
}
.into())
}
/// 字典键校验拒绝把可变 Cell 用作可哈希键,不退化为一个看似内容稳定的哈希值。
fn hash(&self, _identity: ObjectRef) -> Result<u64, VmError> {
Err(VmError::UnsupportedOperation("cell hash"))
}
}
#[derive(Debug, Clone)]
/// 源函数的代码与定义环境载荷;core 根据这些借用访问器建立真实 VM 帧,不由 payload.call 递归解释。
pub struct EndorFunctionObject {
// 源函数显示名,供 repr/诊断使用,不是依此重新查找全局函数的键。
name: String,
// 已加载 EndorCodeObject 的强边;执行器从中读取参数、局部槽和指令。
code: ObjectRef,
// new_function 捕获创建时 globals.__builtins__ 的字典身份;None 表示没有该回退环境。
builtins: Option<ObjectRef>,
// 调用期间按名称读取的全局字典强边,不复制函数定义时的所有全局值。
globals: ObjectRef,
// 最后若干位置参数的缺省值;对象值由 trace 声明拥有,执行器按缺参位置补齐。
defaults: Vec<Value>,
// 与 code.free_variables 顺序一致的 Cell 强边;多个函数可共享同一单元以观察后续写入。
closure: Vec<ObjectRef>,
}
impl EndorFunctionObject {
/// 仅 ObjectStore::new_function 创建源函数载荷;代码/字典/闭包形状由存储层验证,字段强边由 trace 声明。
pub(crate) fn new(
name: String,
code: ObjectRef,
globals: ObjectRef,
builtins: Option<ObjectRef>,
defaults: Vec<Value>,
closure: Vec<ObjectRef>,
) -> Self {
Self {
name,
code,
globals,
builtins,
defaults,
closure,
}
}
/// 执行器和诊断借用源函数显示名;不按这个字符串重新绑定其他同名函数。
pub fn name(&self) -> &str {
&self.name
}
/// core 执行器获取代码身份以建立帧;返回 ObjectRef 是借用句柄,不新增外部根。
pub fn code(&self) -> ObjectRef {
self.code
}
/// core 执行器/注解解析读取创建时捕获的 builtin 回退字典;不在每次调用重取 globals.__builtins__。
pub fn builtins(&self) -> Option<ObjectRef> {
self.builtins
}
/// core 执行器/效果分析读取函数自己的全局环境身份;环境内容仍可在后续输入改变。
pub fn globals(&self) -> ObjectRef {
self.globals
}
/// core 参数准备借用尾部缺省值;省略参数时使用这些实际 Value,不重新执行缺省值表达式。
pub fn defaults(&self) -> &[Value] {
&self.defaults
}
/// core 建帧按 free-variable 顺序借用 Cell 身份,使兄弟闭包共享实际单元而非复制当前值。
pub fn closure(&self) -> &[ObjectRef] {
&self.closure
}
}
impl ObjectPayload for EndorFunctionObject {
/// VM/契约检查调用,明确源函数属于 Function;与可调用但不属 Function 的 Type 对象区分。
fn is_function(&self) -> bool {
true
}
/// ObjectStore::assign 调用,声明默认 assign_from 不会就地修改载荷;只读阶段可跳过写尝试,允许调用方重绑局部引用。
fn reference_assignment_only(&self) -> bool {
true
}
/// ObjectStore::payload、内置验证和比较通过此入口向下转换实际 Rust 载荷;不做语言类型判断。
fn as_any(&self) -> &dyn Any {
self
}
/// ObjectStore 的错误/观察信息调用,返回静态诊断标签,不声明名义类型或继承。
fn kind_name(&self) -> &'static str {
"function"
}
/// ObjectStore 设置普通实例字段前查询;允许对象头保存属性,不表示载荷私有字段可被任意直接修改。
fn allows_attributes(&self) -> bool {
true
}
/// Heap 按函数拥有的代码、全局、可选 builtins、Cell 和默认值逐条计数/追踪;同一身份重复出现也要报告。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
visit(self.code);
visit(self.globals);
// 可选回退环境存在时同样拥有;它与 globals 若为同一对象也不能省略这一次访问。
if let Some(builtins) = self.builtins {
visit(builtins);
}
// Cell 本身是强边;当前 Cell 的内容由 Cell.trace 另行声明,不在这里跳过单元直接追踪值。
for reference in &self.closure {
visit(*reference);
}
// 缺省参数中的即时值不含对象边,Object 变体则按每个位置报告。
for value in &self.defaults {
trace_value(value, visit);
}
}
/// ObjectStore::repr 返回函数显示名,不执行函数、不展开其 globals/closure。
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(format!("<function {}>", self.name))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
/// 异常构建器接收的纯数据位置记录,没有对代码对象或文件的托管拥有边。
pub struct EndorTracebackEntry {
// 宿主提供的函数显示名和文件来源;这里不保留可执行函数或文件资源句柄。
pub function: String,
pub file: String,
// 调用方提供的行位置,本记录不做位置换算或非零验证。
pub line: usize,
}
/// Common exception instance data, not an exception class or inheritance tree.
#[derive(Debug, Clone)]
/// 宿主可分配的公共异常实例数据;本文件不实现 raise/catch 控制流或异常类层次。
pub struct EndorBaseExceptionObject {
// 异常显示名称,不建立同名异常类或继承关系。
name: String,
// 参数可包含即时值或对象;对象参数通过 trace 成为真实托管拥有边。
args: Vec<Value>,
// 显式原因与隐式上下文分别保存;存储层要求它们指向异常载荷。
cause: Option<ObjectRef>,
context: Option<ObjectRef>,
// 仅保存上下文抑制标记;设置此值不会解除 context 的拥有边。
suppress_context: bool,
// 说明和 traceback 是自有文本/位置记录,本载荷不据此读取文件或执行回调。
notes: Vec<String>,
traceback: Vec<EndorTracebackEntry>,
}
impl EndorBaseExceptionObject {
/// 宿主/内置对象测试构造异常实例数据;初始没有 cause/context/notes/traceback,尚未分配或抛出异常。
pub fn new(name: impl Into<String>, args: Vec<Value>) -> Self {
Self {
name: name.into(),
args,
cause: None,
context: None,
suppress_context: false,
notes: Vec::new(),
traceback: Vec::new(),
}
}
/// 分配前的构建器记录显式原因并打开上下文抑制;不在此验证句柄或释放已有 context。
pub fn with_cause(mut self, cause: ObjectRef) -> Self {
self.cause = Some(cause);
self.suppress_context = true;
self
}
/// 分配前的构建器记录上下文强边;不自动改变 suppress_context,也不执行上下文异常。
pub fn with_context(mut self, context: ObjectRef) -> Self {
self.context = Some(context);
self
}
/// 宿主构建器覆盖抑制标记,仍保留 context 引用,显示标记与 GC 可达关系分开。
pub fn suppress_context(mut self, suppress: bool) -> Self {
self.suppress_context = suppress;
self
}
/// 宿主构建器追加自有说明文本,保留调用顺序,不做换行/长度校验或输出。
pub fn with_note(mut self, note: impl Into<String>) -> Self {
self.notes.push(note.into());
self
}
/// 宿主构建器按调用顺序追加位置记录;本方法不遍历 VM 帧,也不验证记录的来源位置。
pub fn with_traceback(mut self, entry: EndorTracebackEntry) -> Self {
self.traceback.push(entry);
self
}
/// 异常查询和格式化借用显示名;名称不是语言 Type 身份。
pub fn name(&self) -> &str {
&self.name
}
/// 宿主查询借用实际参数切片;其中对象值由异常载荷拥有,复制句柄不自动 retain。
pub fn args(&self) -> &[Value] {
&self.args
}
/// ObjectStore::validate_builtin/trace 读取可选显式原因;返回借用身份,不创建新拥有根。
pub fn cause(&self) -> Option<ObjectRef> {
self.cause
}
/// ObjectStore::validate_builtin/trace 读取可选上下文;即使已抑制显示,此强边仍参与追踪。
pub fn context(&self) -> Option<ObjectRef> {
self.context
}
/// 宿主显示/测试读取标志;本载荷的 repr/str 不据此展开异常链。
pub fn context_suppressed(&self) -> bool {
self.suppress_context
}
/// 宿主查询按追加顺序借用说明;repr/str 当前不会自动把 notes 拼入输出。
pub fn notes(&self) -> &[String] {
&self.notes
}
/// 宿主查询借用显式位置记录;不因读取此切片而捕获当前 VM 调用栈。
pub fn traceback(&self) -> &[EndorTracebackEntry] {
&self.traceback
}
}
impl ObjectPayload for EndorBaseExceptionObject {
/// ObjectStore::assign 调用,声明默认 assign_from 不会就地修改载荷;只读阶段可跳过写尝试,允许调用方重绑局部引用。
fn reference_assignment_only(&self) -> bool {
true
}
/// ObjectStore::payload、内置验证和比较通过此入口向下转换实际 Rust 载荷;不做语言类型判断。
fn as_any(&self) -> &dyn Any {
self
}
/// ObjectStore 的错误/观察信息调用,返回静态诊断标签,不声明名义类型或继承。
fn kind_name(&self) -> &'static str {
"exception"
}
/// ObjectStore 设置普通实例字段前查询;允许对象头保存属性,不表示载荷私有字段可被任意直接修改。
fn allows_attributes(&self) -> bool {
true
}
/// Heap 追踪参数对象与两种异常链边;suppress_context 仅是显示数据,不得让 GC 漏掉上下文。
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
// 参数按原顺序逐个报告;String/Number 等即时值由统一助手跳过。
for value in &self.args {
trace_value(value, visit);
}
// flatten 仅跳过 None,不去重;两种链指向同一异常时仍是两条真实拥有边。
for reference in [self.cause, self.context].into_iter().flatten() {
visit(reference);
}
}
/// ObjectStore::repr 组合名称和参数的 Rust Debug 文本;不递归调用参数对象的语言 repr。
fn repr(&self, _identity: ObjectRef) -> Result<String, VmError> {
Ok(format!(
"{}({})",
self.name,
self.args
.iter()
.map(|arg| format!("{arg:?}"))
.collect::<Vec<_>>()
.join(", ")
))
}
/// ObjectStore::str 按参数形状生成消息;不自动拼接 cause/context、notes 或 traceback。
fn str(&self, _identity: ObjectRef) -> Result<String, VmError> {
// 零参数为空消息;单 Text/Number 用其内容;其他形状(含单 Object)保留 Rust Debug 形式。
Ok(match self.args.as_slice() {
[] => String::new(),
[Value::Text(text)] => text.clone(),
[Value::Number(number)] => number.to_string(),
args => format!("{args:?}"),
})
}
}
/// 内置载荷及其他对象的 trace 共用助手:只把 Value::Object 声明为拥有边,不 retain/release 或递归遍历子图。
pub(crate) fn trace_value(value: &Value, visit: &mut dyn FnMut(ObjectRef)) {
// Heap 的外层遍历负责后续子图;这里只有一层变体检查,即时值无需 GC 边。
if let Value::Object(reference) = value {
visit(*reference);
}
}