diff --git a/crates/objects/src/store_builtins.rs b/crates/objects/src/store_builtins.rs index d6ddcf8..13702ce 100644 --- a/crates/objects/src/store_builtins.rs +++ b/crates/objects/src/store_builtins.rs @@ -1,3 +1,8 @@ +//! ObjectStore 的内置载荷入口;core::Vm 的同名方法把创建和访问委托到这里。 +//! eval 使用字典实现全局命名空间,使用 Cell 让多个闭包共享可更新的捕获槽。 +//! 返回句柄的 new_* 经 allocate 建立一份外部强引用;读取时复制 Value/ObjectRef +//! 不会自动 retain。修改已有容器统一经 Heap::replace_payload 更新强边与变更记录。 + use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -8,11 +13,16 @@ use crate::{ }; impl ObjectStore { + /// allocate 与 ObjectStore::replace_payload 在写入前调用,检查特定内置载荷的结构约束。 + /// 这里只检查函数、异常的特定关系;通用引用边的有效性仍由 Heap 的 trace 路径检查。 pub(crate) fn validate_builtin(&self, payload: &dyn ObjectPayload) -> Result<(), VmError> { + // 源码函数载荷必须引用代码对象,默认值和捕获槽必须与该代码的布局一致。 if let Some(function) = payload.as_any().downcast_ref::() { + // 借用代码元数据,不执行指令,也不为该借用新增引用计数。 let code = self .payload::(function.code(), "code")? .code(); + // 默认值可以只覆盖末尾部分参数;闭包槽数量则必须与自由变量数量完全一致。 if function.defaults().len() > code.parameters().len() || function.closure().len() != code.free_variables().len() { @@ -20,15 +30,20 @@ impl ObjectStore { "function defaults or closure do not match code", )); } + // 全局空间及可选的内置空间都必须是字典;无需读取它们的全部键值内容。 self.dictionary_kind(function.globals())?; + // 只有创建时捕获了内置空间才检查;None 不要求补建默认字典。 if let Some(builtins) = function.builtins() { self.dictionary_kind(builtins)?; } + // 每个捕获引用必须指向 Cell;槽的内容可以暂时为空,稍后再由执行器写入。 for cell in function.closure() { self.payload::(*cell, "cell")?; } } + // 异常链只接受异常载荷,避免后续格式化或异常传播拿到其他对象种类。 if let Some(exception) = payload.as_any().downcast_ref::() { + // flatten 跳过不存在的 cause/context,存在的两条边逐一检查。 for reference in [exception.cause(), exception.context()] .into_iter() .flatten() @@ -39,6 +54,8 @@ impl ObjectStore { Ok(()) } + /// VM、内置访问器和宿主通过此入口借用具体 Rust 载荷;expected 仅用于错误提示。 + /// object 会验证句柄并记录对象读取;这里的 Rust downcast 不等同于语言层名义类型判断。 pub fn payload( &self, reference: ObjectRef, @@ -51,14 +68,18 @@ impl ObjectStore { .ok_or(VmError::ObjectKindMismatch(expected)) } + /// Vm::new_string 委托的堆字符串构造入口;文本存入不可变字符串载荷。 pub fn new_string(&mut self, text: impl Into) -> Result { self.allocate(EndorStringObject::new(text)) } + /// Vm::new_code 将已构造的指令/元数据封装为代码对象;此处不启动执行。 pub fn new_code(&mut self, code: EndorCode) -> Result { self.allocate(EndorCodeObject::new(code)) } + /// Vm 的执行入口及 eval 创建闭包时调用,组合代码、全局空间、默认值和捕获槽。 + /// name 用作函数标识;code 指向可执行代码;closure 按该代码的自由变量顺序排列。 pub fn new_function( &mut self, name: impl Into, @@ -67,15 +88,20 @@ impl ObjectStore { defaults: Vec, closure: Vec, ) -> Result { + // 先检查全局空间,再读取它在函数创建时指定的内置命名空间。 self.dictionary_kind(globals)?; + // 保存的是字典身份:字典内部后续更新可见,但改写 globals.__builtins__ 不会重绑定此字段。 let builtins = match self.dict_get_name(globals, "__builtins__")? { + // 有该键时必须是字典对象;普通对象或标量(包括 Nil)都不能充当内置空间。 Some(Value::Object(reference)) => { self.dictionary_kind(reference)?; Some(reference) } + // 缺少键表示无内置空间,与显式存放 Nil 不同。 None => None, _ => return Err(VmError::ObjectKindMismatch("dict")), }; + // allocate 再校验代码/槽布局,并由 trace 为这些字段引用的对象建立内部强边。 self.allocate(EndorFunctionObject::new( name.into(), code, @@ -86,25 +112,30 @@ impl ObjectStore { )) } + /// Vm::new_number 构造堆上的数值对象,与 Value::Number 的直接数值表示分开。 pub fn new_number(&mut self, number: EndorNumber) -> Result { self.allocate(EndorNumberObject::new(number)) } + /// Vm::new_dict 及 eval 的字典构造路径使用,初始没有条目或内部引用边。 pub fn new_dict(&mut self) -> Result { self.allocate(EndorDictObject::new()) } - /// Returns borrowed keys and values in insertion order. + /// Vm::dict_items 和宿主迭代入口按插入顺序借用条目;不复制容器,也不新增外部强引用。 + /// payload 路径记录整个对象的读取,因此迭代依赖整个字典内容。 pub fn dict_items(&self, dictionary: ObjectRef) -> Result<&[(Value, Value)], VmError> { Ok(self .payload::(dictionary, "dict")? .entries()) } + /// 函数校验与按名称查找共用的种类检查,只观察“仍是字典”,不观察其全部内容。 pub(crate) fn dictionary_kind( &self, dictionary: ObjectRef, ) -> Result<&EndorDictObject, VmError> { + // 直接走 heap.get:若改用 payload/object,会附带过宽的整对象读取依赖。 let dict = self .heap .get(dictionary)? @@ -112,25 +143,32 @@ impl ObjectStore { .as_any() .downcast_ref::() .ok_or(VmError::ObjectKindMismatch("dict"))?; + // 仅在依赖收集上下文中登记种类观察;有效句柄和 downcast 已在上面检查。 self.heap.record_dictionary_kind(dictionary); Ok(dict) } - /// Name lookup records both present and missing text keys. General dict_get - /// and iteration continue to observe the entire dictionary contents. + /// eval 查找全局名和内置名时调用:既记录命中值,也记录“此名称不存在”。 + /// 这里只查一个字典;全局未命中后再查 builtins 的顺序由 eval 决定。 + /// 通用 dict_get 和条目迭代仍采用整字典依赖。 pub fn dict_get_name( &self, dictionary: ObjectRef, name: &str, ) -> Result, VmError> { + // 先确认字典种类,再以文本键查找,避免无关名称变更污染本次读取的依赖。 let dict = self.dictionary_kind(dictionary)?; + // clone 复制返回的 Value 表示;若其中含对象句柄,它仍是借用句柄。 let value = self .dict_position(dict, &Value::Text(name.into()))? .map(|index| dict.entries[index].1.clone()); + // None 也要登记,使未来新增这个名称能使原先的缺失观察失效。 self.heap.record_name(dictionary, name, &value); Ok(value) } + /// Vm::dict_get 与宿主通用键查找入口;返回 None 表示缺键,Some(Nil) 表示存了空值。 pub fn dict_get(&self, dictionary: ObjectRef, key: &Value) -> Result, VmError> { + // 通用读取保守地观察整个字典;先检查键可哈希,再进行线性相等比较。 let dict = self.payload::(dictionary, "dict")?; self.key_hash(key)?; Ok(self @@ -138,43 +176,58 @@ impl ObjectStore { .map(|index| dict.entries[index].1.clone())) } + /// Vm::dict_set、eval 构造字典和写入全局名时调用;修改保持字典对象身份。 pub fn dict_set( &mut self, dictionary: ObjectRef, key: Value, value: Value, ) -> Result<(), VmError> { + // 先复制载荷准备新状态;Rust clone 不 retain,提交前仍由旧字典持有原有引用边。 let mut dict = self.payload::(dictionary, "dict")?.clone(); self.key_hash(&key)?; + // 对象值必须来自本堆且仍有效;标量无需额外句柄检查。 self.validate_value(&value)?; if let Some(index) = self.dict_position(&dict, &key)? { - // Preserve the first key object and insertion position. + // 相等键只覆盖值,保留第一次插入的键对象及其顺序,不保存本次传入的替代键。 dict.entries[index].1 = value; } else { + // 新键追加到末尾;因此遍历顺序就是首次插入顺序。 dict.entries.push((key, value)); } + // Heap 在实际替换前检查新边和只读屏障,先保留新边再释放旧边,并记录 Contents 变更。 self.heap.replace_payload(dictionary, dict) } + /// Vm::dict_delete 删除一个键;返回值说明是否存在该键,错误通过 Result 单独传播。 pub fn dict_delete(&mut self, dictionary: ObjectRef, key: &Value) -> Result { + // 先复制载荷准备新状态;Rust clone 不 retain,提交前仍由旧字典持有原有引用边。 let mut dict = self.payload::(dictionary, "dict")?.clone(); self.key_hash(key)?; + // 缺键直接返回,不提交替换,因此也不会触发已有对象的写屏障或产生变更记录。 let Some(index) = self.dict_position(&dict, key)? else { return Ok(false); }; + // 删除后其余条目的相对顺序不变;被删除键值的强边在 Heap 提交时才释放。 dict.entries.remove(index); + // Heap 在实际替换前检查新边和只读屏障,先保留新边再释放旧边,并记录 Contents 变更。 self.heap.replace_payload(dictionary, dict)?; Ok(true) } + /// Vm::dict_clear 用空载荷替换条目集合;对象身份保留,原键值的强边由 Heap 释放。 pub fn dict_clear(&mut self, dictionary: ObjectRef) -> Result<(), VmError> { + // 先确认字典种类;即使原本为空仍走替换路径,受相同只读保护约束。 self.payload::(dictionary, "dict")?; self.heap .replace_payload(dictionary, EndorDictObject::new()) } + /// 字典读写共用的线性定位;条目向量没有哈希索引,hash 只负责准入校验。 fn dict_position(&self, dict: &EndorDictObject, key: &Value) -> Result, VmError> { + // 只比较键,忽略值;相等比较失败时立即返回错误,不把失败解释为不相等。 for (index, (existing, _)) in dict.entries.iter().enumerate() { + // 返回第一个相等键的位置,供读值、覆盖值或删除整条记录使用。 if self.key_equal(existing, key)? { return Ok(Some(index)); } @@ -182,23 +235,30 @@ impl ObjectStore { Ok(None) } + /// 通用字典操作检查键是否支持哈希;返回值当前不用于条目定位,也不用于持久化。 fn key_hash(&self, value: &Value) -> Result { + // 对象键先验证归属和存活,随后才允许访问它的载荷哈希实现。 self.validate_value(value)?; + // 直接值使用标准哈希器;对象委托各自实现,可由载荷拒绝不可哈希种类。 let mut hasher = DefaultHasher::new(); match value { Value::Nil => "nil".hash(&mut hasher), Value::Bool(value) => value.hash(&mut hasher), Value::Text(value) => value.hash(&mut hasher), + // 数值直接表示尚未定义字典哈希语义,因此明确拒绝,不隐式转换成文本或整数。 Value::Number(_) => return Err(VmError::UnsupportedOperation("numeric hash")), Value::Object(reference) => return self.hash(*reference), } Ok(hasher.finish()) } + /// dict_position 的键比较:直接文本与堆字符串按内容互通,其余按对象协议或 Value 比较。 fn key_equal(&self, left: &Value, right: &Value) -> Result { + // 将两种字符串表示临时取成拥有的 String;其他对象返回 None,仍保留后续比较路径。 let text = |value: &Value| -> Result, VmError> { Ok(match value { Value::Text(text) => Some(text.clone()), + // 借用载荷尝试字符串 downcast;非字符串不是错误,无效句柄才会传播错误。 Value::Object(reference) => self .object(*reference)? .payload() @@ -208,9 +268,11 @@ impl ObjectStore { _ => None, }) }; + // 两边都是文本才做内容相等;不做 Unicode 规范化或大小写折叠。 if let (Some(left), Some(right)) = (text(left)?, text(right)?) { return Ok(left == right); } + // 两个对象走 Store::compare 的正向/反向协议及身份回退;其他组合使用 Value 的相等实现。 match (left, right) { (Value::Object(left), Value::Object(right)) => { self.compare(*left, *right, CompareOp::Eq) @@ -219,11 +281,13 @@ impl ObjectStore { } } + /// Vm::new_cell 在 eval 把局部槽提升为闭包共享槽时调用;None 表示尚未初始化。 pub fn new_cell(&mut self, value: Option) -> Result { self.allocate(EndorCellObject::new(value)) } - /// Reads a borrowed value; an empty cell is not Value::Nil. + /// Vm::cell_get 供自由变量读取及赋值前读取旧值使用;复制出的对象句柄仍是借用。 + /// 空槽返回 EmptyCell;已初始化为 Some(Value::Nil) 则正常返回 Nil。 pub fn cell_get(&self, cell: ObjectRef) -> Result { self.payload::(cell, "cell")? .value() @@ -231,13 +295,18 @@ impl ObjectStore { .ok_or(VmError::EmptyCell) } + /// Vm::cell_set 在自由变量赋值时调用;所有共享该 Cell 的闭包随后读取相同的新值。 pub fn cell_set(&mut self, cell: ObjectRef, value: Value) -> Result<(), VmError> { + // 确认是 Cell 后由 Heap 检查引用边和只读屏障,更新时保留 Cell 身份。 self.payload::(cell, "cell")?; self.heap .replace_payload(cell, EndorCellObject::new(Some(value))) } + /// Vm::cell_clear 在删除捕获变量时调用;恢复未初始化状态,并释放旧值的内部强边。 + /// 即使槽原本为空仍提交替换,不能通过重复清空绕过只读屏障。 pub fn cell_clear(&mut self, cell: ObjectRef) -> Result<(), VmError> { + // 确认是 Cell 后由 Heap 检查引用边和只读屏障,更新时保留 Cell 身份。 self.payload::(cell, "cell")?; self.heap.replace_payload(cell, EndorCellObject::default()) }