89a4e1bb86
- emit 识别 generator+async 组合,模块同时标记 is_generator/is_async - 新增 AsyncGeneratorState:帧快照 + 请求队列 + 当前请求 - next/return/throw 返回 Promise,请求入队逐条处理 - YIELD 让出经 promise unwrap 后交付(yield rejected promise 关闭生成器) - AWAIT 挂起保留当前请求,微任务恢复后继续执行 - %AsyncGeneratorPrototype% / %AsyncGeneratorFunction.prototype% 内建 - session GC / promote 支持异步生成器状态盒 - 普通 async 函数嵌套调用隔离外层异步生成器上下文
315 lines
14 KiB
Rust
315 lines
14 KiB
Rust
use std::sync::{Arc, OnceLock};
|
|
|
|
use oxide_bytecode::module::CompiledModule;
|
|
|
|
use crate::vm::{CallFrame, InlineSyncState, Vm};
|
|
use crate::{vm_debug, vm_info, vm_trace, vm_warn};
|
|
use oxide_types::object::JsObject;
|
|
use oxide_types::value::JsValue;
|
|
|
|
/// 按 `flat_id` 下标收集整棵子模块树为平表,供 `run()` 装载。
|
|
fn collect_flat_modules(module: &CompiledModule) -> Vec<CompiledModule> {
|
|
let mut out: Vec<Option<CompiledModule>> = Vec::new();
|
|
place_flat(module, &mut out);
|
|
out.into_iter().map(|m| m.expect("flat_id slot must be filled")).collect()
|
|
}
|
|
|
|
fn place_flat(module: &CompiledModule, out: &mut Vec<Option<CompiledModule>>) {
|
|
let idx = module.flat_id as usize;
|
|
if idx >= out.len() {
|
|
out.resize(idx + 1, None);
|
|
}
|
|
out[idx] = Some(module.clone());
|
|
for sub in &module.sub_modules {
|
|
place_flat(sub, out);
|
|
}
|
|
}
|
|
|
|
impl Vm {
|
|
/// 保存当前完整 VM 执行状态到堆上(内联同步调用与生成器恢复共用)。
|
|
pub(crate) fn save_inline_state(&mut self) -> Box<InlineSyncState> {
|
|
vm_trace!("save_inline_state: pc={} depth={}", self.pc, self.frames.len());
|
|
Box::new(InlineSyncState {
|
|
regs: Box::new(self.regs),
|
|
pc: self.pc,
|
|
bytecode: std::mem::take(&mut self.bytecode),
|
|
active_immutables: self.active_immutables,
|
|
active_reg_limit: self.active_reg_limit,
|
|
root_reg_limit: self.root_reg_limit,
|
|
try_stack: std::mem::take(&mut self.try_stack),
|
|
frames: std::mem::take(&mut self.frames),
|
|
exception_value: self.exception_value.take(),
|
|
pending_exception: self.pending_exception.take(),
|
|
pending_error_kind: self.pending_error_kind.take(),
|
|
pending_completion: self.pending_completion,
|
|
for_in_iters: std::mem::take(&mut self.iters.for_in_iters),
|
|
for_of_iters: std::mem::take(&mut self.iters.for_of_iters),
|
|
last_for_of_result: self.iters.last_for_of_result,
|
|
saved_bytecode_stack: std::mem::take(&mut self.saved_bytecode_stack),
|
|
saved_immutables_stack: std::mem::take(&mut self.saved_immutables_stack),
|
|
save_stack: std::mem::take(&mut self.save_stack),
|
|
spill_stack: std::mem::take(&mut self.spill_stack),
|
|
cell_stack: std::mem::take(&mut self.cell_stack),
|
|
inline_callee: self.inline_callee,
|
|
inline_args_base: self.inline_args_base,
|
|
inline_args_count: self.inline_args_count,
|
|
})
|
|
}
|
|
|
|
/// 把 [`save_inline_state`] 保存的状态完整恢复回 VM。
|
|
pub(crate) fn restore_inline_state(&mut self, saved: Box<InlineSyncState>) {
|
|
vm_trace!("restore_inline_state: pc={}", saved.pc);
|
|
self.regs = *saved.regs;
|
|
self.pc = saved.pc;
|
|
self.bytecode = saved.bytecode;
|
|
self.active_immutables = saved.active_immutables;
|
|
self.active_reg_limit = saved.active_reg_limit;
|
|
self.root_reg_limit = saved.root_reg_limit;
|
|
self.try_stack = saved.try_stack;
|
|
self.frames = saved.frames;
|
|
self.exception_value = saved.exception_value;
|
|
self.pending_exception = saved.pending_exception;
|
|
self.pending_error_kind = saved.pending_error_kind;
|
|
self.pending_completion = saved.pending_completion;
|
|
self.iters.for_in_iters = saved.for_in_iters;
|
|
self.iters.for_of_iters = saved.for_of_iters;
|
|
self.iters.last_for_of_result = saved.last_for_of_result;
|
|
self.saved_bytecode_stack = saved.saved_bytecode_stack;
|
|
self.saved_immutables_stack = saved.saved_immutables_stack;
|
|
self.save_stack = saved.save_stack;
|
|
self.spill_stack = saved.spill_stack;
|
|
self.cell_stack = saved.cell_stack;
|
|
self.inline_callee = saved.inline_callee;
|
|
self.inline_args_base = saved.inline_args_base;
|
|
self.inline_args_count = saved.inline_args_count;
|
|
}
|
|
|
|
pub(crate) fn call_bytecode_function_inline(
|
|
&mut self, callee: JsValue, callee_obj: &JsObject, receiver: JsValue, args: &[JsValue],
|
|
) -> Result<JsValue, String> {
|
|
if callee_obj.sub_module_index() == 0 {
|
|
return Err(self.error_message_text("TypeError", "accessor is not callable"));
|
|
}
|
|
let sub_idx = callee_obj.sub_module_index() as usize;
|
|
vm_debug!(
|
|
"call_bytecode_function_inline: sub_idx={}, args={}, depth={}",
|
|
sub_idx,
|
|
args.len(),
|
|
self.frames.len()
|
|
);
|
|
if sub_idx >= self.sub_modules.len() {
|
|
return Err(format!(
|
|
"accessor sub_module_index {} out of bounds (max {})",
|
|
sub_idx,
|
|
self.sub_modules.len()
|
|
));
|
|
}
|
|
if self.frames.len() >= self.kernel_core.config.max_call_depth {
|
|
self.raise_error_kind("RangeError", "Maximum call stack size exceeded")?;
|
|
return Ok(JsValue::undefined());
|
|
}
|
|
if self.native_call_depth >= self.kernel_core.config.max_call_depth {
|
|
self.raise_error_kind("RangeError", "Maximum call stack size exceeded")?;
|
|
return Ok(JsValue::undefined());
|
|
}
|
|
// 异步生成器函数调用返回异步生成器迭代器对象,next/return/throw 返回 Promise。
|
|
if self.sub_modules[sub_idx].is_generator && self.sub_modules[sub_idx].is_async {
|
|
return self.create_async_generator_object(callee, receiver, args);
|
|
}
|
|
// 生成器函数调用返回迭代器对象,不执行函数体。
|
|
if self.sub_modules[sub_idx].is_generator {
|
|
return self.create_generator_object(callee, receiver, args);
|
|
}
|
|
// 异步函数调用返回 capability promise,立即同步执行 body 到首个 await。
|
|
if self.sub_modules[sub_idx].is_async {
|
|
return self.create_async_object(callee, receiver, args);
|
|
}
|
|
self.native_call_depth += 1;
|
|
|
|
let subs = Arc::clone(&self.sub_modules);
|
|
let sub = &subs[sub_idx];
|
|
|
|
vm_trace!("call_bytecode: saving state pc={} depth={}", self.pc, self.frames.len());
|
|
let saved = self.save_inline_state();
|
|
|
|
self.regs = [JsValue::undefined(); 256];
|
|
self.pc = 0;
|
|
self.bytecode = sub.bytecode.clone();
|
|
self.activate_immutables(sub_idx, &sub.constants);
|
|
self.active_reg_limit = sub.n_registers.max(1);
|
|
self.root_reg_limit = self.active_reg_limit;
|
|
self.cell_stack.push(Vec::with_capacity(sub.cells_needed as usize));
|
|
self.inline_callee = Some(callee);
|
|
// inline 同步调用无 CallFrame:完整实参写入 spill 栈实参区,供 CREATE_ARGUMENTS 读取。
|
|
self.inline_args_base = self.spill_stack.len() as u32;
|
|
self.spill_stack.extend_from_slice(args);
|
|
self.inline_args_count = args.len().min(u16::MAX as usize) as u16;
|
|
for i in 0..sub.n_args as usize {
|
|
self.regs[sub.param_base as usize + i] = args.get(i).copied().unwrap_or(JsValue::undefined());
|
|
}
|
|
self.regs[254] = if sub.is_arrow { callee_obj.captured_this() } else { receiver };
|
|
self.regs[255] = JsValue::undefined();
|
|
for (name, reg) in &sub.builtin_reg_map {
|
|
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
|
|
let global = self.session.global_object();
|
|
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
|
|
self.regs[*reg as usize] = global.get_prop_at(pos);
|
|
}
|
|
}
|
|
let _ = callee;
|
|
|
|
vm_trace!(
|
|
"call_bytecode: dispatching sub_idx={} n_regs={} n_args={} arrow={}",
|
|
sub_idx,
|
|
sub.n_registers,
|
|
sub.n_args,
|
|
sub.is_arrow
|
|
);
|
|
let result = self.dispatch();
|
|
self.native_call_depth -= 1;
|
|
|
|
vm_trace!("call_bytecode: restoring state pc={} result={:?}", saved.pc, result.as_ref().ok());
|
|
self.restore_inline_state(saved);
|
|
result
|
|
}
|
|
|
|
pub(crate) fn restore_frame(&mut self, frame: CallFrame) {
|
|
vm_trace!(
|
|
"restore_frame: return_addr={} caller_reg_limit={}",
|
|
frame.return_addr,
|
|
frame.caller_reg_limit
|
|
);
|
|
if let Some(saved_bc) = self.saved_bytecode_stack.pop() {
|
|
self.bytecode = saved_bc;
|
|
}
|
|
vm_debug!(
|
|
"restore_frame: return_addr={} bc_len={} saved_stack={} fn={:?}",
|
|
frame.return_addr,
|
|
self.bytecode.len(),
|
|
self.saved_bytecode_stack.len(),
|
|
self.kernel_core
|
|
.perm_interner()
|
|
.lookup(frame.function_name)
|
|
.map(|s| s.to_string())
|
|
);
|
|
if let Some(saved_imm) = self.saved_immutables_stack.pop() {
|
|
self.active_immutables = saved_imm;
|
|
}
|
|
let offset = frame.saved_reg_offset as usize;
|
|
let len = frame.caller_reg_limit as usize;
|
|
self.regs[..len].copy_from_slice(&self.save_stack[offset..offset + len]);
|
|
self.save_stack.truncate(offset);
|
|
// spill 帧边界截断:子函数写入的 spill 数据在帧恢复后丢弃。
|
|
self.spill_stack.truncate(frame.spill_offset as usize);
|
|
self.regs[254] = frame.saved_this;
|
|
self.regs[255] = frame.saved_new_target;
|
|
self.active_reg_limit = frame.caller_reg_limit;
|
|
self.pc = frame.return_addr;
|
|
}
|
|
|
|
/// 重新执行当前已加载的 bytecode:清空执行状态并重置 IC 缓存后再次 dispatch。
|
|
pub fn rerun(&mut self) -> Result<JsValue, String> {
|
|
vm_info!("rerun: clearing IC caches");
|
|
self.clear_execution_state();
|
|
self.active_reg_limit = self.root_reg_limit;
|
|
crate::ic_helper::clear_ic_caches(&mut self.bytecode);
|
|
self.dispatch()
|
|
}
|
|
|
|
/// 加载并执行一个已编译模块,返回模块顶层执行结果或未捕获异常消息。
|
|
///
|
|
/// 内部初始化寄存器/bytecode/immutables 与 builtin 寄存器预绑定,然后进入
|
|
/// dispatch 主循环;执行完成或异常展开后返回。
|
|
pub fn run(&mut self, module: &CompiledModule) -> Result<JsValue, String> {
|
|
vm_debug!("run: starting bytecode execution, {} instructions", module.bytecode.len());
|
|
self.clear_execution_state();
|
|
self.cell_stack.clear();
|
|
self.cell_stack.push(Vec::new());
|
|
self.sub_modules = Arc::new(collect_flat_modules(module));
|
|
self.immutables_cache = (0..self.sub_modules.len()).map(|_| OnceLock::new()).collect();
|
|
self.bytecode = module.bytecode.clone();
|
|
self.activate_immutables(0, &module.constants);
|
|
self.root_reg_limit = module.n_registers.max(1);
|
|
self.active_reg_limit = self.root_reg_limit;
|
|
|
|
for (name, reg) in &module.builtin_reg_map {
|
|
let si = self.kernel_core.perm_interner().intern(name.as_str()).0;
|
|
let global = self.session.global_object();
|
|
if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) {
|
|
self.regs[*reg as usize] = global.get_prop_at(pos);
|
|
}
|
|
}
|
|
|
|
// 全局代码的 this 绑定为全局对象(ECMA-262 全局执行上下文)。
|
|
let global = self.session.global_object();
|
|
self.regs[254] = JsValue::from_js_object(global.as_ptr() as *mut JsObject);
|
|
|
|
let result = self.dispatch();
|
|
// 顶层执行结束后 drain 微任务队列:Promise reactions 与 thenable 委托在此执行。
|
|
self.drain_job_queue();
|
|
result
|
|
}
|
|
|
|
pub(crate) fn unwind(&mut self) -> Result<(), String> {
|
|
vm_debug!("unwind: {} try handlers on stack", self.try_stack.len());
|
|
while let Some(mut handler) = self.try_stack.pop() {
|
|
if handler.frame_depth > self.frames.len() {
|
|
// 残留 handler 所属帧已返回(return 逃出泄漏的兜底):丢弃防跳回
|
|
// 死函数的 catch/finally——否则 catch 再抛会反复弹出同一 handler
|
|
// 形成死循环。其 for_of_depth 快照属于已死帧,也不可据此关闭
|
|
// 当前帧的迭代器,故整体跳过。
|
|
continue;
|
|
}
|
|
while self.frames.len() > handler.frame_depth {
|
|
if let Some(frame) = self.frames.pop() {
|
|
self.cell_stack.pop();
|
|
self.restore_frame(frame);
|
|
}
|
|
}
|
|
// 对该处理器作用域内被中断的 for-of 循环执行 IteratorClose。
|
|
self.close_for_of_above(handler.for_of_depth);
|
|
if let Some(finally_pc) = handler.finally_pc {
|
|
if handler.finally_active {
|
|
// 新异常在 finally 体内抛出:覆盖在途异常与完成,继续向外展开,
|
|
// 不再重入本 finally(否则已执行的 finally 会重复运行)。
|
|
self.pending_exception = None;
|
|
self.pending_error_kind = None;
|
|
self.pending_completion = None;
|
|
continue;
|
|
}
|
|
vm_trace!("unwind: entering finally at pc={}", finally_pc);
|
|
self.pending_exception = Some(self.exception_value.take().unwrap_or(JsValue::undefined()));
|
|
handler.finally_active = true;
|
|
self.try_stack.push(handler);
|
|
self.pc = finally_pc;
|
|
return Ok(());
|
|
}
|
|
if let Some(catch_pc) = handler.catch_pc {
|
|
vm_trace!("unwind: caught at pc={}", catch_pc);
|
|
let exc = self.exception_value.take().unwrap_or(JsValue::undefined());
|
|
self.regs[0] = exc;
|
|
self.pc = catch_pc;
|
|
return Ok(());
|
|
}
|
|
}
|
|
self.close_for_of_above(0);
|
|
while let Some(frame) = self.frames.pop() {
|
|
self.cell_stack.pop();
|
|
self.restore_frame(frame);
|
|
}
|
|
let exc = self.exception_value.take().unwrap_or(JsValue::undefined());
|
|
// 保留逃逸的 JsValue,使 for-of 的 next() 抛出时可重新抛出原值而非展平后的
|
|
// 字符串(由 dispatch_for_of_done/next 消费)。
|
|
self.last_uncaught_value = Some(exc);
|
|
let kind_str = self.pending_error_kind.take().unwrap_or("Error");
|
|
let exc_text = self.error_text(exc);
|
|
let msg = if exc_text.starts_with(kind_str) {
|
|
format!("uncaught {exc_text}")
|
|
} else {
|
|
format!("uncaught {kind_str}: {exc_text}")
|
|
};
|
|
vm_warn!("unwind: uncaught {}: {}", kind_str, exc_text);
|
|
Err(msg)
|
|
}
|
|
}
|