docs(core): explain stack values comparisons and object builders

This commit is contained in:
2026-11-15 17:54:50 +08:00
parent 56df4e5b35
commit 8e800dd0b8
+50 -2
View File
@@ -1050,7 +1050,9 @@ impl Vm {
}
}
/// 各操作码按下标读取语言值,返回 clone 的借用表示,不 retain;Null 或无对应项报字节码错误。
fn stack_value(frame: &EndorFrame, index: usize) -> Result<Value, VmError> {
// Rust clone 仅复制 Value/句柄,不能把借用读取当成新拥有根。
match frame.stack.get(index) {
Some(StackEntry::Value(value)) => Ok(value.clone()),
_ => Err(invalid(
@@ -1060,15 +1062,18 @@ impl Vm {
}
}
/// 操作码读取栈顶语言值的快捷入口;栈非空由代码验证保证。
fn top_value(frame: &EndorFrame) -> Result<Value, VmError> {
Self::stack_value(frame, frame.stack.len() - 1)
}
/// StoreFast/StoreGlobal/StoreDeref 共用赋值选择:对象相同或共享引用原地更新成功时保留旧身份。
fn assignment_value(
&mut self,
previous: Option<Value>,
incoming: Value,
) -> Result<Value, VmError> {
// 只有新旧均为对象才尝试原地赋值;标量/不支持协议时使用 incoming。
if let (Some(Value::Object(old)), Value::Object(new)) = (&previous, &incoming)
&& (old == new || self.objects.assign_from(*old, *new)?)
{
@@ -1077,23 +1082,31 @@ impl Vm {
Ok(incoming)
}
/// Load 类操作把借用值放入拥有栈槽,先验证/retain 成功才压栈。
fn push_borrowed(&mut self, frame: &mut EndorFrame, value: Value) -> Result<(), VmError> {
// retain 失败时保持栈不变,避免产生没有拥有根的栈项。
self.retain_value(&value)?;
frame.stack.push(StackEntry::Value(value));
Ok(())
}
/// 操作码完成后丢弃已消费输入,每个对象槽归还一根,Null 不参与计数。
fn discard(&mut self, frame: &mut EndorFrame, count: usize) {
// 逐项弹栈,代码验证保证数量足够,不按对象身份去重释放。
for _ in 0..count {
// Null 仅是调用协议标记,不能作为语言对象根释放。
if let StackEntry::Value(value) = frame.stack.pop().expect("verified stack height") {
self.drop_root(value);
}
}
}
/// Load/Store/DeleteDeref 与 LoadClosure 取对应 Cell;索引布局已验证,仍检查载荷种类。
fn cell_slot(&self, frame: &EndorFrame, index: usize) -> Result<ObjectRef, VmError> {
// 槽必须已初始化为 Cell 对象,Cell 内部仍可为空。
match &frame.localsplus[index] {
Some(Value::Object(reference)) => {
// 验证具体 Cell 载荷,不仅检查 Object 变体。
self.payload::<EndorCellObject>(*reference, "cell")?;
Ok(*reference)
}
@@ -1104,7 +1117,9 @@ impl Vm {
}
}
/// ToBool/UnaryNot/条件跳转共用真值规则,直接值就地判断,对象交载荷协议。
fn value_is_true(&self, value: &Value) -> Result<bool, VmError> {
// 直接数值/字符串与对象载荷分开处理,协议错误不被当成 false。
match value {
Value::Nil => Ok(false),
Value::Bool(value) => Ok(*value),
@@ -1114,7 +1129,9 @@ impl Vm {
}
}
/// 比较把直接 Text 与堆字符串统一成拥有 String,非字符串 None,坏句柄报错。
fn text_value(&self, value: &Value) -> Result<Option<String>, VmError> {
// 仅尝试所需具体载荷,非目标类别是正常 None,不是 kind mismatch。
Ok(match value {
Value::Text(text) => Some(text.clone()),
Value::Object(reference) => self
@@ -1127,10 +1144,12 @@ impl Vm {
})
}
/// 比较/算术将直接 Number 与堆数值统一为借用数值,不转换表示或新增根。
fn number_value<'a>(
&'a self,
value: &'a Value,
) -> Result<Option<&'a crate::EndorNumber>, VmError> {
// 仅尝试所需具体载荷,非目标类别是正常 None,不是 kind mismatch。
Ok(match value {
Value::Number(n) => Some(n),
Value::Object(o) => self
@@ -1143,16 +1162,20 @@ impl Vm {
})
}
/// CompareOp 指令的语言比较入口,优先数学数值与字符串内容,然后对象协议/直接值相等。
fn compare_values(
&self,
left: &Value,
right: &Value,
operation: CompareOp,
) -> Result<bool, VmError> {
// 两侧都先尝试识别数值,不能对混合表示直接使用 Rust Eq。
let (left_number, right_number) = (self.number_value(left)?, self.number_value(right)?);
// 直接与堆数值都走数学比较,因此整数、小数、分数可跨表示等价。
if let (Some(left), Some(right)) = (left_number, right_number) {
return left.numeric_compare(right, operation);
}
// 仅一侧是数值时相等为 false、不等为 true,有序关系明确拒绝。
if left_number.is_some() || right_number.is_some() {
return match operation {
CompareOp::Eq => Ok(false),
@@ -1160,6 +1183,7 @@ impl Vm {
_ => Err(VmError::UnsupportedOperation("mixed numeric ordering")),
};
}
// 两侧均为文本时按字符串内容比较,不做大小写折叠或 Unicode 规范化。
if let (Some(left), Some(right)) = (self.text_value(left)?, self.text_value(right)?) {
return Ok(match operation {
CompareOp::Eq => left == right,
@@ -1170,9 +1194,11 @@ impl Vm {
CompareOp::Ge => left >= right,
});
}
// 非数值/文本的双对象走 Store 正向、反向与身份回退协议。
if let (Value::Object(left), Value::Object(right)) = (left, right) {
return self.compare(*left, *right, operation);
}
// 其他直接值只支持相等/不等,不能凭 Rust 变体顺序定义语言大小。
match operation {
CompareOp::Eq => Ok(left == right),
CompareOp::Ne => Ok(left != right),
@@ -1180,17 +1206,25 @@ impl Vm {
}
}
/// BuildMap 逐对读取栈上键值,构造字典拥有根;失败释放半成品,输入栈由调用方清理。
fn build_map(&mut self, frame: &EndorFrame, count: usize) -> Result<ObjectRef, VmError> {
// 新字典先取得一份根,所有插入成功才移交给 BuildMap 调用方。
let dictionary = self.new_dict()?;
// 闭包收拢插入错误,外层统一释放未交付字典根。
let result = (|| -> Result<(), VmError> {
// 输入是末尾 count 对交错键值;代码验证已检查栈高及数量乘法。
let start = frame.stack.len() - count * 2;
// 按源码顺序插入,重复相等键遵循字典保留首次键位置的规则。
for index in 0..count {
// 先复制键的借用表示,原栈根继续保护其对象。
let key = Self::stack_value(frame, start + index * 2)?;
// 读取对应值的借用表示,字典提交后自己持有内部强边。
let value = Self::stack_value(frame, start + index * 2 + 1)?;
self.dict_set(dictionary, key, value)?;
}
Ok(())
})();
// 失败字典可能已持有部分键值边,释放其根会一并归还这些边。
if let Err(error) = result {
self.release(dictionary)?;
return Err(error);
@@ -1198,45 +1232,58 @@ impl Vm {
Ok(dictionary)
}
/// MakeFunction 从默认值、Cell 和代码栈布局构造函数,成功返回拥有根,不先弹出输入。
fn make_function(&mut self, frame: &EndorFrame, argument: u32) -> Result<ObjectRef, VmError> {
// 低/高 16 位计数决定默认值段与 Cell 段,最后一项固定为代码。
let (default_count, closure_count) = Instruction::function_counts(argument);
// 代码必须是对象,后续 payload 再检查确为 EndorCodeObject。
let Value::Object(code) = Self::top_value(frame)? else {
return Err(VmError::ObjectKindMismatch("code"));
};
// 复制代码诊断名,避免持载荷借用跨可变函数分配。
let name = self
.payload::<EndorCodeObject>(code, "code")?
.code()
.name()
.to_owned();
// 栈高度由验证器证明,默认值段在 Cell 段之前。
let start = frame.stack.len() - default_count - closure_count - 1;
// 收集借用默认值表示,不提前归还栈根。
let defaults = (start..start + default_count)
.map(|index| Self::stack_value(frame, index))
.collect::<Result<Vec<_>, _>>()?;
// 捕获段每项必须是对象;是否 Cell 以及数量是否吻合由函数构造再次验证。
let closure = (start + default_count..frame.stack.len() - 1)
.map(|index| match Self::stack_value(frame, index)? {
Value::Object(cell) => Ok(cell),
_ => Err(VmError::ObjectKindMismatch("cell")),
})
.collect::<Result<Vec<_>, _>>()?;
// Keep every input root on the stack until allocation has validated
// and retained all function edges. Failure leaves cleanup to the frame.
// 所有输入根在验证并建立函数内部强边前一直留栈,失败时仍由帧统一清理。
self.new_function(name, code, frame.globals, defaults, closure)
}
/// BuildClass 从四个栈项构造类,保留当前函数作为字段注解环境,输入保活至构造完成。
fn build_class(&mut self, frame: &EndorFrame) -> Result<ObjectRef, VmError> {
// 类构造输入固定为名称、基类、字段字典、方法字典。
let start = frame.stack.len() - 4;
// 类名必须是直接文本,与元数据里显示名称分开。
let Value::Text(name) = Self::stack_value(frame, start)? else {
return Err(VmError::ObjectKindMismatch("class name"));
};
// 基类必须是对象,Store 类构造进一步校验 Type 载荷与继承分配策略。
let Value::Object(base) = Self::stack_value(frame, start + 1)? else {
return Err(VmError::ObjectKindMismatch("base Type"));
};
// 字段说明必须来自字典对象,读取时再校验具体载荷。
let Value::Object(fields) = Self::stack_value(frame, start + 2)? else {
return Err(VmError::ObjectKindMismatch("field map"));
};
// 方法说明必须来自字典对象,函数句柄有效性由 Store 类构造验证。
let Value::Object(methods) = Self::stack_value(frame, start + 3)? else {
return Err(VmError::ObjectKindMismatch("method map"));
};
// 字段键值都须为直接文本,形成按字典插入顺序排列的 ClassField。
let fields = self
.dict_items(fields)?
.iter()
@@ -1248,6 +1295,7 @@ impl Vm {
_ => Err(VmError::ObjectKindMismatch("class field annotation")),
})
.collect::<Result<Vec<_>, _>>()?;
// 方法键须文本、值须对象,收集为名称有序表,仍不额外保留函数根。
let methods = self
.dict_items(methods)?
.iter()