From a7078adc544cb81511fe73987775e9b49b5f5ea9 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Thu, 12 Nov 2026 21:37:03 +0800 Subject: [PATCH] docs: explain builtin payload ownership and VM consumers --- crates/objects/src/builtins.rs | 108 +++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/crates/objects/src/builtins.rs b/crates/objects/src/builtins.rs index d7323d8..2122185 100644 --- a/crates/objects/src/builtins.rs +++ b/crates/objects/src/builtins.rs @@ -1,3 +1,10 @@ +//! 内置字符串、字典、闭包单元、源函数和异常实例的 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}; @@ -5,56 +12,73 @@ 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) -> 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 { 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 { Ok(format!("{:?}", self.text)) } + /// ObjectStore::str 调用,返回原文的独立 String 副本,不带 repr 引号。 fn str(&self, _identity: ObjectRef) -> Result { Ok(self.text.clone()) } + /// VM 条件或宿主真值查询调用;空字符串为假,其余为真,不修剪空白。 fn is_true(&self) -> Result { Ok(!self.text.is_empty()) } + /// ObjectStore::hash/字典键校验调用,按完整文本求哈希;不是稳定跨版本的持久化摘要。 fn hash(&self, _identity: ObjectRef) -> Result { + // 每次从空哈希状态混入同样的文本内容,使相等字符串提供兼容哈希;这里不缓存哈希值。 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, VmError> { + // 其他载荷返回“不支持”而不是 false,ObjectStore 才能继续尝试反向比较或其错误处理。 let Some(other) = other.as_any().downcast_ref::() else { return Ok(None); }; + // 比较种类由调用方提供;六种操作都比较文本内容,ObjectRef 身份不参与结果。 Ok(Some(match op { CompareOp::Eq => self.text == other.text, CompareOp::Ne => self.text != other.text, @@ -70,47 +94,61 @@ impl ObjectPayload for EndorStringObject { /// 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 { Ok(format!("", self.len())) } + /// ObjectStore::is_true 查询字典是否非空;内容可变,所以每次取当前长度。 fn is_true(&self) -> Result { Ok(!self.is_empty()) } + /// 字典键校验/ObjectStore::hash 调用时明确拒绝;可变容器没有内容稳定的键哈希契约。 fn hash(&self, _identity: ObjectRef) -> Result { Err(VmError::UnsupportedOperation("dict hash")) } @@ -118,35 +156,46 @@ impl ObjectPayload for EndorDictObject { /// 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, } impl EndorCellObject { + /// ObjectStore::new_cell/cell_set 创建单元载荷;Option 区分未初始化与已保存 Nil,Heap 才负责引用计数。 pub fn new(value: Option) -> 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 { + // 只看 Option 是否占用,Some(Nil) 与其他已初始化值一样显示 occupied。 Ok(if self.value.is_some() { "" } else { @@ -154,22 +203,31 @@ impl ObjectPayload for EndorCellObject { } .into()) } + /// 字典键校验拒绝把可变 Cell 用作可哈希键,不退化为一个看似内容稳定的哈希值。 fn hash(&self, _identity: ObjectRef) -> Result { 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, + // 调用期间按名称读取的全局字典强边,不复制函数定义时的所有全局值。 globals: ObjectRef, + // 最后若干位置参数的缺省值;对象值由 trace 声明拥有,执行器按缺参位置补齐。 defaults: Vec, + // 与 code.free_variables 顺序一致的 Cell 强边;多个函数可共享同一单元以观察后续写入。 closure: Vec, } impl EndorFunctionObject { + /// 仅 ObjectStore::new_function 创建源函数载荷;代码/字典/闭包形状由存储层验证,字段强边由 trace 声明。 pub(crate) fn new( name: String, code: ObjectRef, @@ -187,80 +245,106 @@ impl EndorFunctionObject { 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 { 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 { Ok(format!("", 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, + // 显式原因与隐式上下文分别保存;存储层要求它们指向异常载荷。 cause: Option, context: Option, + // 仅保存上下文抑制标记;设置此值不会解除 context 的拥有边。 suppress_context: bool, + // 说明和 traceback 是自有文本/位置记录,本载荷不据此读取文件或执行回调。 notes: Vec, traceback: Vec, } impl EndorBaseExceptionObject { + /// 宿主/内置对象测试构造异常实例数据;初始没有 cause/context/notes/traceback,尚未分配或抛出异常。 pub fn new(name: impl Into, args: Vec) -> Self { Self { name: name.into(), @@ -272,71 +356,91 @@ impl EndorBaseExceptionObject { 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) -> 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 { self.cause } + /// ObjectStore::validate_builtin/trace 读取可选上下文;即使已抑制显示,此强边仍参与追踪。 pub fn context(&self) -> Option { 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 { Ok(format!( "{}({})", @@ -348,7 +452,9 @@ impl ObjectPayload for EndorBaseExceptionObject { .join(", ") )) } + /// ObjectStore::str 按参数形状生成消息;不自动拼接 cause/context、notes 或 traceback。 fn str(&self, _identity: ObjectRef) -> Result { + // 零参数为空消息;单 Text/Number 用其内容;其他形状(含单 Object)保留 Rust Debug 形式。 Ok(match self.args.as_slice() { [] => String::new(), [Value::Text(text)] => text.clone(), @@ -358,7 +464,9 @@ impl ObjectPayload for EndorBaseExceptionObject { } } +/// 内置载荷及其他对象的 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); }