docs(core): explain frame initialization and execution root ownership

This commit is contained in:
2026-11-15 17:52:09 +08:00
parent 11703b7331
commit 6fddeedef3
+50 -2
View File
@@ -637,57 +637,76 @@ impl Vm {
&self.last_traceback
}
/// 宿主归还 call/execute/协程完成结果,或执行器清理拥有值;标量由 Rust 释放,对象归还一份外部根。
pub fn release_value(&mut self, value: Value) -> Result<(), VmError> {
// 仅 Object 变体占用托管根,直接值不调用 release/retain。
if let Value::Object(reference) = value {
self.release(reference)?;
}
Ok(())
}
/// 执行器把借用值放入拥有槽/结果前先验证并保留对象根,标量不需要计数。
fn retain_value(&mut self, value: &Value) -> Result<(), VmError> {
// 先检查对象来自当前堆且存活,不能为异堆/过期值建立执行根。
self.validate_value(value)?;
// 仅 Object 变体占用托管根,直接值不调用 release/retain。
if let Value::Object(reference) = value {
self.retain(*reference)?;
}
Ok(())
}
/// 已知拥有根的内部清理入口,失败代表执行器所有权不变量破坏,不静默忽略。
fn drop_root(&mut self, value: Value) {
self.release_value(value)
.expect("execution owns every object root it releases");
}
/// 返回/失败/取消时统一销毁活跃帧:清理约束、栈、局部槽、函数及返回注解根。
fn clear_frame(&mut self, frame: EndorFrame) {
// 先释放委托返回约束持有的定义根,不依赖元数据 Rust Drop 管理托管计数。
self.clear_native_returns(frame.native_returns);
// 每个栈项独立拥有计数,重复对象值必须逐项归还。
for entry in frame.stack {
// Null 是协议标记,不是对象或语言 Nil,不参与根释放。
if let StackEntry::Value(value) = entry {
self.drop_root(value);
}
}
// 未绑定 None 跳过,其余每个局部/自由变量槽拥有的对象根逐项释放。
for value in frame.localsplus.into_iter().flatten() {
self.drop_root(value);
}
// 函数根保证代码/全局/内置空间在帧内可达;这些借用字段不额外 release。
self.release(frame.function)
.expect("frame owns its function root");
// 帧创建时额外保留的名义/能力返回定义根,也必须单独归还。
if let Some(reference) = frame.return_type.and_then(|kind| kind.reference()) {
self.release(reference)
.expect("frame owns its return type root");
}
}
/// prepare_call 为源码函数建立帧,先校验数量/值/契约,再取得根并初始化局部和捕获槽。
fn prepare_frame(
&mut self,
function: ObjectRef,
arguments: &[Value],
) -> Result<EndorFrame, VmError> {
// 复制函数元数据以跨可变 Store 操作使用;clone 不是取得托管根。
let data = self
.payload::<EndorFunctionObject>(function, "function")?
.clone();
// 共享不可变 Rust 执行数据,常量图仍需后面的函数根保活。
let executable = self
.payload::<EndorCodeObject>(data.code(), "code")?
.shared_code();
// 源码参数槽总数含显式 self,与默认值共同决定数量区间。
let maximum = executable.parameters().len();
// 函数构造已保证默认值数量不超过参数数量,因此减法安全。
let required = maximum - data.defaults().len();
// 数量先检查,后续默认值索引依赖 required..maximum 不变量。
if arguments.len() < required || arguments.len() > maximum {
return Err(VmError::ArgumentCount {
name: data.name().into(),
@@ -696,12 +715,16 @@ impl Vm {
provided: arguments.len(),
});
}
// 所有传入对象先验证,不能等到部分槽初始化后才发现异堆参数。
for value in arguments {
// 先检查对象来自当前堆且存活,不能为异堆/过期值建立执行根。
self.validate_value(value)?;
}
// 优先实参首项;仅当所有参数均可默认且无实参时才尝试首默认值作为 self。
let receiver = arguments
.first()
.or_else(|| {
// 至少一个必填参数时不能从默认值假造接收者。
if required == 0 {
data.defaults().first()
} else {
@@ -712,23 +735,30 @@ impl Vm {
Value::Object(receiver) => Some(*receiver),
_ => None,
});
// 对象首参触发方法契约门禁;返回 bool 决定即使普通源码检查关闭是否仍要验注解。
let typed_method = if let Some(receiver) = receiver {
self.objects
.check_method_contract_call(function, receiver, self.source_type_checks)?
} else {
false
};
// 尚未解析的返回约束不拥有根,解析完成后才统一保留。
let mut return_type = None;
// 显式开关或类型化能力要求启用源码注解检查;无源码签名仍跳过这段解析。
if (self.source_type_checks || typed_method)
&& let Some(signature) = executable.source_signature()
{
// 所有参数(含使用默认值的位置)逐个匹配,不仅检查调用方显式传入部分。
for (index, expected_name) in signature.parameters.iter().enumerate() {
// 缺失的尾参数从默认值列表按 index-required 读取。
let value = arguments
.get(index)
.unwrap_or_else(|| &data.defaults()[index - required]);
// 注解按函数环境和可能的接收者解析,方法自身类名可绑定真实声明定义。
let expected = self
.objects
.annotation_type(function, expected_name, receiver)?;
// 匹配失败生成具名参数诊断,在取得帧拥有根之前退出。
if !self.objects.matches_annotation(value, expected)? {
return Err(VmError::TypeMismatch {
function: data.name().into(),
@@ -738,19 +768,23 @@ impl Vm {
});
}
}
// 先解析返回约束,随后额外保留其定义根,防执行期间全局重绑定使其失效。
return_type = Some(self.objects.annotation_type(
function,
&signature.return_type,
receiver,
)?);
}
// 从这里开始函数成为帧拥有根,代码/全局/内置空间通过函数内部边维持存活。
self.retain(function)?;
// 若返回类型根无法保留,归还刚取得的函数根,不创建半拥有帧。
if let Some(reference) = return_type.and_then(|kind| kind.reference())
&& let Err(error) = self.retain(reference)
{
self.release(function)?;
return Err(error);
}
// 建立空槽帧;函数/返回定义根已持有,其他槽后续逐项初始化。
let mut frame = EndorFrame {
return_type,
native_returns: Vec::new(),
@@ -767,8 +801,11 @@ impl Vm {
stack: Vec::with_capacity(executable.stack_size()),
executable,
};
// 局部闭包汇总初始化失败,外层 clear_frame 统一处理已成功安装的根。
let initialized = (|| -> Result<(), VmError> {
// 方法能力返回约束也要保留,后续返回需满足所有能力而非仅源码声明。
if let Some(receiver) = receiver {
// push_native_return 负责保留各类型定义,失败时其自身清理未提交部分。
for (name, expected) in self.objects.method_trait_returns(function, receiver)? {
self.push_native_return(
&mut frame.native_returns,
@@ -776,33 +813,44 @@ impl Vm {
)?;
}
}
// 已传参数会覆盖默认值前段,仅拼接尚缺的尾部默认值。
let defaults = data.defaults().iter().skip(arguments.len() - required);
// 实参先于缺失默认值,依次填参数前缀,其余未绑定局部仍为 None。
for (slot, value) in frame
.localsplus
.iter_mut()
.zip(arguments.iter().chain(defaults))
{
// 先 retain 再写槽,失败槽仍空;已写槽由外层失败清理释放。
self.retain_value(value)?;
*slot = Some(value.clone());
}
// Captured local cells occupy the original local slots, as in
// CPython's localsplus layout. Free cells follow the local slots.
// 捕获局部保留原局部槽位置但改存 Cell;外部自由变量 Cell 追加在全部局部之后。
// 捕获局部不增加槽位置,仅将原值替换为拥有该值的 Cell。
let local_count = frame.executable.layout().locals.len();
// 只遍历局部段,外部自由变量 Cell 在后续单独安装。
for (index, slot) in frame.localsplus[..local_count].iter_mut().enumerate() {
// 需要被内部闭包捕获的局部改用共享 Cell,即使原槽未绑定也创建空 Cell。
if frame.executable.is_cell_slot(index) {
// 新 Cell 的载荷强边先保留旧值,返回的一份外部根由该局部槽接管。
let cell = self.new_cell(slot.clone())?;
// 槽原值已被 Cell 内部边接管,归还原来那份槽执行根,不重复持有。
if let Some(old) = slot.replace(Value::Object(cell)) {
self.drop_root(old);
}
}
}
// 外层捕获 Cell 段紧跟所有局部槽,顺序与代码 free_variables 一致。
let first_free = frame.executable.layout().locals.len();
// 闭包 Cell 已由函数持边,但每个活跃自由变量槽仍需独立执行根。
for (index, cell) in data.closure().iter().enumerate() {
// retain 成功才写槽;函数构造的闭包数量校验保证目标索引合法。
self.retain(*cell)?;
frame.localsplus[first_free + index] = Some(Value::Object(*cell));
}
Ok(())
})();
// 失败时清理已装入槽的根及函数/返回约束,未装入槽不需要回滚。
if let Err(error) = initialized {
self.clear_frame(frame);
return Err(error);