Files
OxideJS-New/crates/oxide_vm/src/suspended.rs
T

354 lines
15 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 挂起执行上下文快照:生成器 / 异步函数 / 异步生成器 三份状态结构体共享的执行核心。
//!
//! 挂起时把 VM 的执行核心(regs/pc/bytecode/各栈段/在途异常与完成)整体搬入,
//! 恢复时搬回;GC 侧对快照的根收集统一走 for_each_value。
//! 约定:本结构是"值"的容器,不含调度标志(generator_dispatch/async_*_dispatch/
//! async_context 等仍由各恢复包装函数管理,避免跨状态机污染)。
use std::sync::Arc;
use oxide_bytecode::opcode;
use oxide_types::object::Cell;
use oxide_types::value::JsValue;
use crate::vm::{CallFrame, Completion, ForInIter, PendingAsyncDispose, PendingAsyncEscape, TryHandler, Vm};
use crate::vm_state::ForOfEntry;
/// 挂起执行上下文快照(三份状态结构体共享的执行核心)。
pub(crate) struct SuspendedFrame {
pub regs: Box<[JsValue; 256]>,
pub pc: usize,
pub bytecode: Arc<[opcode::Instr]>,
/// 模块 flat_id(callee 表代际平表中的下标;恢复时按 callee 记录的代际
/// 解析平表并重激活 immutables)。
pub sub_idx: u32,
pub active_reg_limit: u8,
pub root_reg_limit: u8,
/// 挂起帧(push_bytecode_frame 压入,快照时弹出存此)。
pub frame: Option<CallFrame>,
pub spill_stack: Vec<JsValue>,
pub save_stack: Vec<JsValue>,
pub cell_stack: Vec<Vec<*mut Cell>>,
pub try_stack: Vec<TryHandler>,
pub for_in_iters: Vec<*mut ForInIter>,
pub for_of_iters: Vec<ForOfEntry>,
/// `yield*` 委托中的内层迭代器;异步函数恒为 None。
pub delegated_iterator: Option<JsValue>,
pub saved_bytecode_stack: Vec<Arc<[opcode::Instr]>>,
pub saved_immutables_stack: Vec<*const [JsValue]>,
pub exception_value: Option<JsValue>,
pub pending_exception: Option<JsValue>,
pub pending_error_kind: Option<&'static str>,
pub pending_completion: Option<Completion>,
/// 逃出 for-await-of 的异步关闭在途状态(随挂起快照跨微任务存活)。
pub pending_async_escape: Option<PendingAsyncEscape>,
/// `await using` 资源释放的异步挂起在途状态(随挂起快照跨微任务存活)。
pub pending_async_dispose: Option<PendingAsyncDispose>,
}
impl SuspendedFrame {
/// 全空帧(New 阶段构造状态盒时用)。
pub fn new_empty() -> Self {
SuspendedFrame {
regs: Box::new([JsValue::undefined(); 256]),
pc: 0,
bytecode: Arc::default(),
sub_idx: 0,
active_reg_limit: 0,
root_reg_limit: 0,
frame: None,
spill_stack: Vec::new(),
save_stack: Vec::new(),
cell_stack: Vec::new(),
try_stack: Vec::new(),
for_in_iters: Vec::new(),
for_of_iters: Vec::new(),
delegated_iterator: None,
saved_bytecode_stack: Vec::new(),
saved_immutables_stack: Vec::new(),
exception_value: None,
pending_exception: None,
pending_error_kind: None,
pending_completion: None,
pending_async_escape: None,
pending_async_dispose: None,
}
}
/// 从当前 VM 快照:弹出挂起帧,搬入各栈段、迭代器与在途异常/完成。
///
/// # 前提
/// 嵌套 dispatch 已让出,VM 只含当前状态机的执行数据(与现有 snapshot_* 的
/// 语义逐字一致)。`callee` 用于计算 sub_idx。
///
/// # 边界
/// frames 为空返回 Err(挂起点缺帧)。
pub fn save_from(&mut self, vm: &mut Vm, callee: JsValue) -> Result<(), String> {
let frame = vm.frames.pop().ok_or_else(|| "frame missing on suspend".to_string())?;
self.frame = Some(frame);
// 挂起弹帧不经 restore_frame:帧已弹入状态盒,活动镜像清零(null 空切片)。
vm.active_upvalues = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
*self.regs = vm.regs;
self.pc = vm.pc;
self.bytecode = std::mem::take(&mut vm.bytecode);
self.sub_idx = if callee.is_object() {
unsafe { (*callee.as_js_object_ptr()).sub_module_index() }
} else {
0
};
self.active_reg_limit = vm.active_reg_limit;
self.root_reg_limit = vm.root_reg_limit;
self.spill_stack = std::mem::take(&mut vm.spill_stack);
self.save_stack = std::mem::take(&mut vm.save_stack);
self.cell_stack = std::mem::take(&mut vm.cell_stack);
self.try_stack = std::mem::take(&mut vm.try_stack);
self.for_in_iters = std::mem::take(&mut vm.iters.for_in_iters);
self.for_of_iters = std::mem::take(&mut vm.iters.for_of_iters);
self.delegated_iterator = std::mem::take(&mut vm.delegated_iterator);
self.saved_bytecode_stack = std::mem::take(&mut vm.saved_bytecode_stack);
self.saved_immutables_stack = std::mem::take(&mut vm.saved_immutables_stack);
self.exception_value = vm.exception_value.take();
self.pending_exception = vm.pending_exception.take();
self.pending_error_kind = vm.pending_error_kind.take();
self.pending_completion = vm.pending_completion.take();
self.pending_async_escape = std::mem::take(&mut vm.pending_async_escape);
self.pending_async_dispose = std::mem::take(&mut vm.pending_async_dispose);
Ok(())
}
/// 恢复进 VM:写回各栈段、在途异常/完成,按 callee 记录的表代际重激活
/// immutables,并把执行键维度(表代际 / flat_id)还原到被恢复模块。
///
/// # 边界
/// - `callee` 须为挂起函数对象值(状态盒 `callee` 槽):代际按对象创建期
/// 记录解析,存活函数对象按代际保活其表,跨 run 恢复命中同一张表。
/// - callee 非对象(gen 0 哨兵)、代际表已被回收或下标越界返回 Err,
/// 调用方须按各自路径回滚并报错(保持现有三处行为)。
///
/// # 副作用
/// - `active_table_gen` / `active_flat_id` 还原为被恢复模块的 (代际,
/// flat_id):恢复体后续发射的标签模板须按本模块 (代际, flat_id) 命中
/// 模板缓存——不还原则同顶层连续恢复两代挂起体撞调用方侧同键,静默
/// 取回他侧模板对象。
pub fn restore_into(&mut self, vm: &mut Vm, callee: JsValue) -> Result<(), String> {
vm.regs = *self.regs;
vm.pc = self.pc;
vm.bytecode = std::mem::take(&mut self.bytecode);
let gen = if callee.is_object() {
// SAFETY: callee 为执行核心产出的函数对象值,session 生命周期内指针有效。
unsafe { (*callee.as_js_object_ptr()).table_gen() }
} else {
vm.current_gen
};
// 平表条目取 Arc 克隆(与调用方共享同一编译产物):后续 activate_immutables
// 等 &mut 调用期间不持有对注册表的借用。
let module = vm.tables.get(&gen).and_then(|t| t.modules.get(self.sub_idx as usize)).cloned();
match module {
Some(m) => vm.activate_immutables(gen, self.sub_idx as usize, &m),
None => return Err("suspended state module table is no longer available".into()),
}
// 模板缓存键维度随模块恢复:与 activate_immutables 同位还原。
vm.active_table_gen = gen;
vm.active_flat_id = self.sub_idx;
vm.active_reg_limit = self.active_reg_limit;
vm.root_reg_limit = self.root_reg_limit;
vm.frames.clear();
// 帧弹回前取被恢复函数的表,弹回后置回活动镜像(帧空即顶层空切片)。
let up = self
.frame
.as_ref()
.map(|f| f.upvalues)
.unwrap_or(std::ptr::slice_from_raw_parts(std::ptr::null(), 0));
if let Some(frame) = self.frame.take() {
vm.frames.push(frame);
}
vm.active_upvalues = up;
vm.spill_stack = std::mem::take(&mut self.spill_stack);
vm.save_stack = std::mem::take(&mut self.save_stack);
vm.cell_stack = std::mem::take(&mut self.cell_stack);
vm.try_stack = std::mem::take(&mut self.try_stack);
vm.iters.for_in_iters = std::mem::take(&mut self.for_in_iters);
vm.iters.for_of_iters = std::mem::take(&mut self.for_of_iters);
vm.delegated_iterator = std::mem::take(&mut self.delegated_iterator);
vm.saved_bytecode_stack = std::mem::take(&mut self.saved_bytecode_stack);
vm.saved_immutables_stack = std::mem::take(&mut self.saved_immutables_stack);
vm.exception_value = self.exception_value.take();
vm.pending_exception = self.pending_exception.take();
vm.pending_error_kind = self.pending_error_kind.take();
vm.pending_completion = self.pending_completion.take();
vm.pending_async_escape = std::mem::take(&mut self.pending_async_escape);
vm.pending_async_dispose = std::mem::take(&mut self.pending_async_dispose);
Ok(())
}
/// GC 根遍历:产出全部 JsValue(对象与字符串都产出),含 cell/for_in 解引用。
pub fn for_each_value(&self, mut f: impl FnMut(JsValue)) {
for v in self.regs.iter() {
f(*v);
}
if let Some(frame) = &self.frame {
f(frame.saved_this);
f(frame.saved_new_target);
f(frame.callee);
f(frame.arguments_obj);
if frame.has_constructed_this {
f(frame.constructed_this);
}
}
for v in &self.spill_stack {
f(*v);
}
for v in &self.save_stack {
f(*v);
}
for cells in &self.cell_stack {
for &p in cells {
if p.is_null() {
continue;
}
// SAFETY: cell 经 alloc_cell 独立堆分配,本 session 内指针有效。
f(unsafe { &*p }.value);
}
}
for entry in &self.for_of_iters {
f(entry.iterator);
f(entry.last_result);
f(entry.fast_value);
f(entry.fast_inner);
}
if let Some(it) = self.delegated_iterator {
f(it);
}
if let Some(v) = self.exception_value {
f(v);
}
if let Some(v) = self.pending_exception {
f(v);
}
if let Some(Completion::Return { value, .. }) = self.pending_completion {
f(value);
}
if let Some(pend) = &self.pending_async_escape {
f(pend.close_promise);
if let Completion::Return { value, .. } = pend.completion {
f(value);
}
for &v in &pend.remaining {
f(v);
}
}
if let Some(pend) = &self.pending_async_dispose {
f(pend.dispose_promise);
if let Some(v) = pend.completion {
f(v);
}
if let crate::vm::DisposeResumeKind::Escape(Completion::Return { value, .. }) = pend.kind {
f(value);
}
}
for iter in &self.for_in_iters {
if iter.is_null() {
continue;
}
// SAFETY: for_in_iters 存放堆上迭代器体,状态盒独占持有。
for (v, _si) in unsafe { (*(*iter)).keys.iter() } {
f(*v);
}
}
}
/// 堆字节数(drop 记账用:bytecode + spill/save/for_of 容量)。
pub fn heap_bytes(&self) -> u64 {
self.bytecode.len() as u64 * std::mem::size_of::<opcode::Instr>() as u64
+ self.spill_stack.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
+ self.save_stack.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
+ self.for_of_iters.capacity() as u64 * std::mem::size_of::<ForOfEntry>() as u64
}
}
impl Drop for SuspendedFrame {
/// 释放状态盒持有的 for-in 迭代器体:体是堆上 `Box`,每个状态盒独占
/// 持有自身体,随状态盒释放逐条释放(null 跳过)。restore 已把向量
/// 移出时字段为空,Drop 不再触碰,无双放。
fn drop(&mut self) {
for iter in self.for_in_iters.drain(..) {
if iter.is_null() {
continue;
}
// SAFETY: 指针是堆上迭代器体,状态盒独占持有,释放恰好一次。
unsafe {
drop(Box::from_raw(iter));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::vm::{FrameContinuation, Vm};
fn vm() -> Vm {
Vm::new()
}
fn sentinel_frame(tag: JsValue) -> CallFrame {
CallFrame {
return_addr: 7,
function_name: 1,
frame_id: 0,
caller_reg_limit: 2,
caller_active_reg_limit: 2,
saved_reg_offset: 3,
spill_offset: 4,
arguments_base: 5,
arguments_count: 6,
saved_this: tag,
saved_new_target: tag,
callee: tag,
arguments_obj: JsValue::undefined(),
construct_result_reg: None,
constructed_this: tag,
has_constructed_this: true,
is_derived_constructor: false,
super_called: false,
strict: false,
continuation: FrameContinuation::None,
upvalues: std::ptr::slice_from_raw_parts(std::ptr::null(), 0),
}
}
#[test]
fn save_restore_round_trip() {
let mut machine = vm();
machine.regs[0] = JsValue::float(42.0);
machine.pc = 9;
machine.frames.push(sentinel_frame(JsValue::float(43.0)));
machine.spill_stack.push(JsValue::float(44.0));
machine.delegated_iterator = Some(JsValue::float(45.0));
machine.pending_completion = Some(Completion::Return {
value: JsValue::float(46.0),
remaining_finally: 1,
for_of_count: 0,
for_in_count: 0,
dispose_count: 0,
});
let mut frame = SuspendedFrame::new_empty();
frame.save_from(&mut machine, JsValue::undefined()).unwrap();
assert_eq!(frame.regs[0], JsValue::float(42.0));
assert_eq!(frame.pc, 9);
assert_eq!(frame.delegated_iterator, Some(JsValue::float(45.0)));
assert!(matches!(
frame.pending_completion,
Some(Completion::Return { value, remaining_finally: 1, .. }) if value == JsValue::float(46.0)
));
// gen 0 空表占位 + sub_idx = 0 越界:恢复应报错(保持现有三处行为)。
let mut machine2 = vm();
let mut frame2 = SuspendedFrame::new_empty();
frame2.sub_idx = 0;
let err = frame2.restore_into(&mut machine2, JsValue::undefined());
assert!(err.is_err());
}
}