Files
endorphin-aster-vm/crates/core/src/eval.rs
T

1731 lines
91 KiB
Rust

//! VM 的指令执行与调用生命周期;宿主入口在此准备帧,底层对象规则仍通过 Vm/Store 操作。
//! 执行结果中的对象拥有一份外部根,参数通常借用;帧/原生上下文负责保留和清理执行期间根。
//! 只读续执行跨 Pending 保持 Heap 作用域;托管可变协程另存载荷,不沿用该全局作用域。
use crate::ExecutionProgress;
use crate::frame::{Coroutine, CoroutineState, NativeReturn, StackEntry, SuspendedCall};
use crate::{
CompareOp, EndorCellObject, EndorCodeObject, EndorFrame, EndorFunctionObject,
EndorStringObject, ExecutionLimits, ExecutionLocation, Instruction, ObjectRef, Opcode, Value,
Vm, VmError,
};
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 }
}
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<Value, VmError> {
self.execute_with_limits(code, globals, ExecutionLimits::default())
}
/// 宿主以指定预算执行代码,临时源码函数包装全局/代码引用,调用结束后释放包装根。
pub fn execute_with_limits(
&mut self,
code: ObjectRef,
globals: ObjectRef,
limits: ExecutionLimits,
) -> Result<Value, VmError> {
// 新调用从空诊断开始,后续失败才写入该次剩余帧回溯。
self.last_traceback.clear();
// 借用代码载荷的诊断名后转为拥有 String,避免跨可变分配保留载荷借用。
let name = self
.payload::<EndorCodeObject>(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
}
/// 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<Value, VmError> {
self.call_with_limits(function, arguments, ExecutionLimits::default())
}
/// 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<ObjectRef>), VmError> {
// 精确入口统一维护只读作用域与结果所有权,这里只改变返回观察的形状。
let (value, reads) =
self.call_read_only_precise_with_limits(function, arguments, limits)?;
Ok((value, reads.objects))
}
/// 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<Value, VmError> {
// 已有只读续执行保留活跃作用域,禁止另开同步调用干扰它。
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()
.map(|frame| ExecutionLocation {
function: frame.name.clone(),
filename: frame.executable.filename().into(),
first_line: frame.executable.first_line(),
instruction_offset: frame.current_offset,
})
.collect();
}
// 逐帧回收栈、局部、函数和返回约束根,不把 Rust drop 误当托管 release。
while let Some(frame) = frames.pop() {
self.clear_frame(frame);
}
result
}
/// 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,
arguments: &[Value],
limits: ExecutionLimits,
slice: usize,
) -> Result<ExecutionProgress, VmError> {
// 拒绝零量子,避免调度者永远不推进且仍报告 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);
}
};
Ok(ExecutionProgress::Complete {
value,
dependencies: reads.dependencies,
reads: reads.objects,
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,
});
// 首片边界从零计,调用准备已用计数也占首片软预算,不另赠一片。
self.resume_read_only_to(slice)
}
// 出错分支在返回前处理本分支拥有的结果/作用域,不能保留半完成状态。
Err(error) => {
self.objects.end_read_only(scope)?;
Err(error)
}
}
}
/// 调度者判断是否有只读续执行,不表示托管协程数量或等待状态。
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<ExecutionProgress, VmError> {
// 拒绝零量子,避免调度者永远不推进且仍报告 Pending。
if slice == 0 {
return Err(VmError::InvalidArgument("execution slice must be positive"));
}
// 读取此前累计计数,新增软边界以 saturating_add 防整数绕回。
let executed = self
.suspended_call
.as_ref()
.ok_or(VmError::InvalidArgument("no suspended call"))?
.executed;
self.resume_read_only_to(executed.saturating_add(slice))
}
/// start/resume 共用的累计软边界执行器;Pending 原样放回状态,终态关闭只读域。
fn resume_read_only_to(&mut self, slice_end: usize) -> Result<ExecutionProgress, VmError> {
// 暂时取走续执行状态独占推进;只有 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,
&mut call.executed,
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
.iter()
.map(|frame| ExecutionLocation {
function: frame.name.clone(),
filename: frame.executable.filename().into(),
first_line: frame.executable.first_line(),
instruction_offset: frame.current_offset,
})
.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,
dependencies: reads.dependencies,
reads: reads.objects,
instructions: call.executed,
}),
// 已产生的成功结果此时不能交付,归还其对象根避免泄漏。
(Ok(Some(value)), Err(error)) => {
self.drop_root(value);
Err(error)
}
(Err(error), _) => Err(error),
(Ok(None), _) => unreachable!(),
}
}
/// Release all retained execution roots and close the read-only scope.
/// 宿主放弃当前只读续执行,逐帧释放根并关闭作用域,无任务时返回 false。
pub fn cancel_read_only_call(&mut self) -> Result<bool, VmError> {
// 没有续执行是正常无操作;有任务则一次取走,后续取消不会重复释放。
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);
}
self.objects.end_read_only(call.scope)?;
Ok(true)
}
/// Prepare a managed independently resumable call. Native delegation may
/// run during preparation; source instructions start only on resume.
/// The returned handle owns one external reference, like every allocation.
/// 宿主任务系统准备独立可恢复调用,返回一份协程拥有根;原生准备可发生在首次 resume 之前。
pub fn new_coroutine(
&mut self,
function: ObjectRef,
arguments: &[Value],
limits: ExecutionLimits,
) -> Result<ObjectRef, VmError> {
// 先拒绝只读上下文,避免用托管协程绕过活跃只读保护。
self.check_coroutine_entry()?;
// 本次真正执行前清理旧诊断,错误才记录新的帧位置。
self.last_traceback.clear();
// 首次调用准备也计入累计预算,后续恢复不能清零。
let mut executed = 0;
// prepare_call 可能直接完成原生调用或返回已持根的源码帧。
let prepared = self.prepare_call(function, arguments.to_vec(), &mut executed, limits)?;
// 源码帧暂存 Frames,立即结果暂存 Ready;两者尚持有执行根待转换。
let state = match prepared {
// 新源码帧先核对硬帧上限,零预算必须清理已取得的帧根。
PreparedCall::Frame(frame) => {
// 构造执行帧不等于已获执行许可,失败归还其全部拥有根。
if limits.max_frames == 0 {
self.clear_frame(frame);
return Err(VmError::ExecutionLimit("frames"));
}
CoroutineState::Frames(std::sync::Arc::new(vec![frame]))
}
PreparedCall::Value(value) => CoroutineState::Ready(value),
};
// 先建立托管载荷强边,再归还准备阶段根,确保转换期间对象始终存活。
// 把准备状态包装为托管载荷,trace 会为其保留与帧根一致的内部强边。
let coroutine = Coroutine {
state,
limits,
executed,
};
// 枚举每一份根身份,重复项表示多份所有权,不能去重。
let mut roots = Vec::new();
// 先快照原执行根,分配后载荷内部边接管这些引用。
coroutine.trace(&mut |reference| roots.push(reference));
// 分配成功先建立内部边,失败则没有托管接管者;两种情况都要归还原执行根。
let result = self.allocate(coroutine);
// 逐项归还原执行根;成功路径已由载荷持边,错误路径则释放准备状态资源。
for root in roots {
self.release(root)?;
}
result
}
/// 创建/恢复/取消共用门禁,禁止在全局只读续执行或其他只读域内进行协程操作。
fn check_coroutine_entry(&self) -> Result<(), VmError> {
// 可变协程不持有全局只读域,也不能干扰 VM 已停放的只读调用。
if self.has_suspended_call() || self.objects.is_read_only() {
return Err(VmError::UnsupportedOperation(
"coroutine operation inside read-only execution",
));
}
Ok(())
}
/// Resume one managed call, preserving cumulative hard limits. Other calls
/// and heap writes may execute between Pending results. No prior instruction
/// is replayed. Completion returns an owned value and consumes the frames.
/// 宿主推进一个协程量子,先临时保留协程自身,防回调移除其最后外部来源。
pub fn resume_coroutine(
&mut self,
reference: ObjectRef,
slice: usize,
) -> Result<CoroutineProgress, VmError> {
// 临时拥有根覆盖整个恢复,包括原生回调可能修改持有协程的对象图。
self.retain(reference)?;
// 保存执行结果,使错误返回也经过下方协程临时根释放。
let result = self.resume_coroutine_inner(reference, slice);
// 归还仅由本次推进取得的保护根,不消耗调用者原有根。
self.release(reference)?;
result
}
/// 恢复状态、把载荷边转为执行根,执行后再停放或提交 Finished;外层负责保护协程本身。
fn resume_coroutine_inner(
&mut self,
reference: ObjectRef,
slice: usize,
) -> Result<CoroutineProgress, VmError> {
// 先拒绝只读上下文,避免用托管协程绕过活跃只读保护。
self.check_coroutine_entry()?;
// 软量子必须正数,否则没有可验证的推进工作。
if slice == 0 {
return Err(VmError::InvalidArgument("execution slice must be positive"));
}
// 借用并校验现有协程载荷,读取其硬预算和停放状态;此处尚未替换载荷。
let coroutine = self.payload::<Coroutine>(reference, "coroutine")?;
// 硬限制来自创建时状态,不能在每个量子重新给予预算。
let limits = coroutine.limits;
// 从上次停放的累计计数继续,而不是从零执行。
let mut executed = coroutine.executed;
// 只复制状态表示/Arc,不因 clone 增加托管计数;先由旧载荷边保证存活。
let state = match &coroutine.state {
CoroutineState::Frames(frames) => CoroutineState::Frames(frames.clone()),
CoroutineState::Ready(value) => CoroutineState::Ready(value.clone()),
_ => return Err(VmError::InvalidArgument("coroutine is running or finished")),
};
// 枚举每一份根身份,重复项表示多份所有权,不能去重。
let mut roots = Vec::new();
// 列出恢复为活跃执行时所需全部根;Ready 标量没有对象根。
match &state {
// 帧身份沿同一 trace 清单转换,不复制执行行为或重新绑定实参。
CoroutineState::Frames(frames) => {
// 复用 frame::trace_roots,确保与活跃帧清理清单完全对应。
for frame in frames.iter() {
frame.trace_roots(&mut |root| roots.push(root));
}
}
CoroutineState::Ready(Value::Object(root)) => roots.push(*root),
_ => {}
}
// 恢复时方向相反:先取得每份执行根,再替换旧载荷以释放内部边;重复边对应重复根。
// 事务式取得新执行根;中途失败仅归还本轮已成功 retain 的部分。
let mut retained = Vec::new();
// 逐项取得恢复所需执行根,旧载荷在全部取得成功前保持不变。
for root in roots {
// 任何一个根无法保留就不替换旧载荷,原停放状态仍有效。
if let Err(error) = self.retain(root) {
// 归还本轮新加根,不释放仍属于旧载荷的内部边。
for root in retained {
self.release(root)?;
}
return Err(error);
}
retained.push(root);
}
// 状态替换也可能失败,调用方必须清理已保留根或尚未交付的结果。
if let Err(error) = self.replace_payload(
reference,
Coroutine {
state: CoroutineState::Running,
limits,
executed,
},
) {
// 归还本轮新加根,不释放仍属于旧载荷的内部边。
for root in retained {
self.release(root)?;
}
return Err(error);
}
// 本次真正执行前清理旧诊断,错误才记录新的帧位置。
self.last_traceback.clear();
// Running 替换释放旧载荷 Arc 后,快照 Arc 应成为唯一所有者,可取回帧向量。
let mut frames = match state {
// 帧身份沿同一 trace 清单转换,不复制执行行为或重新绑定实参。
CoroutineState::Frames(frames) => {
std::sync::Arc::try_unwrap(frames).expect("private frame storage has one owner")
}
// 准备阶段已完成,直接提交 Finished,再把新取得的结果根交给调用者。
CoroutineState::Ready(value) => {
// 状态替换也可能失败,调用方必须清理已保留根或尚未交付的结果。
if let Err(error) = self.replace_payload(
reference,
Coroutine {
state: CoroutineState::Finished,
limits,
executed,
},
) {
self.drop_root(value);
return Err(error);
}
return Ok(CoroutineProgress::Complete {
value,
instructions: executed,
});
}
_ => unreachable!(),
};
// 软边界按累计计数增加,saturating_add 防边界绕回,硬上限仍独立检查。
let slice_end = executed.saturating_add(slice);
// 允许 await 停放;None 既可能是软量子结束,也可能等待对象 Pending。
let result = self.eval_frames(&mut frames, limits, &mut executed, slice_end, true);
// 需要停放:先分析是否 await,再把完整帧/计数提交回托管载荷。
if let Ok(None) = result {
// waiting 只取最内层帧等待对象;栈顶操作数留存,恢复时不会重放创建它的调用。
let waiting = frames.last().filter(|frame| frame.waiting).map(|frame| {
// 已执行 Await 证明栈顶是对象,否则属于执行器内部不变量破坏。
match Self::top_value(frame).expect("await operand remains on stack") {
Value::Object(reference) => reference,
_ => unreachable!(),
}
});
// 把帧向量重新装进共享快照,内部边接管前仍持原执行根。
let frames = std::sync::Arc::new(frames);
// 枚举每一份根身份,重复项表示多份所有权,不能去重。
let mut roots = Vec::new();
// 复用 frame::trace_roots,确保与活跃帧清理清单完全对应。
for frame in frames.iter() {
frame.trace_roots(&mut |root| roots.push(root));
}
// 先提交 Frames 与完整强边,发布成功前不能释放原执行根造成悬空。
let published = self.replace_payload(
reference,
Coroutine {
state: CoroutineState::Frames(frames),
limits,
executed,
},
);
// 逐项归还原执行根;成功路径已由载荷持边,错误路径则释放准备状态资源。
for root in roots {
self.release(root)?;
}
// 归还原执行根后传播发布错误;失败不会交付虚假的 Pending 状态。
published?;
// Waiting 只返回借用等待身份,图由停放协程帧维持,调度者须保留协程根。
if let Some(awaitable) = waiting {
return Ok(CoroutineProgress::Waiting {
awaitable,
instructions: executed,
});
}
return Ok(CoroutineProgress::Pending {
instructions: executed,
});
}
// 在帧清理前提取回溯,保存 current_offset 而非下一指令位置。
if result.is_err() {
self.last_traceback = frames
.iter()
.map(|frame| ExecutionLocation {
function: frame.name.clone(),
filename: frame.executable.filename().into(),
first_line: frame.executable.first_line(),
instruction_offset: frame.current_offset,
})
.collect();
}
// 终态清理所有剩余活跃帧根,成功返回值已先从栈中移出。
while let Some(frame) = frames.pop() {
self.clear_frame(frame);
}
// 无论成功或失败都清空运行状态,保留预算/计数供诊断,Finished 不再持帧图。
let finished = self.replace_payload(
reference,
Coroutine {
state: CoroutineState::Finished,
limits,
executed,
},
);
// 成功必须同时完成状态提交;提交失败归还结果,执行错误路径仍须处理收尾结果。
match result {
// 结果拥有根仅在 Finished 提交成功后移交给宿主。
Ok(Some(value)) => {
// 结果已产生但状态提交失败,不能泄漏该结果根。
if let Err(error) = finished {
self.drop_root(value);
return Err(error);
}
Ok(CoroutineProgress::Complete {
value,
instructions: executed,
})
}
// 此处先确保 Finished 发布成功,再返回原执行错误。
Err(error) => {
finished?;
Err(error)
}
Ok(None) => unreachable!(),
}
}
/// Cancellation releases managed frame edges; dropping an unreachable
/// coroutine has the same ownership effect, including cycles through locals.
/// Writes performed by previous quanta are never rolled back.
/// 宿主取消停放帧或准备结果,用 Finished 替换以释放载荷边;不回滚此前写入。
pub fn cancel_coroutine(&mut self, reference: ObjectRef) -> Result<bool, VmError> {
// 先拒绝只读上下文,避免用托管协程绕过活跃只读保护。
self.check_coroutine_entry()?;
// 借用并校验现有协程状态,先排除正在执行或已结束的情况,再提交取消。
let coroutine = self.payload::<Coroutine>(reference, "coroutine")?;
// 正执行状态拒绝取消,避免在 Rust 回调重入时移除执行者状态。
if matches!(coroutine.state, CoroutineState::Running) {
return Err(VmError::InvalidArgument("coroutine is running"));
}
// 重复取消是正常 false,不再修改载荷版本。
if matches!(coroutine.state, CoroutineState::Finished) {
return Ok(false);
}
// 取消后仍保留创建时硬限制元数据,只清理帧/结果拥有边,不创建新执行预算。
let limits = coroutine.limits;
// 取消保留累计诊断计数,不把已经执行的副作用或预算消耗抹除。
let executed = coroutine.executed;
self.replace_payload(
reference,
Coroutine {
state: CoroutineState::Finished,
limits,
executed,
},
)?;
Ok(true)
}
/// Outer-to-inner execution locations from the last failed call/execute.
/// These are copied metadata, not heap references or language exceptions.
/// 宿主错误报告借用最近记录的复制位置元数据,不保留故障对象图。
/// 仅源码帧执行失败路径生成回溯,参数准备或入口拒绝等错误不保证产生新的非空条目。
pub fn last_traceback(&self) -> &[ExecutionLocation] {
&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(),
required,
maximum,
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 {
None
}
})
.and_then(|value| match value {
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(),
position: format!("parameter {}", executable.parameters()[index]),
expected: expected_name.clone(),
actual: self.objects.value_type_name(value)?,
});
}
}
// 先解析返回约束,随后额外保留其定义根,防执行期间全局重绑定使其失效。
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(),
function,
previous: None,
code: data.code(),
globals: data.globals(),
builtins: data.builtins(),
name: data.name().into(),
instruction_offset: 0,
current_offset: 0,
waiting: false,
localsplus: vec![None; executable.slot_count()],
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,
NativeReturn { name, expected },
)?;
}
}
// 已传参数会覆盖默认值前段,仅拼接尚缺的尾部默认值。
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());
}
// 捕获局部保留原局部槽位置但改存 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);
}
Ok(frame)
}
/// prepare_call 分派非源码载荷:上下文回调交 NativeContext,旧式回调自行验证并保留返回根。
fn invoke_native(
&mut self,
function: ObjectRef,
arguments: &[Value],
) -> Result<NativeResult, VmError> {
// 先验证当前函数句柄,不让失效/异堆身份进入原生协议。
self.object(function)?;
// 参数起初是借用,仅验证有效性;此循环本身不取得拥有根。
for argument in arguments {
self.validate_value(argument)?;
}
// 对象首参可能是方法接收者,普通覆盖和显式能力检查不可只靠原生数量约束。
if let Some(Value::Object(receiver)) = arguments.first() {
self.objects.check_method_contract_call(
function,
*receiver,
self.source_type_checks,
)?;
}
// 已发布的数量区间必须满足,缺少元数据不在此处猜测数量。
if let Some(arity) = self.objects.call_arity(function)?
&& !arity.accepts(arguments.len())
{
return Err(VmError::ArgumentCount {
name: self.object(function)?.payload().kind_name().into(),
required: arity.required,
maximum: arity.maximum,
provided: arguments.len(),
});
}
// 克隆到的分派器 Arc 不借用载荷,可在 NativeContext 中安全可变访问 Store。
if let Some(callable) = self.object(function)?.payload().native_callable() {
return NativeContext::execute(&mut self.objects, function, arguments, callable);
}
// 无上下文旧式协议读取完整签名;有签名才检查参数/效果及结果。
let signature = self.objects.native_signature(function)?;
// 复制诊断类别名,不在执行回调后依赖之前的载荷借用。
let name = self.object(function)?.payload().kind_name().to_owned();
// 完整签名存在才执行相应参数或返回检查,缺失时维持旧协议行为。
if let Some(signature) = &signature {
signature.check_arguments(&self.objects, &name, arguments)?;
}
// 旧式回调返回借用值,不能立即作为拥有结果交给宿主。
let result = self.object(function)?.payload().call(arguments)?;
// 完整签名存在才执行相应参数或返回检查,缺失时维持旧协议行为。
if let Some(signature) = &signature {
signature.check_return(&self.objects, &name, &result)?;
}
// 验证并取得结果根,即使回调原样返回某个参数,也必须给结果独立所有权。
self.retain_value(&result)?;
Ok(NativeResult::Return(result))
}
/// 同步调用、只读续执行、协程及 Call 指令共用准备循环,原生委托不递归进入求值器。
fn prepare_call(
&mut self,
mut function: ObjectRef,
mut arguments: Vec<Value>,
executed: &mut usize,
limits: ExecutionLimits,
) -> Result<PreparedCall, VmError> {
// 最初函数/实参借用;第一次 Invoke 后才接管 NativeContext 移交的函数/参数根。
let mut owned = false;
// 累计原生/能力返回约束,后续委托每一层完成都仍须满足这些要求。
let mut returns = Vec::new();
// 闭包把委托循环的提前错误汇总,外层统一清理尚未移交的约束与输入根。
let result = (|| loop {
// 源码载荷交给 prepare_frame,不走原生调用协议或 Rust 递归执行。
if self
.object(function)?
.payload()
.as_any()
.is::<EndorFunctionObject>()
{
// 源码帧取得自己的函数/参数根,后面归还委托输入根不会使帧失效。
let mut frame = self.prepare_frame(function, &arguments)?;
// 暂取帧本地能力返回约束,将外层原生委托约束放在前面。
let mut local = std::mem::take(&mut frame.native_returns);
// 把累计约束所有权移入源码帧,外层 returns 变空,避免统一清理重复归还。
frame.native_returns = std::mem::take(&mut returns);
// 本地约束追加在外层之后,最终返回时按反序检查。
frame.native_returns.append(&mut local);
return Ok(PreparedCall::Frame(frame));
}
// 每次原生/委托分派前检查累计硬预算,避免无限原生委托链绕过指令限制。
if *executed >= limits.max_instructions {
return Err(VmError::ExecutionLimit("instructions"));
}
*executed += 1;
// 非 Any 返回约束需要跨委托保留,不能只校验中间包装器直接返回值。
if let Some(signature) = self.objects.native_signature(function)?
&& !signature.returns.is_any()
{
// 诊断名与解析后的返回类型一并保存,不在最终返回时重查可能被重绑定的全局。
let check = NativeReturn {
name: self.object(function)?.payload().kind_name().into(),
expected: signature.returns,
};
self.push_native_return(&mut returns, check)?;
}
// 对象首参可能是方法接收者,普通覆盖和显式能力检查不可只靠原生数量约束。
if let Some(Value::Object(receiver)) = arguments.first() {
// 有效接收者的全部能力返回约束同样跨委托保留。
for (name, expected) in self.objects.method_trait_returns(function, *receiver)? {
self.push_native_return(&mut returns, NativeReturn { name, expected })?;
}
}
// 结果已经拥有:Return 的结果根或 Invoke 的函数/各参数根由本循环接管。
match self.invoke_native(function, &arguments)? {
// 最终直接结果要通过委托链所有返回检查后才可交付。
NativeResult::Return(value) => {
// 从最内层到外层校验,每项约束都必须满足。
for check in returns.iter().rev() {
// 检查失败先归还本次结果根,再让外层清理链上剩余资源。
if let Err(error) = check.check(&self.objects, &value) {
self.drop_root(value);
return Err(error);
}
}
return Ok(PreparedCall::Value(value));
}
NativeResult::Invoke {
function: next,
arguments: values,
} => {
// 只有已接管 Invoke 输入时才归还当前函数/实参,初始借用输入不能释放。
if owned {
self.release(function)?;
// 先清空旧拥有参数列表并逐项归还,随后安装新委托输出。
for value in arguments.drain(..) {
self.release_value(value)?;
}
}
// 新委托函数/参数根已经由 NativeContext 保留,赋值只转交清理责任。
function = next;
arguments = values;
// 从此循环退出时须统一归还当前委托输入;返回结果或源码帧各自已保活。
owned = true;
}
}
})();
// 直接完成/错误清理仍在本地的约束,已移入源码帧的那部分不再在此表中。
self.clear_native_returns(returns);
// 只有已接管 Invoke 输入时才归还当前函数/实参,初始借用输入不能释放。
if owned {
self.release(function)
.expect("delegated call owns its function");
// 循环结束时归还最后一批拥有参数,重复对象参数逐项计数。
for value in arguments {
self.drop_root(value);
}
}
result
}
/// 委托链保留返回约束定义根,全部 retain 成功后才追加检查项,中途失败回滚已保留部分。
fn push_native_return(
&mut self,
returns: &mut Vec<NativeReturn>,
check: NativeReturn,
) -> Result<(), VmError> {
// 先记成功 retain 的定义身份,未全部成功前不提交检查项。
let mut retained = Vec::new();
// 枚举联合返回类型内全部定义引用,重复引用不能去重。
for reference in check.expected.references() {
// 后续定义根失败时,归还此前成功取得的根,不留下半安装约束。
if let Err(error) = self.retain(reference) {
// 回滚只处理本次取得的根,不触碰原签名载荷拥有边。
for reference in retained {
self.release(reference)?;
}
return Err(error);
}
retained.push(reference);
}
// 全部定义根已取得,追加检查项后由返回链/帧负责清理。
returns.push(check);
Ok(())
}
/// 委托结束/帧销毁统一归还返回约束定义根,每个引用按拥有次数独立 release。
fn clear_native_returns(&mut self, returns: Vec<NativeReturn>) {
// 销毁每份检查项持有的定义根,Rust CallType 的 drop 不会自动 release。
for check in returns {
// 枚举联合返回类型内全部定义引用,重复引用不能去重。
for reference in check.expected.references() {
self.release(reference)
.expect("native return check owns its type");
}
}
}
/// 各操作码按下标读取语言值,返回 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(
frame.current_offset,
"NULL call marker used as a value",
)),
}
}
/// 操作码读取栈顶语言值的快捷入口;栈非空由代码验证保证。
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)?)
{
return Ok(Value::Object(*old));
}
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)
}
_ => Err(invalid(
frame.current_offset,
"cell slot is not initialized",
)),
}
}
/// ToBool/UnaryNot/条件跳转共用真值规则,直接值就地判断,对象交载荷协议。
fn value_is_true(&self, value: &Value) -> Result<bool, VmError> {
// 直接数值/字符串与对象载荷分开处理,协议错误不被当成 false。
match value {
Value::Nil => Ok(false),
Value::Bool(value) => Ok(*value),
Value::Text(value) => Ok(!value.is_empty()),
Value::Number(value) => Ok(!value.is_zero()),
// 对象真值走 Store 的载荷协议;该底层入口当前不自动登记对象版本依赖。
Value::Object(reference) => self.is_true(*reference),
}
}
/// 比较把直接 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
.object(*reference)?
.payload()
.as_any()
.downcast_ref::<EndorStringObject>()
.map(|text| text.as_str().to_owned()),
_ => None,
})
}
/// 比较/算术将直接 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
.object(*o)?
.payload()
.as_any()
.downcast_ref::<crate::EndorNumberObject>()
.map(|n| n.value()),
_ => None,
})
}
/// 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),
CompareOp::Ne => Ok(true),
_ => 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,
CompareOp::Ne => left != right,
CompareOp::Lt => left < right,
CompareOp::Le => left <= right,
CompareOp::Gt => left > right,
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),
_ => Err(VmError::UnsupportedOperation("ordering")),
}
}
/// 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);
}
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<_>, _>>()?;
// 所有输入根在验证并建立函数内部强边前一直留栈,失败时仍由帧统一清理。
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()
.map(|(name, type_name)| match (name, type_name) {
(Value::Text(name), Value::Text(type_name)) => Ok(crate::ClassField {
name: name.clone(),
type_name: type_name.clone(),
}),
_ => Err(VmError::ObjectKindMismatch("class field annotation")),
})
.collect::<Result<Vec<_>, _>>()?;
// 方法键须文本、值须对象,收集为名称有序表,仍不额外保留函数根。
let methods = self
.dict_items(methods)?
.iter()
.map(|(name, function)| match (name, function) {
(Value::Text(name), Value::Object(function)) => Ok((name.clone(), *function)),
_ => Err(VmError::ObjectKindMismatch("class method")),
})
.collect::<Result<std::collections::BTreeMap<_, _>, _>>()?;
self.objects
.new_class_with_scope(name, base, fields, methods, Some(frame.function))
}
/// 所有同步/切片/协程入口共用的显式帧循环:Some 是拥有结果,None 是软预算或允许的 await 停放。
/// 错误不在本函数销毁全部帧,由外层保存回溯再统一清理;指令索引/栈高已在代码构造时验证。
fn eval_frames(
&mut self,
frames: &mut Vec<EndorFrame>,
limits: ExecutionLimits,
executed: &mut usize,
slice_end: usize,
allow_wait: bool,
) -> Result<Option<Value>, VmError> {
// 每轮只推进当前最内层帧,源码调用通过帧向量增长而非 Rust 递归。
loop {
// 硬上限先于软边界检查,同点到达硬上限必须报错,不能继续 Pending。
if *executed >= limits.max_instructions {
return Err(VmError::ExecutionLimit("instructions"));
}
// 软量子结束保留帧、栈和指令指针,外层决定如何停放。
if *executed >= slice_end {
return Ok(None);
}
// 实际取指前累计一次,原生委托准备另在 prepare_call 计数。
*executed += 1;
// 当前帧必须存在;源码 Return 最外层时会提前返回结果退出循环。
let frame = frames.last_mut().expect("an evaluation frame is active");
// 按完整逻辑指令起点取指,扩展前缀已合并,非法指针返回位置错误。
let decoded = frame.executable.instruction_at(frame.instruction_offset)?;
// 保存当前起点给错误诊断,同时预置顺序后继;跳转/等待会覆盖下一位置。
frame.current_offset = decoded.offset;
frame.instruction_offset = decoded.next_offset;
// 本轮操作种类,后面的栈和槽更新按此分派。
let opcode = decoded.instruction.opcode;
// 完整 u32 操作数转为索引/数量,范围合法性由代码验证保证。
let argument = decoded.instruction.argument as usize;
// 每条指令成功后保持根计数与栈/槽一致,错误交外层清理当前帧。
match opcode {
Opcode::Await => {
// 只读调用不允许 await,不可用等待期间外部修改破坏只读快照。
if self.objects.is_read_only() {
return Err(VmError::UnsupportedOperation(
"await during read-only execution",
));
}
// 等待协议仅对象可实现,直接 Nil/数字/文本都不能 await。
let Value::Object(awaitable) = Self::top_value(frame)? else {
return Err(VmError::ObjectKindMismatch("awaitable object"));
};
// poll_await 非阻塞观察当前状态,不重跑生成等待对象的表达式。
match self.object(awaitable)?.payload().poll_await()? {
// 操作数根继续留栈,协程状态稍后接管;同步入口不允许这种停放。
crate::AwaitPoll::Pending => {
// 仅可变协程支持 Pending await,同步调用不能悄悄变为暂停。
if !allow_wait {
return Err(VmError::UnsupportedOperation(
"pending await outside a coroutine",
));
}
// 保留已求值等待操作数,指针回到 Await 本身;不会重放此前调用或语句。
frame.waiting = true;
frame.instruction_offset = decoded.offset;
return Ok(None);
}
// 先取得结果根再释放等待操作数,避免结果仅由等待对象图持有时失效。
crate::AwaitPoll::Ready(value) => {
self.retain_value(&value)?;
self.discard(frame, 1);
frame.stack.push(StackEntry::Value(value));
frame.waiting = false;
}
}
}
// 无操作,只消耗本轮预算并继续预置后继。
Opcode::Nop => {}
Opcode::LoadConst => {
// clone 仍为借用表示,push_borrowed 为结果栈项取得独立根。
let value = frame.executable.constants()[argument].clone();
self.push_borrowed(frame, value)?;
}
Opcode::LoadFast => {
// 未绑定 None 报具名局部错误,已绑定值压栈时另保留根。
let value = frame.localsplus[argument].clone().ok_or_else(|| {
VmError::UnboundLocal(frame.executable.slot_name(argument).into())
})?;
self.push_borrowed(frame, value)?;
}
// 局部赋值先保留新槽根,再释放原栈输入及被替换旧槽根。
Opcode::StoreFast => {
// 先执行共享引用赋值选择,可能保留旧对象身份,再按目标槽/字典协议提交。
let value = self.assignment_value(
frame.localsplus[argument].clone(),
Self::top_value(frame)?,
)?;
self.retain_value(&value)?;
self.discard(frame, 1);
// 新值先保留/写入,最后归还被替换的旧槽根,即使新旧同身份也安全。
if let Some(old) = frame.localsplus[argument].replace(value) {
self.drop_root(old);
}
}
Opcode::DeleteFast => {
// 先移走槽拥有值,使局部恢复 None,再归还旧根。
let value = frame.localsplus[argument].take().ok_or_else(|| {
VmError::UnboundLocal(frame.executable.slot_name(argument).into())
})?;
self.drop_root(value);
}
Opcode::LoadGlobal => {
// 从代码 names 表借用名称,索引不来自任意运行时文本。
let name = &frame.executable.layout().names[argument];
// 按名记录命中/缺失,只在全局缺失时查询捕获的内置字典。
let mut value = self.objects.dict_get_name(frame.globals, name)?;
// Some(Nil) 也是命中,不会回退内置空间。
if value.is_none()
&& let Some(builtins) = frame.builtins
{
value = self.objects.dict_get_name(builtins, name)?;
}
// 全局和可选内置均未命中才报 NameNotFound。
let value = value.ok_or_else(|| VmError::NameNotFound(name.clone()))?;
self.push_borrowed(frame, value)?;
}
// 全局修改由字典拥有新值内部边,成功后释放输入栈根。
Opcode::StoreGlobal => {
// 全局写入使用直接文本键,与名称读取协议对应。
let key = Value::Text(frame.executable.layout().names[argument].clone());
// 先执行共享引用赋值选择,可能保留旧对象身份,再按目标槽/字典协议提交。
let value = self.assignment_value(
self.dict_get(frame.globals, &key)?,
Self::top_value(frame)?,
)?;
self.dict_set(frame.globals, key, value)?;
self.discard(frame, 1);
}
Opcode::DeleteGlobal => {
// 从代码 names 表借用名称,索引不来自任意运行时文本。
let name = &frame.executable.layout().names[argument];
// 删除缺键报告名称错误,字典删除协议其他错误正常传播。
if !self.dict_delete(frame.globals, &Value::Text(name.clone()))? {
return Err(VmError::NameNotFound(name.clone()));
}
}
Opcode::LoadDeref | Opcode::LoadClosure => {
// 对应槽必须存活且为 Cell,本帧与闭包函数均可能持有其根/边。
let cell = self.cell_slot(frame, argument)?;
// LoadClosure 压 Cell 本身,LoadDeref 压当前内容,两者都经 push_borrowed 取得栈根。
let value = if opcode == Opcode::LoadClosure {
Value::Object(cell)
} else {
self.cell_get(cell).map_err(|error| {
// 语言层将空捕获槽映射为变量未绑定,其余底层错误保持原样。
if error == VmError::EmptyCell {
VmError::UnboundLocal(frame.executable.slot_name(argument).into())
} else {
error
}
})?
};
self.push_borrowed(frame, value)?;
}
// 空 Cell 读取经 ok() 视作没有旧值,写入成功后才弹出输入。
Opcode::StoreDeref => {
// 对应槽必须存活且为 Cell,本帧与闭包函数均可能持有其根/边。
let cell = self.cell_slot(frame, argument)?;
// 读取/计算下一状态后再更新栈,失败由原帧保留输入清理责任。
let value =
self.assignment_value(self.cell_get(cell).ok(), Self::top_value(frame)?)?;
self.cell_set(cell, value)?;
self.discard(frame, 1);
}
Opcode::DeleteDeref => {
// 对应槽必须存活且为 Cell,本帧与闭包函数均可能持有其根/边。
let cell = self.cell_slot(frame, argument)?;
// 当前实现将读取错误视为未绑定;上方 cell_slot 已检查槽载荷。
if self.cell_get(cell).is_err() {
return Err(VmError::UnboundLocal(
frame.executable.slot_name(argument).into(),
));
}
self.cell_clear(cell)?;
}
// 丢弃一个拥有栈项,协议 Null 则不 release。
Opcode::PopTop => self.discard(frame, 1),
Opcode::Copy => {
// Copy 复制表示不自动 retain,对象项下方另取新栈根,Null 可直接复制。
let entry = frame.stack[frame.stack.len() - argument].clone();
// 协议 Null 没有对象根;普通对象项复制时须取得一份新根。
if let StackEntry::Value(value) = &entry {
self.retain_value(value)?;
}
frame.stack.push(entry);
}
Opcode::Swap => {
// Swap 仅交换现有拥有槽,不增加或释放任何根。
let length = frame.stack.len();
frame.stack.swap(length - 1, length - argument);
}
// 压入专用调用标记,不能与 Value::Nil 混淆。
Opcode::PushNull => frame.stack.push(StackEntry::Null),
Opcode::Call => {
// 调用布局是函数、receiver/Null、显式参数,argument 只计显式实参。
let start = frame.stack.len() - argument - 2;
// 调用目标必须为对象,是否可调用由 prepare_call/载荷协议继续判定。
let Value::Object(function) = Self::stack_value(frame, start)? else {
return Err(VmError::UnsupportedOperation("call"));
};
// 先收集借用参数,原栈输入直到调用准备完成前保持根保护。
let mut arguments = Vec::with_capacity(argument + 1);
// 非 Null 的协议接收者先加入实际参数,Null 则不传入语言实参。
if let StackEntry::Value(value) = &frame.stack[start + 1] {
arguments.push(value.clone());
}
// 后续显式参数按源码顺序传入,不在此处归还原栈根。
for index in start + 2..frame.stack.len() {
arguments.push(Self::stack_value(frame, index)?);
}
// 准备成功得到已持根源码帧或拥有结果,之后才能清理当前帧调用输入。
match self.prepare_call(function, arguments, executed, limits)? {
// 新帧还未加入向量,若深度预算不足必须主动清理它。
PreparedCall::Frame(mut callee) => {
// 硬帧限制包括现有所有源码帧,不把原生委托另算源码帧。
if frames.len() >= limits.max_frames {
self.clear_frame(callee);
return Err(VmError::ExecutionLimit("frames"));
}
// 记录调用者索引,无裸指针,不受向量扩容影响。
callee.previous = Some(frames.len() - 1);
self.discard(frames.last_mut().unwrap(), argument + 2);
frames.push(callee);
}
// 原生直接结果已经拥有根,清除调用输入后直接移入栈,不再 retain。
PreparedCall::Value(value) => {
self.discard(frames.last_mut().unwrap(), argument + 2);
frames
.last_mut()
.unwrap()
.stack
.push(StackEntry::Value(value));
}
}
}
Opcode::ReturnValue => {
// 返回值先借用读取,实际根在后面 pop 时从帧栈移交,不能提前 release。
let result = Self::top_value(frame)?;
// 源码返回注解已解析并保活,执行中全局重绑定不能替换该约束。
if let Some(expected) = frame.return_type
&& !self.objects.matches_annotation(&result, expected)?
{
return Err(VmError::TypeMismatch {
function: frame.name.clone(),
position: "return".into(),
expected: frame
.executable
.source_signature()
.expect("checked source signature")
.return_type
.clone(),
actual: self.objects.value_type_name(&result)?,
});
}
// 所有委托返回约束都要满足,按最内层到外层顺序检查。
for check in frame.native_returns.iter().rev() {
check.check(&self.objects, &result)?;
}
// 仅移出结果项不 release,将它原有的栈根转交调用者/外层结果。
frame.stack.pop();
self.clear_frame(frames.pop().unwrap());
// 还有调用者则接管结果根到其栈;最外层直接移交给宿主。
if let Some(caller) = frames.last_mut() {
caller.stack.push(StackEntry::Value(result));
} else {
return Ok(Some(result));
}
}
// 无条件跳转仅改变下一位置,不改变栈高度或引用计数。
Opcode::JumpForward | Opcode::JumpBackward => {
frame.instruction_offset = decoded.jump_target()?
}
Opcode::PopJumpIfFalse | Opcode::PopJumpIfTrue => {
// 先求真再丢弃输入根,协议错误时输入仍在帧中供外层清理。
let truth = self.value_is_true(&Self::top_value(frame)?)?;
self.discard(frame, 1);
// 条件成立才覆盖预置顺序后继,跳转距离以代码单元计算。
if truth == (opcode == Opcode::PopJumpIfTrue) {
frame.instruction_offset = decoded.jump_target()?;
}
}
Opcode::ToBool | Opcode::UnaryNot => {
// 先求真再丢弃输入根,协议错误时输入仍在帧中供外层清理。
let truth = self.value_is_true(&Self::top_value(frame)?)?;
self.discard(frame, 1);
frame.stack.push(StackEntry::Value(Value::Bool(
// ToBool 保留真值,UnaryNot 取反,两者都输出直接 Bool。
if opcode == Opcode::UnaryNot {
!truth
} else {
truth
},
)));
}
Opcode::BinaryArithmetic => {
// 加法额外支持两个直接 Text 拼接,堆字符串不在此快速路径中。
if decoded.instruction.argument == 0 {
// 读取左输入借用表示,原栈根继续维持对象存活。
let left = Self::stack_value(frame, frame.stack.len() - 2)?;
// 读取右输入借用表示,不能先弹栈再访问对象载荷。
let right = Self::top_value(frame)?;
// 仅双直接文本走拼接,其他情况继续数值运算并校验 Number。
if let (Value::Text(a), Value::Text(b)) = (left, right) {
// 限制合并 UTF-8 字节数为 1 MiB,饱和相加防预算绕回。
if a.len().saturating_add(b.len()) > 1_048_576 {
return Err(VmError::InvalidArgument(
"text concatenation exceeds operation budget",
));
}
self.discard(frame, 2);
frame.stack.push(StackEntry::Value(Value::Text(a + &b)));
continue;
}
}
// 读取左输入借用表示,原栈根继续维持对象存活。
let left = Self::stack_value(frame, frame.stack.len() - 2)?;
// 读取右输入借用表示,不能先弹栈再访问对象载荷。
let right = Self::top_value(frame)?;
// 直接/堆数值统一为借用 Number;缺失表示报 Number 种类错误。
let left = self
.number_value(&left)?
.ok_or(VmError::ObjectKindMismatch("Number"))?;
// 右值同样要求数值,不能隐式把文本或 Bool 转数字。
let right = self
.number_value(&right)?
.ok_or(VmError::ObjectKindMismatch("Number"))?;
// 操作数映射已由代码验证,运算仍可因零除/预算/非有限值报错。
let result = left.arithmetic(
right,
crate::ArithmeticOp::from_argument(decoded.instruction.argument).unwrap(),
)?;
self.discard(frame, 2);
frame.stack.push(StackEntry::Value(Value::Number(result)));
}
Opcode::UnaryNegate => {
// 借用栈顶,原拥有根保留至协议运算或修改成功。
let value = Self::top_value(frame)?;
// 数值取负只接受直接/堆 Number,不丢弃其他类型信息做隐式转换。
let value = self
.number_value(&value)?
.ok_or(VmError::ObjectKindMismatch("Number"))?;
// 先成功计算再释放输入,输出直接 Number 自己拥有 Rust 数值数据。
let result = value.negated()?;
self.discard(frame, 1);
frame.stack.push(StackEntry::Value(Value::Number(result)));
}
Opcode::CompareOp => {
// 读取左输入借用表示,原栈根继续维持对象存活。
let left = Self::stack_value(frame, frame.stack.len() - 2)?;
// 读取右输入借用表示,不能先弹栈再访问对象载荷。
let right = Self::top_value(frame)?;
// 数学/文本/对象协议在辅助函数统一选择,结果是直接 Bool。
let result = self.compare_values(
&left,
&right,
Instruction::compare_operation(decoded.instruction.argument).unwrap(),
)?;
self.discard(frame, 2);
frame.stack.push(StackEntry::Value(Value::Bool(result)));
}
Opcode::LoadAttr => {
// 属性协议需要对象 owner,直接值不能假装有托管动态字段。
let Value::Object(owner) = Self::top_value(frame)? else {
return Err(VmError::ObjectKindMismatch("object"));
};
// 读取/计算下一状态后再更新栈,失败由原帧保留输入清理责任。
let value =
self.bind_attr(owner, &frame.executable.layout().names[argument])?;
self.discard(frame, 1);
frame.stack.push(StackEntry::Value(value));
}
Opcode::BuildClass => {
// 构类成功返回拥有根,之后才丢弃名称/基类/字段/方法输入四项。
let class = self.build_class(frame)?;
self.discard(frame, 4);
frame.stack.push(StackEntry::Value(Value::Object(class)));
}
Opcode::StoreAttr => {
// 属性协议需要对象 owner,直接值不能假装有托管动态字段。
let Value::Object(owner) = Self::top_value(frame)? else {
return Err(VmError::ObjectKindMismatch("object"));
};
// 读取/计算下一状态后再更新栈,失败由原帧保留输入清理责任。
let value = Self::stack_value(frame, frame.stack.len() - 2)?;
self.set_attr(
owner,
frame.executable.layout().names[argument].clone(),
value,
)?;
self.discard(frame, 2);
}
Opcode::BuildMap => {
// 字典构造取得根后,释放原键值栈根,由字典内部边接管持有。
let dictionary = self.build_map(frame, argument)?;
self.discard(frame, argument * 2);
frame
.stack
.push(StackEntry::Value(Value::Object(dictionary)));
}
Opcode::MakeFunction => {
// 函数构造校验并保留全部边后才消费原栈输入,避免捕获值提前释放。
let function = self.make_function(frame, decoded.instruction.argument)?;
// 按与构造器相同的位段计数弹出默认值、Cell 与代码项。
let (defaults, closure) =
Instruction::function_counts(decoded.instruction.argument);
self.discard(frame, defaults + closure + 1);
frame.stack.push(StackEntry::Value(Value::Object(function)));
}
// 扩展参数已由解码器合并,验证代码不含可执行的独立前缀。
Opcode::ExtendedArg => unreachable!("EXTENDED_ARG was decoded before evaluation"),
}
}
}
}