diff --git a/crates/core/src/eval.rs b/crates/core/src/eval.rs index 534ee20..617926a 100644 --- a/crates/core/src/eval.rs +++ b/crates/core/src/eval.rs @@ -1,3 +1,7 @@ +//! VM 的指令执行与调用生命周期;宿主入口在此准备帧,底层对象规则仍通过 Vm/Store 操作。 +//! 执行结果中的对象拥有一份外部根,参数通常借用;帧/原生上下文负责保留和清理执行期间根。 +//! 只读续执行跨 Pending 保持 Heap 作用域;托管可变协程另存载荷,不沿用该全局作用域。 + use crate::ExecutionProgress; use crate::frame::{Coroutine, CoroutineState, NativeReturn, StackEntry, SuspendedCall}; use crate::{ @@ -8,11 +12,15 @@ use crate::{ use crate::{CoroutineProgress, ObjectPayload}; use crate::{NativeContext, NativeResult}; +/// prepare_call 统一结果,成功的 Value/Frame 都携带执行期间已取得的拥有根。 enum PreparedCall { + // 原生或委托调用在准备阶段直接完成,结果根移交上层。 Value(Value), + // 源码帧已绑定参数并持有根,交给循环执行或暂存。 Frame(EndorFrame), } +/// 求值器构造携带代码单元偏移的字节码错误,不将偏移误报为源码行。 fn invalid(offset: usize, reason: &'static str) -> VmError { VmError::InvalidBytecode { offset, reason } } @@ -21,24 +29,31 @@ impl Vm { /// Execute a code object in a globals dictionary. Like CPython's EvalCode /// entry path, this constructs a temporary function and enters its frame. /// Object results own one external reference: release_value() when done. + /// 宿主执行已验证代码,使用默认预算,最终对象结果归调用方拥有。 pub fn execute(&mut self, code: ObjectRef, globals: ObjectRef) -> Result { self.execute_with_limits(code, globals, ExecutionLimits::default()) } + /// 宿主以指定预算执行代码,临时源码函数包装全局/代码引用,调用结束后释放包装根。 pub fn execute_with_limits( &mut self, code: ObjectRef, globals: ObjectRef, limits: ExecutionLimits, ) -> Result { + // 新调用从空诊断开始,后续失败才写入该次剩余帧回溯。 self.last_traceback.clear(); + // 借用代码载荷的诊断名后转为拥有 String,避免跨可变分配保留载荷借用。 let name = self .payload::(code, "code")? .code() .name() .to_owned(); + // 创建无参数默认值/无捕获的临时执行函数;代码所需布局仍由函数校验。 let function = self.new_function(name, code, globals, Vec::new(), Vec::new())?; + // 先保存调用结果,使成功和错误都能在返回前释放临时函数根。 let result = self.call_with_limits(function, &[], limits); + // 结果对象已由调用路径保留,释放包装函数不应使返回结果失效。 self.release(function)?; result } @@ -46,6 +61,7 @@ impl Vm { /// Dispatch both language and host functions. Arguments are borrowed; /// every Object result owns one external reference, including native /// callbacks that return one of their borrowed arguments. + /// 宿主同步调用源码/原生函数,参数借用,返回对象根需 release_value。 pub fn call(&mut self, function: ObjectRef, arguments: &[Value]) -> Result { self.call_with_limits(function, arguments, ExecutionLimits::default()) } @@ -53,12 +69,14 @@ impl Vm { /// Allow result allocation while rejecting mutations of objects that were /// already alive on entry. All reads share the call's normal frame budget. /// Returned read handles are borrowed dependency identities, not owned roots. + /// 宿主同步只读兼容入口,只保留对象读取集合,丢弃精确依赖键。 pub fn call_read_only_with_limits( &mut self, function: ObjectRef, arguments: &[Value], limits: ExecutionLimits, ) -> Result<(Value, Vec), VmError> { + // 精确入口统一维护只读作用域与结果所有权,这里只改变返回观察的形状。 let (value, reads) = self.call_read_only_precise_with_limits(function, arguments, limits)?; Ok((value, reads.objects)) @@ -67,43 +85,56 @@ impl Vm { /// Synchronous read-only call with precise observations as well as the /// conservative object list. Result ownership and hard limits match call(). /// ReadSet and all identities/value snapshots inside it are borrowed only. + /// 宿主同步只读入口,建立保护快照,成功返回拥有结果与借用观察集合。 pub fn call_read_only_precise_with_limits( &mut self, function: ObjectRef, arguments: &[Value], limits: ExecutionLimits, ) -> Result<(Value, crate::ReadSet), VmError> { + // 快照进入前对象身份;之后新建结果允许初始化,但已有对象受写屏障保护。 let scope = self.objects.begin_read_only(); + // 先保存调用结果,使成功和错误都能在返回前释放临时函数根。 let result = self.call_with_limits(function, arguments, limits); + // 结束域先取回借用观察,之后才将调用成功值与观察组合返回。 let reads = self.objects.end_read_only_precise(scope)?; result.map(|value| (value, reads)) } + /// 同步调用核心:准备可立即完成的原生结果或源码帧,执行后清理帧及保存错误回溯。 pub fn call_with_limits( &mut self, function: ObjectRef, arguments: &[Value], limits: ExecutionLimits, ) -> Result { + // 已有只读续执行保留活跃作用域,禁止另开同步调用干扰它。 if self.suspended_call.is_some() { return Err(VmError::UnsupportedOperation( "call while read-only execution is suspended", )); } + // 新调用从空诊断开始,后续失败才写入该次剩余帧回溯。 self.last_traceback.clear(); + // 累计调用准备和指令工作量,切片恢复不会将其清零。 let mut executed = 0; + // 原生调用可能在准备阶段完成并直接移交拥有结果,源码则进入帧循环。 let frame = match self.prepare_call(function, arguments.to_vec(), &mut executed, limits)? { PreparedCall::Value(value) => return Ok(value), PreparedCall::Frame(frame) => frame, }; + // 源码帧已创建但预算不允许执行,必须先清理所持根再报错。 if limits.max_frames == 0 { self.clear_frame(frame); return Err(VmError::ExecutionLimit("frames")); } + // 显式帧向量代替 Rust 递归调用,调用深度由硬预算控制。 let mut frames = vec![frame]; + // 同步入口没有软切片限制且不允许 Pending await;只读恢复另按 slice_end 执行。 let result = self .eval_frames(&mut frames, limits, &mut executed, usize::MAX, false) .map(|value| value.expect("unbounded slice completes or fails")); + // 错误时先保存诊断,再销毁帧;使用 current_offset 而不是下一指令偏移。 if result.is_err() { self.last_traceback = frames .iter() @@ -115,6 +146,7 @@ impl Vm { }) .collect(); } + // 逐帧回收栈、局部、函数和返回约束根,不把 Rust drop 误当托管 release。 while let Some(frame) = frames.pop() { self.clear_frame(frame); } @@ -124,6 +156,7 @@ impl Vm { /// Begin a resumable read-only call. One VM owns at most one continuation. /// A slice is a soft instruction quantum: native callbacks and delegation /// complete atomically and remain subject to the cumulative hard limits. + /// 宿主开始单个可分片只读调用,原生准备可立即完成,源码帧停放在 VM 内。 pub fn start_read_only_call( &mut self, function: ObjectRef, @@ -131,22 +164,32 @@ impl Vm { limits: ExecutionLimits, slice: usize, ) -> Result { + // 拒绝零量子,避免调度者永远不推进且仍报告 Pending。 if slice == 0 { return Err(VmError::InvalidArgument("execution slice must be positive")); } + // 只读续执行只能在没有其他续执行或只读域时启动,避免作用域令牌交错。 if self.suspended_call.is_some() || self.objects.is_read_only() { return Err(VmError::UnsupportedOperation( "start suspended call inside another execution", )); } + // 新调用从空诊断开始,后续失败才写入该次剩余帧回溯。 self.last_traceback.clear(); + // 快照进入前对象身份;之后新建结果允许初始化,但已有对象受写屏障保护。 let scope = self.objects.begin_read_only(); + // 累计调用准备和指令工作量,切片恢复不会将其清零。 let mut executed = 0; + // 准备原生调用/委托可能消耗预算或报错,先捕获结果以便三种分支都结束/保存作用域。 let prepared = self.prepare_call(function, arguments.to_vec(), &mut executed, limits); + // 成功直接值关闭域;源码帧保存域;准备失败关闭域后传播错误。 match prepared { + // 原生立即完成,无源码帧要保存,但仍需结束观察收集。 Ok(PreparedCall::Value(value)) => { + // 若读取域收尾失败,必须归还已经取得的结果根,不能泄漏。 let reads = match self.objects.end_read_only_precise(scope) { Ok(reads) => reads, + // 出错分支在返回前处理本分支拥有的结果/作用域,不能保留半完成状态。 Err(error) => { self.drop_root(value); return Err(error); @@ -159,21 +202,25 @@ impl Vm { instructions: executed, }) } + // 源帧尚未执行,先通过帧预算再交给 VM 的单个续执行槽。 Ok(PreparedCall::Frame(frame)) => { + // 源码帧已创建但预算不允许执行,必须先清理所持根再报错。 if limits.max_frames == 0 { self.clear_frame(frame); self.objects.end_read_only(scope)?; return Err(VmError::ExecutionLimit("frames")); } + // 保存根仍由帧持有,Heap 只读作用域跨后续 Pending 不关闭。 self.suspended_call = Some(SuspendedCall { frames: vec![frame], scope, limits, executed, }); - // Preparation counts toward the first quantum as well. + // 首片边界从零计,调用准备已用计数也占首片软预算,不另赠一片。 self.resume_read_only_to(slice) } + // 出错分支在返回前处理本分支拥有的结果/作用域,不能保留半完成状态。 Err(error) => { self.objects.end_read_only(scope)?; Err(error) @@ -181,16 +228,20 @@ impl Vm { } } + /// 调度者判断是否有只读续执行,不表示托管协程数量或等待状态。 pub fn has_suspended_call(&self) -> bool { self.suspended_call.is_some() } /// Resume existing frames, never replay earlier instructions. The read-only /// scope remains active across Pending; preexisting heap writes stay barred. + /// 宿主从已保存指令位置继续,预算跨片累计,不能重放已执行语句。 pub fn resume_read_only_call(&mut self, slice: usize) -> Result { + // 拒绝零量子,避免调度者永远不推进且仍报告 Pending。 if slice == 0 { return Err(VmError::InvalidArgument("execution slice must be positive")); } + // 读取此前累计计数,新增软边界以 saturating_add 防整数绕回。 let executed = self .suspended_call .as_ref() @@ -199,11 +250,14 @@ impl Vm { self.resume_read_only_to(executed.saturating_add(slice)) } + /// start/resume 共用的累计软边界执行器;Pending 原样放回状态,终态关闭只读域。 fn resume_read_only_to(&mut self, slice_end: usize) -> Result { + // 暂时取走续执行状态独占推进;只有 Pending 才重新放回,终态不残留槽。 let mut call = self .suspended_call .take() .ok_or(VmError::InvalidArgument("no suspended call"))?; + // 按累计 slice_end 推进现有帧;false 禁止 await 停放,None 表示软切片结束。 let result = self.eval_frames( &mut call.frames, call.limits, @@ -211,11 +265,15 @@ impl Vm { slice_end, false, ); + // 软切片耗尽:保留原帧/作用域/硬预算计数,不清理根或重新准备函数。 if let Ok(None) = result { + // 报告从调用起点累计的工作量,不是当前片局部数量。 let instructions = call.executed; + // 将完整续执行状态归还 VM,原有只读屏障继续生效。 self.suspended_call = Some(call); return Ok(ExecutionProgress::Pending { instructions }); } + // 错误时先保存诊断,再销毁帧;使用 current_offset 而不是下一指令偏移。 if result.is_err() { self.last_traceback = call .frames @@ -228,10 +286,13 @@ impl Vm { }) .collect(); } + // 逐帧回收栈、局部、函数和返回约束根,不把 Rust drop 误当托管 release。 while let Some(frame) = call.frames.pop() { self.clear_frame(frame); } + // 完成/失败统一关闭作用域,保留收尾错误供下方与执行结果一起处理。 let reads = self.objects.end_read_only_precise(call.scope); + // 仅执行与收尾均成功才交付结果;收尾失败释放成功结果,执行失败优先传播执行错误。 match (result, reads) { (Ok(Some(value)), Ok(reads)) => Ok(ExecutionProgress::Complete { value, @@ -239,6 +300,7 @@ impl Vm { reads: reads.objects, instructions: call.executed, }), + // 已产生的成功结果此时不能交付,归还其对象根避免泄漏。 (Ok(Some(value)), Err(error)) => { self.drop_root(value); Err(error) @@ -249,10 +311,13 @@ impl Vm { } /// Release all retained execution roots and close the read-only scope. + /// 宿主放弃当前只读续执行,逐帧释放根并关闭作用域,无任务时返回 false。 pub fn cancel_read_only_call(&mut self) -> Result { + // 没有续执行是正常无操作;有任务则一次取走,后续取消不会重复释放。 let Some(mut call) = self.suspended_call.take() else { return Ok(false); }; + // 逐帧回收栈、局部、函数和返回约束根,不把 Rust drop 误当托管 release。 while let Some(frame) = call.frames.pop() { self.clear_frame(frame); }