refactor: GeneratorState 收敛到 SuspendedFrame(save/restore/GC 遍历/string 边)
This commit is contained in:
@@ -9,14 +9,13 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_builtins::iterator::{is_callable, make_iter_result};
|
||||
use oxide_bytecode::opcode;
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_runtime_api::{to_boolean, NativeResult};
|
||||
use oxide_types::mem::P;
|
||||
use oxide_types::object::{Cell, JsObject, NativeFnPtr};
|
||||
use oxide_types::object::{JsObject, NativeFnPtr};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::vm::{CallFrame, ForInIter, FrameContinuation, TryHandler, Vm};
|
||||
use crate::vm::Vm;
|
||||
|
||||
/// 生成器执行阶段。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -45,32 +44,8 @@ pub(crate) struct GeneratorState {
|
||||
pub this_value: JsValue,
|
||||
/// 完成后返回值。
|
||||
pub result: JsValue,
|
||||
// ── 挂起时的执行上下文 ──
|
||||
pub regs: Box<[JsValue; 256]>,
|
||||
pub pc: usize,
|
||||
pub bytecode: Vec<opcode::Instr>,
|
||||
/// 生成器模块 flat_id(恢复时重激活 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<'static>>,
|
||||
pub for_of_iters: Vec<JsValue>,
|
||||
pub last_for_of_result: JsValue,
|
||||
/// `yield*` 委托中的内层迭代器:Some = 挂起在委托点,恢复时转发 next/return/throw。
|
||||
pub delegated_iterator: Option<JsValue>,
|
||||
pub saved_bytecode_stack: Vec<Vec<opcode::Instr>>,
|
||||
pub saved_immutables_stack: Vec<*const [JsValue]>,
|
||||
/// 在途异常/完成(throw 穿越 finally 挂起时保留,恢复后继续展开)。
|
||||
pub exception_value: Option<JsValue>,
|
||||
pub pending_exception: Option<JsValue>,
|
||||
pub pending_error_kind: Option<&'static str>,
|
||||
pub pending_completion: Option<crate::vm::Completion>,
|
||||
/// 挂起时的执行上下文(regs/pc/bytecode/各栈段/迭代器/在途异常)。
|
||||
pub suspended: crate::suspended::SuspendedFrame,
|
||||
}
|
||||
|
||||
/// `next()/return()/throw()` 的注入模式。
|
||||
@@ -137,27 +112,7 @@ impl Vm {
|
||||
args: args.to_vec(),
|
||||
this_value,
|
||||
result: JsValue::undefined(),
|
||||
regs: Box::new([JsValue::undefined(); 256]),
|
||||
pc: 0,
|
||||
bytecode: Vec::new(),
|
||||
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(),
|
||||
last_for_of_result: JsValue::undefined(),
|
||||
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,
|
||||
suspended: crate::suspended::SuspendedFrame::new_empty(),
|
||||
});
|
||||
obj_ref.set_native_data(Box::into_raw(state) as *mut u8);
|
||||
self.gc_state.track_epoch_object(obj);
|
||||
@@ -199,33 +154,10 @@ impl Vm {
|
||||
self.native_call_depth += 1;
|
||||
|
||||
// 压入生成器帧(New)。
|
||||
self.regs = [JsValue::undefined(); 256];
|
||||
self.pc = 0;
|
||||
self.bytecode = Vec::new();
|
||||
self.active_reg_limit = 1;
|
||||
self.root_reg_limit = 1;
|
||||
self.try_stack.clear();
|
||||
self.iters.for_in_iters.clear();
|
||||
self.iters.for_of_iters.clear();
|
||||
self.iters.last_for_of_result = JsValue::undefined();
|
||||
self.spill_stack.clear();
|
||||
self.save_stack.clear();
|
||||
self.saved_bytecode_stack.clear();
|
||||
self.saved_immutables_stack.clear();
|
||||
self.cell_stack.clear();
|
||||
self.inline_callee = None;
|
||||
let callee = unsafe { (*state_ptr).callee };
|
||||
let this_value = unsafe { (*state_ptr).this_value };
|
||||
let args = unsafe { std::mem::take(&mut (*state_ptr).args) };
|
||||
let push_res = self.push_bytecode_frame(
|
||||
callee,
|
||||
this_value,
|
||||
&args,
|
||||
None,
|
||||
None,
|
||||
JsValue::undefined(),
|
||||
FrameContinuation::None,
|
||||
);
|
||||
let push_res = self.prepare_execution_initial(callee, this_value, &args);
|
||||
unsafe { (*state_ptr).args = args };
|
||||
if let Err(e) = push_res {
|
||||
self.restore_inline_state(saved);
|
||||
@@ -332,33 +264,10 @@ impl Vm {
|
||||
|
||||
// 首次执行:压入生成器帧;挂起:恢复快照。
|
||||
if was_new {
|
||||
self.regs = [JsValue::undefined(); 256];
|
||||
self.pc = 0;
|
||||
self.bytecode = Vec::new();
|
||||
self.active_reg_limit = 1;
|
||||
self.root_reg_limit = 1;
|
||||
self.try_stack.clear();
|
||||
self.iters.for_in_iters.clear();
|
||||
self.iters.for_of_iters.clear();
|
||||
self.iters.last_for_of_result = JsValue::undefined();
|
||||
self.delegated_iterator = None;
|
||||
self.spill_stack.clear();
|
||||
self.save_stack.clear();
|
||||
self.saved_bytecode_stack.clear();
|
||||
self.saved_immutables_stack.clear();
|
||||
self.cell_stack.clear();
|
||||
self.inline_callee = None;
|
||||
let callee = unsafe { (*state_ptr).callee };
|
||||
let args = unsafe { std::mem::take(&mut (*state_ptr).args) };
|
||||
let push_res = self.push_bytecode_frame(
|
||||
callee,
|
||||
JsValue::undefined(),
|
||||
&args,
|
||||
None,
|
||||
None,
|
||||
JsValue::undefined(),
|
||||
FrameContinuation::None,
|
||||
);
|
||||
let push_res = self.prepare_execution_initial(callee, JsValue::undefined(), &args);
|
||||
unsafe { (*state_ptr).args = args };
|
||||
if let Err(e) = push_res {
|
||||
self.restore_inline_state(saved);
|
||||
@@ -367,38 +276,13 @@ impl Vm {
|
||||
unsafe { (*state_ptr).phase = GeneratorPhase::Running };
|
||||
} else {
|
||||
let state = unsafe { &mut *state_ptr };
|
||||
self.regs = *state.regs;
|
||||
self.pc = state.pc;
|
||||
self.bytecode = std::mem::take(&mut state.bytecode);
|
||||
let subs = Arc::clone(&self.sub_modules);
|
||||
if (state.sub_idx as usize) < subs.len() {
|
||||
self.activate_immutables(state.sub_idx as usize, &subs[state.sub_idx as usize].constants);
|
||||
} else {
|
||||
let restore_res = state.suspended.restore_into(self, &subs);
|
||||
if restore_res.is_err() {
|
||||
// 挂起状态跨 run:sub_modules 已重建,无法恢复(与动态函数同限制)。
|
||||
self.restore_inline_state(saved);
|
||||
return Err("generator suspended across runs is no longer valid".into());
|
||||
}
|
||||
self.active_reg_limit = state.active_reg_limit;
|
||||
self.root_reg_limit = state.root_reg_limit;
|
||||
self.frames.clear();
|
||||
if let Some(frame) = state.frame.take() {
|
||||
self.frames.push(frame);
|
||||
}
|
||||
self.spill_stack = std::mem::take(&mut state.spill_stack);
|
||||
self.save_stack = std::mem::take(&mut state.save_stack);
|
||||
self.cell_stack = std::mem::take(&mut state.cell_stack);
|
||||
self.try_stack = std::mem::take(&mut state.try_stack);
|
||||
self.iters.for_in_iters = std::mem::take(&mut state.for_in_iters);
|
||||
self.iters.for_of_iters = std::mem::take(&mut state.for_of_iters);
|
||||
self.iters.last_for_of_result = state.last_for_of_result;
|
||||
self.delegated_iterator = state.delegated_iterator.take();
|
||||
self.saved_bytecode_stack = std::mem::take(&mut state.saved_bytecode_stack);
|
||||
self.saved_immutables_stack = std::mem::take(&mut state.saved_immutables_stack);
|
||||
self.exception_value = state.exception_value.take();
|
||||
self.pending_exception = state.pending_exception.take();
|
||||
self.pending_error_kind = state.pending_error_kind.take();
|
||||
self.pending_completion = state.pending_completion.take();
|
||||
self.inline_callee = None;
|
||||
state.phase = GeneratorPhase::Running;
|
||||
|
||||
// `yield*` 委托恢复:把 next/return/throw 请求转发给内层迭代器,按内层
|
||||
@@ -747,37 +631,10 @@ impl Vm {
|
||||
|
||||
/// 把当前 VM 执行状态(生成器 body 刚在嵌套 dispatch 中让出)快照进状态盒。
|
||||
///
|
||||
/// 生成器帧弹出存 `frame`,其余栈段整段搬入(嵌套循环期间这些栈只含生成器数据)。
|
||||
/// 生成器帧弹出存 `suspended.frame`,其余栈段整段搬入(嵌套循环期间这些栈
|
||||
/// 只含生成器数据)。
|
||||
fn snapshot_generator(&mut self, state: &mut GeneratorState) -> Result<(), String> {
|
||||
let frame = self
|
||||
.frames
|
||||
.pop()
|
||||
.ok_or_else(|| "generator frame missing on yield".to_string())?;
|
||||
state.frame = Some(frame);
|
||||
state.regs = Box::new(self.regs);
|
||||
state.pc = self.pc;
|
||||
state.bytecode = std::mem::take(&mut self.bytecode);
|
||||
state.sub_idx = if state.callee.is_object() {
|
||||
unsafe { (*state.callee.as_js_object_ptr()).sub_module_index() }
|
||||
} else {
|
||||
0
|
||||
};
|
||||
state.active_reg_limit = self.active_reg_limit;
|
||||
state.root_reg_limit = self.root_reg_limit;
|
||||
state.spill_stack = std::mem::take(&mut self.spill_stack);
|
||||
state.save_stack = std::mem::take(&mut self.save_stack);
|
||||
state.cell_stack = std::mem::take(&mut self.cell_stack);
|
||||
state.try_stack = std::mem::take(&mut self.try_stack);
|
||||
state.for_in_iters = std::mem::take(&mut self.iters.for_in_iters);
|
||||
state.for_of_iters = std::mem::take(&mut self.iters.for_of_iters);
|
||||
state.last_for_of_result = self.iters.last_for_of_result;
|
||||
state.delegated_iterator = self.delegated_iterator.take();
|
||||
state.saved_bytecode_stack = std::mem::take(&mut self.saved_bytecode_stack);
|
||||
state.saved_immutables_stack = std::mem::take(&mut self.saved_immutables_stack);
|
||||
state.exception_value = self.exception_value.take();
|
||||
state.pending_exception = self.pending_exception.take();
|
||||
state.pending_error_kind = self.pending_error_kind.take();
|
||||
state.pending_completion = self.pending_completion.take();
|
||||
state.suspended.save_from(self, state.callee)?;
|
||||
state.phase = GeneratorPhase::Suspended;
|
||||
Ok(())
|
||||
}
|
||||
@@ -965,35 +822,40 @@ pub(crate) fn generator_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||
push(state.callee, &mut edges);
|
||||
edges.extend(state.args.iter().copied().filter(|v| v.is_object()));
|
||||
push(state.result, &mut edges);
|
||||
edges.extend(state.regs.iter().copied().filter(|v| v.is_object()));
|
||||
if let Some(frame) = &state.frame {
|
||||
push(frame.saved_this, &mut edges);
|
||||
push(frame.saved_new_target, &mut edges);
|
||||
push(frame.callee, &mut edges);
|
||||
if let Some(ct) = frame.constructed_this {
|
||||
push(ct, &mut edges);
|
||||
state.suspended.for_each_value(|v| {
|
||||
if v.is_object() {
|
||||
edges.push(v);
|
||||
}
|
||||
}
|
||||
edges.extend(state.spill_stack.iter().copied().filter(|v| v.is_object()));
|
||||
edges.extend(state.save_stack.iter().copied().filter(|v| v.is_object()));
|
||||
for cells in &state.cell_stack {
|
||||
for &cell_ptr in cells {
|
||||
if cell_ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: cell 由 session_epoch 分配,本 session 内指针有效。
|
||||
let cell = unsafe { &*cell_ptr };
|
||||
push(cell.value, &mut edges);
|
||||
}
|
||||
}
|
||||
edges.extend(state.for_of_iters.iter().copied().filter(|v| v.is_object()));
|
||||
push(state.last_for_of_result, &mut edges);
|
||||
if let Some(iter) = state.delegated_iterator {
|
||||
push(iter, &mut edges);
|
||||
}
|
||||
});
|
||||
edges
|
||||
}
|
||||
|
||||
/// 生成器状态内所有 session 字符串的扁平列表(GC mark 字符串边)。
|
||||
/// 与 `generator_native_edges` 的对象收集互补,经同一字段清单(自身字段 +
|
||||
/// `suspended.for_each_value`)产出字符串。
|
||||
pub(crate) fn generator_native_string_edges(obj: &JsObject) -> Vec<*mut oxide_types::object::JsString> {
|
||||
let Some(state) = generator_state_mut(obj) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
let push_str = |v: JsValue, out: &mut Vec<*mut oxide_types::object::JsString>| {
|
||||
if v.is_string() {
|
||||
out.push(v.as_string_ptr_mut());
|
||||
}
|
||||
};
|
||||
push_str(state.callee, &mut out);
|
||||
for &v in &state.args {
|
||||
push_str(v, &mut out);
|
||||
}
|
||||
push_str(state.result, &mut out);
|
||||
state.suspended.for_each_value(|v| {
|
||||
if v.is_string() {
|
||||
out.push(v.as_string_ptr_mut());
|
||||
}
|
||||
});
|
||||
out
|
||||
}
|
||||
|
||||
/// 用转发函数重写状态快照中的所有 JsValue(session GC 移动式清扫 / promote 用)。
|
||||
pub(crate) fn rewrite_generator_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) {
|
||||
let Some(state) = generator_state_mut(obj) else {
|
||||
@@ -1004,36 +866,7 @@ pub(crate) fn rewrite_generator_native(obj: &JsObject, mut rewrite: impl FnMut(J
|
||||
*v = rewrite(*v);
|
||||
}
|
||||
state.result = rewrite(state.result);
|
||||
for v in state.regs.iter_mut() {
|
||||
*v = rewrite(*v);
|
||||
}
|
||||
if let Some(frame) = &mut state.frame {
|
||||
frame.saved_this = rewrite(frame.saved_this);
|
||||
frame.saved_new_target = rewrite(frame.saved_new_target);
|
||||
frame.callee = rewrite(frame.callee);
|
||||
frame.constructed_this = frame.constructed_this.map(&mut rewrite);
|
||||
}
|
||||
for v in &mut state.spill_stack {
|
||||
*v = rewrite(*v);
|
||||
}
|
||||
for v in &mut state.save_stack {
|
||||
*v = rewrite(*v);
|
||||
}
|
||||
for cells in &mut state.cell_stack {
|
||||
for &mut cell_ptr in cells.iter_mut() {
|
||||
if cell_ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: cell 由 session_epoch 分配,本 session 内指针有效。
|
||||
let cell = unsafe { &mut *cell_ptr };
|
||||
cell.value = rewrite(cell.value);
|
||||
}
|
||||
}
|
||||
for v in &mut state.for_of_iters {
|
||||
*v = rewrite(*v);
|
||||
}
|
||||
state.last_for_of_result = rewrite(state.last_for_of_result);
|
||||
state.delegated_iterator = state.delegated_iterator.map(&mut rewrite);
|
||||
state.suspended.rewrite_values(rewrite);
|
||||
}
|
||||
|
||||
/// 释放生成器状态盒(对象被 GC 回收时),返回释放字节数。
|
||||
@@ -1048,9 +881,6 @@ pub(crate) fn drop_generator_native(obj: &JsObject) -> u64 {
|
||||
// SAFETY: 指针来自 create_generator_object 的 Box::into_raw,只在 GC 回收时释放一次。
|
||||
let state = unsafe { Box::from_raw(ptr) };
|
||||
std::mem::size_of::<GeneratorState>() as u64
|
||||
+ state.bytecode.len() as u64 * std::mem::size_of::<u32>() as u64
|
||||
+ state.spill_stack.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
|
||||
+ state.save_stack.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
|
||||
+ state.for_of_iters.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
|
||||
+ state.suspended.heap_bytes()
|
||||
+ state.args.capacity() as u64 * std::mem::size_of::<JsValue>() as u64
|
||||
}
|
||||
|
||||
@@ -157,10 +157,8 @@ impl SessionGc {
|
||||
}
|
||||
}
|
||||
if obj.is_generator_obj() {
|
||||
for value in crate::generator::generator_native_edges(obj) {
|
||||
if value.is_string() {
|
||||
live.insert(value.as_string_ptr_mut());
|
||||
}
|
||||
for ptr in crate::generator::generator_native_string_edges(obj) {
|
||||
live.insert(ptr);
|
||||
}
|
||||
}
|
||||
if obj.is_promise_obj() {
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::sync::{Arc, OnceLock};
|
||||
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
|
||||
use crate::vm::{CallFrame, InlineSyncState, Vm};
|
||||
use crate::vm::{CallFrame, FrameContinuation, InlineSyncState, Vm};
|
||||
use crate::{vm_debug, vm_info, vm_trace, vm_warn};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
@@ -84,6 +84,32 @@ impl Vm {
|
||||
self.inline_args_count = saved.inline_args_count;
|
||||
}
|
||||
|
||||
/// 首次执行的 VM 就绪:清空执行核心(regs/pc/bytecode/各栈段/迭代器/内联态),
|
||||
/// 压入首帧。参数初始化步与 async/gen 调度标志由调用方管理。
|
||||
///
|
||||
/// # 边界
|
||||
/// callee 非函数 / sub_idx 越界 / 超调用深度时由 push_bytecode_frame 返回 Err。
|
||||
pub(crate) fn prepare_execution_initial(
|
||||
&mut self, callee: JsValue, this_value: JsValue, args: &[JsValue],
|
||||
) -> Result<(), String> {
|
||||
self.regs = [JsValue::undefined(); 256];
|
||||
self.pc = 0;
|
||||
self.bytecode = Vec::new();
|
||||
self.active_reg_limit = 1;
|
||||
self.root_reg_limit = 1;
|
||||
self.try_stack.clear();
|
||||
self.iters.for_in_iters.clear();
|
||||
self.iters.for_of_iters.clear();
|
||||
self.iters.last_for_of_result = JsValue::undefined();
|
||||
self.spill_stack.clear();
|
||||
self.save_stack.clear();
|
||||
self.saved_bytecode_stack.clear();
|
||||
self.saved_immutables_stack.clear();
|
||||
self.cell_stack.clear();
|
||||
self.inline_callee = None;
|
||||
self.push_bytecode_frame(callee, this_value, args, None, None, JsValue::undefined(), FrameContinuation::None)
|
||||
}
|
||||
|
||||
pub(crate) fn call_bytecode_function_inline(
|
||||
&mut self, callee: JsValue, callee_obj: &JsObject, receiver: JsValue, args: &[JsValue],
|
||||
) -> Result<JsValue, String> {
|
||||
|
||||
Reference in New Issue
Block a user