From 5a429f34cbaf09414f19eb0c056038591b7ab8d7 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 16 Sep 2026 16:41:38 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20dispatch=20=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E4=B8=8E=20VmHost=20=E5=8A=A8=E6=80=81=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=8B=86=E5=85=A5=20vm=20=E5=AD=90=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/vm/dispatch_loop.rs | 800 ++++++++++++++++ crates/oxide_vm/src/vm/dynamic.rs | 186 ++++ crates/oxide_vm/src/vm/mod.rs | 1165 +---------------------- crates/oxide_vm/src/vm/vmhost.rs | 185 ++++ 4 files changed, 1181 insertions(+), 1155 deletions(-) create mode 100644 crates/oxide_vm/src/vm/dispatch_loop.rs create mode 100644 crates/oxide_vm/src/vm/dynamic.rs create mode 100644 crates/oxide_vm/src/vm/vmhost.rs diff --git a/crates/oxide_vm/src/vm/dispatch_loop.rs b/crates/oxide_vm/src/vm/dispatch_loop.rs new file mode 100644 index 00000000..7421948b --- /dev/null +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -0,0 +1,800 @@ +//! dispatch 主循环:逐指令解码与 156 个 OpCode 臂分派、执行期 GC 安全点、 +//! 步数/分配上限采样与二元运算宏 `binary_arith!`。 + +use num_traits::Zero; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_runtime_api as coercion; +use oxide_types::value::JsValue; + +use super::Vm; +use crate::{vm_error, vm_trace, vm_warn}; + +macro_rules! binary_arith { + ($self:ident, $a:expr, $b:expr, $rd:expr, $op:tt, $check_zero:expr) => {{ + let lv = $self.regs[$a]; + let rv = $self.regs[$b]; + if lv.is_int() && rv.is_int() { + $self.regs[$rd] = JsValue::float(lv.as_int() as f64 $op rv.as_int() as f64); + } else if lv.is_bigint() && rv.is_bigint() { + let l = $self.bigint_value(lv).clone(); + let r = $self.bigint_value(rv).clone(); + if r.is_zero() && $check_zero { + // 仅除/模对 BigInt 零除数抛 RangeError;SUB/MUL 等其余二元运算 + // 遇到 0n 必须正常计算(Number 路径走 f64 inf/NaN)。 + $self.raise_error_kind("RangeError", "Division by zero")?; + $self.regs[$rd] = JsValue::undefined(); + } else { + $self.regs[$rd] = $self.new_bigint(l $op r); + } + } else { + // 注意:混合 BigInt/Number 检查不能在此前置(对象操作数如 + // `{valueOf: () => 2n}` 需先 ToPrimitive 再判定),统一放 coerce 之后。 + let l = $self.coerce_primitive_bounded(lv, false)?; + let r = $self.coerce_primitive_bounded(rv, false)?; + if l.is_bigint() && r.is_bigint() { + // 包装对象 coerce 后暴露双 BigInt(如 Object(2n) / 2n)。 + let lv = $self.bigint_value(l).clone(); + let rv = $self.bigint_value(r).clone(); + if rv.is_zero() && $check_zero { + $self.raise_error_kind("RangeError", "Division by zero")?; + $self.regs[$rd] = JsValue::undefined(); + } else { + $self.regs[$rd] = $self.new_bigint(lv $op rv); + } + } else if l.is_bigint() != r.is_bigint() { + // 包装对象 coerce 后暴露 BigInt 混合(如 Object(1n) - 1)。 + $self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions")?; + $self.regs[$rd] = JsValue::undefined(); + } else { + let ln = coercion::to_number(l); + let rn = coercion::to_number(r); + $self.regs[$rd] = JsValue::float(ln $op rn); + } + } + }} +} + +impl Vm { + pub(crate) fn dispatch(&mut self) -> Result { + // config.max_steps / max_alloc_bytes 逐指令只读且循环内不变:提到循环外, + // 免每次经 kernel_core Arc 指针追寻读取(热点内仅有的 config 访问)。 + let max_steps = self.kernel_core.config.max_steps; + let max_alloc_bytes = self.kernel_core.config.max_alloc_bytes; + let mut steps: u64 = 0; + // 小重入泵送盲区:native 终端循环泵送短 JS 重入时,顶层 steps 不推进、 + // 每次重入 dispatch 太短到不了循环内 64 指令采样点,分配上限结构性永不 + // 采样。重入边界按 hop 计数,每 64 跳强制一次顶层轻采样兜底。 + if self.native_call_depth > 0 { + self.reentry_hops += 1; + if self.reentry_hops & 0x3F == 0 { + if let Some(cap) = max_alloc_bytes { + let used = self.run_alloc_bytes(); + if used > cap { + vm_warn!( + "dispatch: memory limit {cap} exceeded at reentry hop {} (used {used}) at pc={}", + self.reentry_hops, + self.pc + ); + return Err(format!("VM memory limit {cap} exceeded (used {used}) at pc={}", self.pc)); + } + } + } + } + loop { + steps += 1; + // 执行期 GC 安全点:仅在顶层 dispatch(native_call_depth == 0) + // 的指令边界触发——嵌套 dispatch(builtin 经 call_function_sync 重入 + // 执行 JS 回调、generator/async 恢复)期间,调用方寄存器窗口副本存于 + // inline 状态(非 GC 根),此时回收会把调用方 regs 中的活值当死值释放。 + // 返回顶层后检查恢复,存活值此时已回拷为执行根。账目未超水位时仅 + // 少量字段比较。 + if self.native_call_depth == 0 { + // 峰值高水位:同一边界采样 session 堆账目上界 + let bytes = self.gc_state.session_bytes_allocated; + if bytes > self.gc_state.session_bytes_peak { + self.gc_state.session_bytes_peak = bytes; + } + // 单 run 分配包络高水位:O(1) 三计数器读(双 arena + session 账目)。 + let alloc = self.run_alloc_bytes(); + if alloc > self.gc_state.run_alloc_peak { + self.gc_state.run_alloc_peak = alloc; + } + if bytes >= self.gc_state.string_gc_watermark { + self.maybe_collect_session_strings(); + } + // 执行期两档收集:O(1) 包络超触发水位 → 门控(活跃/挂起 for-in) + // + epoch 晋升收集 + session 原地 sweep(边界契约见 maybe_collect_in_run)。 + if alloc >= self.gc_state.gc_watermark { + self.maybe_collect_in_run(); + } + } + if let Some(max_steps) = max_steps { + if steps > max_steps { + vm_warn!("dispatch: step limit {} exceeded at pc={}", max_steps, self.pc); + self.profiling.set_instruction_count(steps); + return Err(format!("VM step limit exceeded at pc={}", self.pc)); + } + } + // 单 run 分配上限:账目盲区(属性区扩容)靠两层采样兜住——轻层每 + // 64 指令读三个计数器,深层每 2^18 指令全量重算(含逐对象属性区 + // 重算);超限 run 按步数超限同款处理(默认 skip / --no-skip 下 + // fail),防单测试 arena 高水位拖垮宿主。 + if let Some(cap) = max_alloc_bytes { + let deep = (steps & 0x3FFFF) == 0; + if deep || (steps & 0x3F) == 0 { + let used = if deep { self.run_alloc_bytes_full() as usize } else { self.run_alloc_bytes() }; + if used > cap { + vm_warn!("dispatch: memory limit {cap} exceeded (used {used}) at pc={}", self.pc); + self.profiling.set_instruction_count(steps); + return Err(format!("VM memory limit {cap} exceeded (used {used}) at pc={}", self.pc)); + } + } + } + if self.pc >= self.bytecode.len() { + let tail: Vec = self + .bytecode + .iter() + .enumerate() + .rev() + .take(5) + .map(|(i, &instr)| format!("{i}:{:?}", opcode::opcode(instr))) + .collect(); + let fn_names: Vec = self + .frames + .iter() + .map(|f| { + self.kernel_core + .perm_interner() + .lookup(f.function_name) + .map(|s| s.to_string()) + .unwrap_or_else(|| "?".into()) + }) + .collect(); + vm_error!( + "dispatch: program counter out of bounds pc={} len={} frames={} cell_stack={} tail={:?} fns={:?}", + self.pc, + self.bytecode.len(), + self.frames.len(), + self.cell_stack.len(), + tail, + fn_names + ); + vm_error!("dispatch: program counter out of bounds pc={} len={}", self.pc, self.bytecode.len()); + self.profiling.instruction_count = steps; + return Err("program counter out of bounds".into()); + } + + let instr = self.bytecode[self.pc]; + let op = opcode::opcode(instr); + let rd = opcode::rd(instr) as usize; + let a = opcode::a(instr) as usize; + let b = opcode::b(instr) as usize; + self.pc += 1; + + match op { + OpCode::NOP => {} + + OpCode::HALT => { + vm_trace!("HALT: regs[0]={:?}", self.regs[0]); + self.profiling.set_instruction_count(steps); + return Ok(self.regs[0]); + } + + OpCode::LOAD_CONST => { + self.dispatch_load_const(rd, instr)?; + } + + OpCode::LOAD_GLOBAL => match self.dispatch_load_global(rd, instr) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::LOAD_GLOBAL_TYPEOF => { + self.dispatch_load_global_typeof(rd, instr)?; + } + + OpCode::CREATE_CLOSURE => { + self.dispatch_create_closure(rd, instr)?; + } + OpCode::MAKE_CELL => { + self.dispatch_make_cell(rd, instr)?; + } + OpCode::MAKE_CELL_FRESH => { + self.dispatch_make_cell_fresh(rd, instr)?; + } + OpCode::CELL_GET => { + self.dispatch_cell_get(rd, a, b)?; + } + OpCode::CELL_SET => { + self.dispatch_cell_set(a, b)?; + } + OpCode::LOAD_UPVALUE => { + self.dispatch_load_upvalue(rd, instr)?; + } + OpCode::STORE_UPVALUE => { + self.dispatch_store_upvalue(rd, a, b)?; + } + OpCode::CREATE_REGEXP => match self.dispatch_create_regexp(rd, a, b) { + Ok(Some(result)) => return Ok(result), + Ok(None) => {} + Err(e) => return Err(e), + }, + + OpCode::ADD => { + self.dispatch_add(rd, a, b)?; + } + + OpCode::CONCAT_N => { + self.dispatch_concat_n(rd, a)?; + } + + OpCode::SUB => { + binary_arith!(self, a, b, rd, -, false); + } + + OpCode::MUL => { + binary_arith!(self, a, b, rd, *, false); + } + + OpCode::DIV => { + binary_arith!(self, a, b, rd, /, true); + } + + OpCode::MOD => { + binary_arith!(self, a, b, rd, %, true); + } + + OpCode::EXP => { + self.dispatch_exp(rd, a, b)?; + } + + OpCode::NEG => { + self.dispatch_neg(rd, a)?; + } + + OpCode::BIT_AND => { + self.dispatch_bit_and(rd, a, b)?; + } + + OpCode::BIT_OR => { + self.dispatch_bit_or(rd, a, b)?; + } + + OpCode::BIT_XOR => { + self.dispatch_bit_xor(rd, a, b)?; + } + + OpCode::SHL => { + self.dispatch_shl(rd, a, b)?; + } + + OpCode::SHR => { + self.dispatch_shr(rd, a, b)?; + } + + OpCode::USHR => { + self.dispatch_ushr(rd, a, b)?; + } + + OpCode::BIT_NOT => { + self.dispatch_bit_not(rd, a)?; + } + + OpCode::EQ => { + self.dispatch_eq(rd, a, b)?; + } + + OpCode::NEQ => { + self.dispatch_neq(rd, a, b)?; + } + + OpCode::LT => { + self.dispatch_lt(rd, a, b)?; + } + + OpCode::GT => { + self.dispatch_gt(rd, a, b)?; + } + + OpCode::LTE => { + self.dispatch_lte(rd, a, b)?; + } + + OpCode::GTE => { + self.dispatch_gte(rd, a, b)?; + } + + OpCode::STRICT_EQ => { + self.dispatch_strict_eq(rd, a, b); + } + + OpCode::STRICT_NEQ => { + self.dispatch_strict_neq(rd, a, b); + } + + OpCode::UNARY_PLUS => { + self.dispatch_unary_plus(rd, a)?; + } + + OpCode::JMP => { + self.dispatch_jmp(instr); + } + + OpCode::BREAK => { + self.dispatch_break(instr)?; + } + + OpCode::CONTINUE => { + self.dispatch_continue(instr)?; + } + + OpCode::JMP_IF_FALSE => { + self.dispatch_jmp_if_false(rd, instr); + } + + OpCode::JMP_IF_TRUE => { + self.dispatch_jmp_if_true(rd, instr); + } + + OpCode::JMP_IF_NULLISH => { + self.dispatch_jmp_if_nullish(rd, instr); + } + + OpCode::LOAD_VAR => match self.dispatch_load_var(rd, a) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::STORE_VAR => match self.dispatch_store_var(rd, a, b) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::MOV => { + vm_trace!("MOV r{} = r{}", rd, a); + self.regs[rd] = self.regs[a]; + } + + OpCode::SPILL => { + self.dispatch_spill(rd)?; + } + + OpCode::UNSPILL => { + self.dispatch_unspill(rd)?; + } + + OpCode::CALL => match self.dispatch_call(rd, a, b) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::CALL_NATIVE => { + self.dispatch_call_native(rd, a, b)?; + } + + OpCode::CALL_SPREAD => match self.dispatch_call_spread(rd, a, b) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::NEW_EXPRESSION => match self.dispatch_new_expression(rd, a, b) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::NEW_EXPRESSION_SPREAD => match self.dispatch_new_expression_spread(rd, a, b) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::SUPER_CALL => match self.dispatch_super_call(rd, a) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::SUPER_CALL_SPREAD => match self.dispatch_super_call_spread(rd, a) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::SUPER_GET_PROP | OpCode::SUPER_STATIC_GET_PROP => { + match self.dispatch_super_get_prop(rd, a, b) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + } + } + + OpCode::SET_HOME_OBJECT => match self.dispatch_set_home_object(rd, a) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::DEFINE_ACCESSOR => { + self.dispatch_define_accessor(rd, a, b)?; + } + + OpCode::DEFINE_ACCESSOR_DYNAMIC => { + self.dispatch_define_accessor_dynamic(rd, a, b)?; + } + + OpCode::DEFINE_PROP => { + self.dispatch_define_prop(rd, a, b)?; + } + + OpCode::DEFINE_GLOBAL_PROP => { + self.dispatch_define_global_prop(rd, a, b)?; + } + + OpCode::DEFINE_GLOBAL_PROP_C => { + let key_idx = self.bytecode[self.pc] as u16; + self.pc += 1; + self.dispatch_define_global_prop_c(a, key_idx)?; + } + + OpCode::DEFINE_GLOBAL_PROP_IF_ABSENT => { + self.dispatch_define_global_prop_if_absent(rd, a, b)?; + } + + OpCode::DEFINE_GLOBAL_PROP_C_IF_ABSENT => { + let key_idx = self.bytecode[self.pc] as u16; + self.pc += 1; + self.dispatch_define_global_prop_c_if_absent(a, key_idx)?; + } + + OpCode::DELETE_GLOBAL_PROP_C => { + let key_idx = self.bytecode[self.pc] as u16; + self.pc += 1; + self.dispatch_delete_global_prop_c(rd, a, key_idx)?; + } + + OpCode::CAN_DECLARE_GLOBAL_FUNC => { + self.dispatch_can_declare_global_func(rd, b)?; + } + + OpCode::DEFINE_GLOBAL_FUNC_BIND => { + self.dispatch_define_global_func_bind(rd, a, b)?; + } + + OpCode::DEFINE_PROP_ATTRS => { + let attrs = self.bytecode[self.pc] as u8; + self.pc += 1; + self.dispatch_define_prop_attrs(rd, a, b, attrs)?; + } + + OpCode::DEFINE_ACCESSOR_ATTRS => { + let key_word = self.bytecode[self.pc]; + let attrs = self.bytecode[self.pc + 1] as u8; + self.pc += 2; + self.dispatch_define_accessor_attrs(rd, a, b, key_word, attrs)?; + } + + OpCode::RETURN => match self.dispatch_return(instr) { + Ok(Some(result)) => return Ok(result), + Ok(None) => {} + Err(e) => return Err(e), + }, + + OpCode::IC_GET_PROP + | OpCode::IC_SET_PROP + | OpCode::GET_PROP + | OpCode::SET_PROP + | OpCode::SET_PROP_BATCH + | OpCode::GET_PROP_DYNAMIC + | OpCode::SET_PROP_DYNAMIC + | OpCode::SET_ELEM + | OpCode::GET_PRIVATE + | OpCode::SET_PRIVATE + | OpCode::INIT_PRIVATE + | OpCode::PRIVATE_BRAND_IN => { + self.dispatch_property_op(op, rd, a, b)?; + } + + OpCode::NEW_OBJECT => { + self.dispatch_new_object(rd, instr)?; + } + + OpCode::NEW_SESSION_OBJECT => { + self.dispatch_new_session_object(rd)?; + } + + OpCode::CREATE_ARGUMENTS => { + self.dispatch_create_arguments(rd)?; + } + + OpCode::CREATE_REST_ARRAY => { + let fixed_count = opcode::b(instr) as usize; + self.dispatch_create_rest_array(rd, fixed_count)?; + } + + OpCode::NEW_ARRAY => { + self.dispatch_new_array(rd, instr); + } + + OpCode::COMPOUND_ADD => { + self.dispatch_compound_add(rd, a)?; + } + + OpCode::COMPOUND_SUB => { + self.dispatch_compound_sub(rd, a)?; + } + + OpCode::COMPOUND_MUL => { + self.dispatch_compound_mul(rd, a)?; + } + + OpCode::COMPOUND_DIV => { + self.dispatch_compound_div(rd, a)?; + } + + OpCode::COMPOUND_MOD => { + self.dispatch_compound_mod(rd, a)?; + } + + OpCode::COMPOUND_EXP => { + self.dispatch_compound_exp(rd, a)?; + } + + OpCode::COMPOUND_AND => { + self.dispatch_compound_bit_and(rd, a)?; + } + + OpCode::COMPOUND_OR => { + self.dispatch_compound_bit_or(rd, a)?; + } + + OpCode::COMPOUND_XOR => { + self.dispatch_compound_bit_xor(rd, a)?; + } + + OpCode::COMPOUND_SHL => { + self.dispatch_compound_shl(rd, a)?; + } + + OpCode::COMPOUND_SHR => { + self.dispatch_compound_shr(rd, a)?; + } + + OpCode::COMPOUND_USHR => { + self.dispatch_compound_ushr(rd, a)?; + } + + OpCode::TYPEOF => { + self.dispatch_typeof(rd, a); + } + + OpCode::TO_OBJECT => { + self.dispatch_to_object(rd)?; + } + + OpCode::VOID => { + self.dispatch_void(rd); + } + + OpCode::TEMPLATE_STR => { + self.dispatch_template_str(rd)?; + } + + OpCode::GET_TEMPLATE_OBJECT => { + self.dispatch_get_template_object(rd)?; + } + + OpCode::DELETE_PROP_STATIC => match self.dispatch_delete_prop_static(rd) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::DELETE_PROP_DYNAMIC => match self.dispatch_delete_prop_dynamic(rd, b) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::INSTANCEOF => { + self.dispatch_instanceof(rd, a, b)?; + } + + OpCode::IN => { + self.dispatch_in(rd, a, b)?; + } + + OpCode::NOT => { + self.dispatch_not(rd, a); + } + + OpCode::AND => { + self.dispatch_and(rd, a, b); + } + + OpCode::OR => { + self.dispatch_or(rd, a, b); + } + + OpCode::NULLISH => { + self.dispatch_nullish(rd, a, b); + } + + OpCode::INC_PRE => { + self.dispatch_inc_pre(rd, a)?; + } + + OpCode::INC_POST => { + self.dispatch_inc_post(rd, a)?; + } + + OpCode::DEC_PRE => { + self.dispatch_dec_pre(rd, a)?; + } + + OpCode::DEC_POST => { + self.dispatch_dec_post(rd, a)?; + } + + OpCode::MEMBER_INC + | OpCode::MEMBER_DEC + | OpCode::DYN_MEMBER_INC + | OpCode::DYN_MEMBER_DEC + | OpCode::COMPOUND_MEMBER_ADD + | OpCode::COMPOUND_MEMBER_SUB + | OpCode::COMPOUND_MEMBER_MUL + | OpCode::COMPOUND_MEMBER_DIV + | OpCode::COMPOUND_MEMBER_MOD + | OpCode::COMPOUND_MEMBER_EXP + | OpCode::COMPOUND_MEMBER_BIT_AND + | OpCode::COMPOUND_MEMBER_BIT_OR + | OpCode::COMPOUND_MEMBER_BIT_XOR + | OpCode::COMPOUND_MEMBER_SHL + | OpCode::COMPOUND_MEMBER_SHR + | OpCode::COMPOUND_MEMBER_USHR => { + self.dispatch_member_op(op, rd, a, b)?; + } + + OpCode::FOR_IN_INIT => { + self.dispatch_for_in_init(a)?; + } + + OpCode::FOR_IN_NEXT => { + self.dispatch_for_in_next(rd)?; + } + + OpCode::FOR_IN_DONE => { + self.dispatch_for_in_done(rd); + } + + OpCode::FOR_IN_CLEANUP => { + self.dispatch_for_in_cleanup(); + } + + OpCode::FOR_OF_INIT => { + self.dispatch_for_of_init(a)?; + } + + OpCode::FOR_OF_NEXT => { + self.dispatch_for_of_next(rd)?; + } + + OpCode::FOR_OF_DONE => { + self.dispatch_for_of_done(rd)?; + } + + OpCode::FOR_OF_CLOSE => { + self.dispatch_for_of_close()?; + } + + OpCode::FOR_AWAIT_OF_INIT => { + self.dispatch_for_await_of_init(a)?; + } + + OpCode::FOR_AWAIT_OF_NEXT => { + self.dispatch_for_await_of_next(rd)?; + } + + OpCode::FOR_AWAIT_OF_DONE => { + self.dispatch_for_await_of_done(rd, a)?; + } + + OpCode::FOR_AWAIT_OF_CLOSE => { + self.dispatch_for_await_of_close()?; + // 异步 IteratorClose 可能经 await 挂起(return() 的 promise), + // 挂起时须像 AWAIT 一样让内嵌 dispatch 返回,由恢复方快照状态。 + if self.async_suspended || self.async_gen_suspended { + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } + } + + OpCode::REST_OBJECT => { + self.dispatch_rest_object(rd, a, b)?; + } + + OpCode::SPREAD_OBJECT => { + self.dispatch_spread_object(rd, a)?; + } + + OpCode::THROW => match self.dispatch_throw(rd) { + Ok(true) => continue, + Ok(false) => {} + Err(e) => return Err(e), + }, + + OpCode::TRY_BEGIN => { + self.dispatch_try_begin(instr); + } + + OpCode::TRY_END => { + self.dispatch_try_end(); + } + + OpCode::TRY_FINALLY_BEGIN => { + self.dispatch_try_finally_begin(instr); + } + + OpCode::TRY_FINALLY_ENTER => { + self.dispatch_try_finally_enter(); + } + + OpCode::TRY_FINALLY_END => match self.dispatch_try_finally_end() { + Ok(Some(result)) => return Ok(result), + Ok(None) => {} + Err(e) => return Err(e), + }, + + OpCode::YIELD => { + // 生成器让出:把让出值存入信号,内嵌 dispatch 返回,恢复方快照挂起状态。 + let value = self.regs[rd]; + self.generator_suspended = Some(value); + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } + + OpCode::YIELD_STAR => { + // `yield*` 委托:取内层迭代器并推进一步。 + // 未 done → 挂起让出(存委托迭代器);done → 委托完成值写 reg 0 继续外层; + // unwind 捕获到异常 → 继续 dispatch(已展开到 catch/finally)。 + match self.dispatch_yield_star(rd)? { + crate::generator::YieldStarOutcome::Suspend(value) => { + self.generator_suspended = Some(value); + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } + crate::generator::YieldStarOutcome::Continue(value) => { + self.regs[0] = value; + } + crate::generator::YieldStarOutcome::Unwind => {} + } + } + + OpCode::SUSPEND_BODY => { + // 生成器调用时参数初始化结束:挂起在 body 起点;正常 next() 恢复时直接穿过。 + if self.generator_init_step { + self.generator_body_started = true; + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } + } + + OpCode::AWAIT => { + // 异步帧挂起:登记恢复反应后内嵌 dispatch 返回,恢复方快照挂起状态。 + self.dispatch_await(rd)?; + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } + + _ => { + return Err(format!("opcode {op} not yet implemented")); + } + } + } + } +} diff --git a/crates/oxide_vm/src/vm/dynamic.rs b/crates/oxide_vm/src/vm/dynamic.rs new file mode 100644 index 00000000..267af3d4 --- /dev/null +++ b/crates/oxide_vm/src/vm/dynamic.rs @@ -0,0 +1,186 @@ +//! 动态编译:`Function` 构造器与脚本动态编译入口,及子树 flat_id 重编号。 + +use std::sync::{Arc, OnceLock}; + +use oxide_bytecode::module::CompiledModule; +use oxide_bytecode::opcode::{self, OpCode}; +use oxide_types::object::PropAttributes; +use oxide_types::value::JsValue; + +use super::Vm; + +impl Vm { + /// 动态编译函数(`Function` 构造器路径):把参数列表与函数体 wrap 成匿名函数 + /// 源码,走完整编译链后取匿名函数模块追加进 VM 平表,返回对应函数对象。 + /// + /// # 源契约 + /// `params` 与 `body` 须为 `string_forge::source_escape` 产物(源码域转义 + /// 形态):孤立 surrogate / FFFD 单元以 `\uXXXX` 转义文本承载,反斜杠 + /// 原样透传。 + /// + /// # 步骤 + /// 1. wrap 源码 `function anonymous(p...) { body }`,parse + compile。 + /// 2. 取 `sub_modules[0]`(匿名函数模块),把其子树 flat_id 重编号到平表末尾 + /// 并重写子树内每条 `CREATE_CLOSURE` 的 imm16。 + /// 3. 同步扩容当前代际的常量缓存,建函数对象并设置 name/length。 + /// + /// # 边界与前提 + /// - 编译或解析失败返回 `Err`(由 builtin 层转 SyntaxError)。 + /// - 追加的子树原 flat_id 自 1 连续(flatten 后 1=匿名体,2…=其嵌套函数); + /// 新 id = 平表长度 + (old - 1)。 + /// - 动态函数在函数对象存活期间跨 run 有效:扩展落当前代际平表,存活函数 + /// 对象按代际引用保活该表;full_reset 重建 session 后失效。 + /// + /// # 副作用 + /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 + pub fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result { + // wrap 源码:末尾换行防止 body 以行注释结尾吞掉右花括号。 + let params_str = params.join(", "); + let source = format!("function anonymous({params_str}) {{\n{body}\n}}"); + + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, &source) + .map_err(|errs| errs.into_iter().map(|e| e.message).collect::>().join("\n"))?; + // 动态路径源契约:调用方以源码域转义(`string_forge::source_escape`) + // 形态传源;正则字面量源切片经 `source_escape_to_key` 还原注入 + // marker 后入池(编码源口径,区别于静态源的 `pool_key_plain`)。 + let mut module = oxide_compiler::compiler::Compiler::new() + .with_source_encoded(true) + .compile(&program)?; + let anonymous = module.sub_modules.remove(0); + // 形参数以编译结果为准:单个实参 "a,b,c" 拼接后解析为 3 个形参 + // (ES 动态函数把非末位实参以逗号连接成参数串再解析)。 + let formal_count = anonymous.n_args as i32; + + // 子树重编号 + 追加:base = 当前代际平表长度,DFS 前序压入,push 序即新 + // flat_id。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 + let table = self.current_table_mut(); + let base = table.modules.len() as u32; + let mut added = Vec::new(); + rehome_subtree(&anonymous, base, &mut added); + Arc::make_mut(&mut table.modules).extend(added); + // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 + table.immutables.resize(table.modules.len(), OnceLock::new()); + + let func_val = self.create_function_object(base, self.current_gen, false, false, false, false); + let func_obj = unsafe { &mut *func_val.as_js_object_ptr() }; + let length_si = self.kernel_core.perm_interner().intern("length").0; + let name_si = self.kernel_core.perm_interner().intern("name").0; + // length/name 为不可写不可枚举可配置,且 length 先于 name(规范属性顺序)。 + let attrs = PropAttributes::new(false, false, true); + let length_val = JsValue::int(formal_count); + let name_val = self.new_string("anonymous"); + self.define_data_property(func_obj, length_si, length_val, attrs)?; + self.define_data_property(func_obj, name_si, name_val, attrs)?; + Ok(func_val) + } + + /// 动态编译脚本(eval 脚本模式):把源码按脚本模式编译,var/函数声明落全局对象 + /// (属性 configurable:true,区别于普通脚本顶层的 false)。 + /// + /// # 步骤 + /// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true, + /// Compiler 置 is_eval_script=true)。 + /// 2. 整棵模块树(根 flat_id=0 + 嵌套函数)追加进平表:`rehome_subtree(&module, base+1)`, + /// 使根落 base、子函数 old→base+old,CREATE_CLOSURE imm16 同步重写。 + /// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。 + /// + /// # 边界与前提 + /// - 顶层 return 不报 SyntaxError(emit 无此检查,与 CLI 脚本路径一致)——已知偏差。 + /// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。 + /// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。 + /// + /// # 副作用 + /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 + pub fn create_dynamic_script(&mut self, code: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, code) + .map_err(|errs| errs.into_iter().map(|e| e.message).collect::>().join("\n"))?; + // eval 脚本:顶层 var/function 声明落全局属性 configurable:true。 + // 动态路径源契约同 `create_dynamic_function`(源码域转义形态传源)。 + let module = oxide_compiler::compiler::Compiler::new() + .with_eval_script(true) + .with_source_encoded(true) + .compile(&program)?; + // 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0 + // 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 + let table = self.current_table_mut(); + let base = table.modules.len() as u32; + let mut added = Vec::new(); + rehome_subtree(&module, base + 1, &mut added); + Arc::make_mut(&mut table.modules).extend(added); + // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 + table.immutables.resize(table.modules.len(), OnceLock::new()); + Ok(self.create_function_object(base, self.current_gen, false, false, false, false)) + } + + /// 动态编译脚本(普通脚本模式):顶层 var/function 声明落全局对象 + /// configurable:false,与静态脚本顶层一致(区别于 [`create_dynamic_script`] + /// 的 eval 脚本 configurable:true 面)。 + /// + /// 供 test262 宿主 `$262.evalScript` 使用:按 test262 harness 规范它是 + /// 当前 realm 的一段普通 script,不是 eval。 + /// + /// # 步骤 + /// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true, + /// 不设 is_eval_script,与普通脚本同一编译臂)。 + /// 2. 整棵模块树追加进平表:同 [`create_dynamic_script`] + /// (`rehome_subtree(&module, base+1)`,根落 base、子函数 old→base+old)。 + /// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。 + /// + /// # 边界与前提 + /// - 编译或解析失败返回 `Err`(由宿主层转 SyntaxError)。 + /// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。 + /// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。 + /// + /// # 副作用 + /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 + pub fn create_plain_dynamic_script(&mut self, code: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, code) + .map_err(|errs| errs.into_iter().map(|e| e.message).collect::>().join("\n"))?; + // 普通脚本:顶层 var/function 声明落全局属性 configurable:false, + // let/const 落全局词法环境。动态路径源契约同 `create_dynamic_function` + // (源码域转义形态传源)。 + let module = oxide_compiler::compiler::Compiler::new() + .with_source_encoded(true) + .compile(&program)?; + // 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0 + // 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 + let table = self.current_table_mut(); + let base = table.modules.len() as u32; + let mut added = Vec::new(); + rehome_subtree(&module, base + 1, &mut added); + Arc::make_mut(&mut table.modules).extend(added); + // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 + table.immutables.resize(table.modules.len(), OnceLock::new()); + Ok(self.create_function_object(base, self.current_gen, false, false, false, false)) + } +} + +/// 把 flatten 后的子模块子树重编号到平表偏移 `base`:DFS 前序拷贝进 `out`, +/// 新 flat_id = base + (old - 1),子树内每条 `CREATE_CLOSURE` 的 imm16 同步重写。 +/// 原子树 flat_id 自 1 连续,因此拷贝顺序即新 id 顺序,`out` 下标对齐平表槽位。 +fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec>) { + let new_id = base + module.flat_id - 1; + let mut bytecode = module.bytecode.to_vec(); + for instr in &mut bytecode { + if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE { + let old = opcode::imm16(*instr) as u32; + let new_flat = base + (old - 1); + *instr = opcode::encode( + OpCode::CREATE_CLOSURE, + opcode::rd(*instr), + (new_flat & 0xFF) as u8, + ((new_flat >> 8) & 0xFF) as u8, + ); + } + } + let mut rehomed = module.clone(); + rehomed.bytecode = Arc::from(bytecode); + rehomed.flat_id = new_id; + out.push(Arc::new(rehomed)); + for sub in &module.sub_modules { + rehome_subtree(sub, base, out); + } +} diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index 9c02dced..1364d192 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -1,35 +1,39 @@ #![allow(clippy::arc_with_non_send_sync)] use std::collections::{HashMap, VecDeque}; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; +#[cfg(test)] +use std::sync::OnceLock; -use num_traits::Zero; +#[cfg(test)] use oxide_bytecode::module::CompiledModule; -use oxide_bytecode::opcode::{self, OpCode}; +use oxide_bytecode::opcode; use smallvec::SmallVec; /// 初始化一个 session 的内置对象(global 槽位、各构造器与原型、IC 预热)。 pub use crate::bindings::init_kernel_builtins; use crate::native::NativeFn; +use crate::vm_debug; #[cfg(test)] use crate::vm_state::ForOfEntry; use crate::vm_state::{GcState, IterState, ProfilingState, SymbolState}; -use crate::{vm_debug, vm_error, vm_trace, vm_warn}; use oxide_kernel::kernel::{KernelCore, KernelSession}; -use oxide_runtime_api as coercion; use oxide_types::error::JsErrorKind; use oxide_types::mem::{Epoch, P}; -use oxide_types::object::{Cell, JsObject, NativeFnPtr, PropAttributes}; +use oxide_types::object::{Cell, JsObject, NativeFnPtr}; use oxide_types::private_key::INT_KEY_COUNT; use oxide_types::value::JsValue; mod call; +mod dispatch_loop; +mod dynamic; mod error; mod frames; mod gc_hooks; mod inline; mod props; mod tables; +mod vmhost; pub(crate) use frames::FrameArgs; pub use frames::{CallFrame, Completion, ForInIter, FrameContinuation, TryHandler}; @@ -137,61 +141,6 @@ pub(crate) fn format_error_message(name: &str, msg: &str) -> String { } } -#[allow(unused_macros)] -macro_rules! throw_err { - ($self:ident, $kind:ident, $msg:expr) => {{ - match $self.raise_error_kind(stringify!($kind), $msg) { - Ok(()) => continue, - Err(e) => return Err(e), - } - }}; -} - -macro_rules! binary_arith { - ($self:ident, $a:expr, $b:expr, $rd:expr, $op:tt, $check_zero:expr) => {{ - let lv = $self.regs[$a]; - let rv = $self.regs[$b]; - if lv.is_int() && rv.is_int() { - $self.regs[$rd] = JsValue::float(lv.as_int() as f64 $op rv.as_int() as f64); - } else if lv.is_bigint() && rv.is_bigint() { - let l = $self.bigint_value(lv).clone(); - let r = $self.bigint_value(rv).clone(); - if r.is_zero() && $check_zero { - // 仅除/模对 BigInt 零除数抛 RangeError;SUB/MUL 等其余二元运算 - // 遇到 0n 必须正常计算(Number 路径走 f64 inf/NaN)。 - $self.raise_error_kind("RangeError", "Division by zero")?; - $self.regs[$rd] = JsValue::undefined(); - } else { - $self.regs[$rd] = $self.new_bigint(l $op r); - } - } else { - // 注意:混合 BigInt/Number 检查不能在此前置(对象操作数如 - // `{valueOf: () => 2n}` 需先 ToPrimitive 再判定),统一放 coerce 之后。 - let l = $self.coerce_primitive_bounded(lv, false)?; - let r = $self.coerce_primitive_bounded(rv, false)?; - if l.is_bigint() && r.is_bigint() { - // 包装对象 coerce 后暴露双 BigInt(如 Object(2n) / 2n)。 - let lv = $self.bigint_value(l).clone(); - let rv = $self.bigint_value(r).clone(); - if rv.is_zero() && $check_zero { - $self.raise_error_kind("RangeError", "Division by zero")?; - $self.regs[$rd] = JsValue::undefined(); - } else { - $self.regs[$rd] = $self.new_bigint(lv $op rv); - } - } else if l.is_bigint() != r.is_bigint() { - // 包装对象 coerce 后暴露 BigInt 混合(如 Object(1n) - 1)。 - $self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions")?; - $self.regs[$rd] = JsValue::undefined(); - } else { - let ln = coercion::to_number(l); - let rn = coercion::to_number(r); - $self.regs[$rd] = JsValue::float(ln $op rn); - } - } - }} -} - /// 基于寄存器的 JS 虚拟机:持有执行状态、寄存器文件、调用栈与 session 内存。 /// /// 执行入口为 [`Vm::run`](见 `vm_runtime` 模块);内存模型为 epoch arena + @@ -452,749 +401,6 @@ impl Vm { // SAFETY: val 是字符串值,其 JsString 指针在生命周期内有效。 Some(unsafe { (*val.as_string_ptr()).to_owned_string() }) } - - pub(crate) fn dispatch(&mut self) -> Result { - // config.max_steps / max_alloc_bytes 逐指令只读且循环内不变:提到循环外, - // 免每次经 kernel_core Arc 指针追寻读取(热点内仅有的 config 访问)。 - let max_steps = self.kernel_core.config.max_steps; - let max_alloc_bytes = self.kernel_core.config.max_alloc_bytes; - let mut steps: u64 = 0; - // 小重入泵送盲区:native 终端循环泵送短 JS 重入时,顶层 steps 不推进、 - // 每次重入 dispatch 太短到不了循环内 64 指令采样点,分配上限结构性永不 - // 采样。重入边界按 hop 计数,每 64 跳强制一次顶层轻采样兜底。 - if self.native_call_depth > 0 { - self.reentry_hops += 1; - if self.reentry_hops & 0x3F == 0 { - if let Some(cap) = max_alloc_bytes { - let used = self.run_alloc_bytes(); - if used > cap { - vm_warn!( - "dispatch: memory limit {cap} exceeded at reentry hop {} (used {used}) at pc={}", - self.reentry_hops, - self.pc - ); - return Err(format!("VM memory limit {cap} exceeded (used {used}) at pc={}", self.pc)); - } - } - } - } - loop { - steps += 1; - // 执行期 GC 安全点:仅在顶层 dispatch(native_call_depth == 0) - // 的指令边界触发——嵌套 dispatch(builtin 经 call_function_sync 重入 - // 执行 JS 回调、generator/async 恢复)期间,调用方寄存器窗口副本存于 - // inline 状态(非 GC 根),此时回收会把调用方 regs 中的活值当死值释放。 - // 返回顶层后检查恢复,存活值此时已回拷为执行根。账目未超水位时仅 - // 少量字段比较。 - if self.native_call_depth == 0 { - // 峰值高水位:同一边界采样 session 堆账目上界 - let bytes = self.gc_state.session_bytes_allocated; - if bytes > self.gc_state.session_bytes_peak { - self.gc_state.session_bytes_peak = bytes; - } - // 单 run 分配包络高水位:O(1) 三计数器读(双 arena + session 账目)。 - let alloc = self.run_alloc_bytes(); - if alloc > self.gc_state.run_alloc_peak { - self.gc_state.run_alloc_peak = alloc; - } - if bytes >= self.gc_state.string_gc_watermark { - self.maybe_collect_session_strings(); - } - // 执行期两档收集:O(1) 包络超触发水位 → 门控(活跃/挂起 for-in) - // + epoch 晋升收集 + session 原地 sweep(边界契约见 maybe_collect_in_run)。 - if alloc >= self.gc_state.gc_watermark { - self.maybe_collect_in_run(); - } - } - if let Some(max_steps) = max_steps { - if steps > max_steps { - vm_warn!("dispatch: step limit {} exceeded at pc={}", max_steps, self.pc); - self.profiling.set_instruction_count(steps); - return Err(format!("VM step limit exceeded at pc={}", self.pc)); - } - } - // 单 run 分配上限:账目盲区(属性区扩容)靠两层采样兜住——轻层每 - // 64 指令读三个计数器,深层每 2^18 指令全量重算(含逐对象属性区 - // 重算);超限 run 按步数超限同款处理(默认 skip / --no-skip 下 - // fail),防单测试 arena 高水位拖垮宿主。 - if let Some(cap) = max_alloc_bytes { - let deep = (steps & 0x3FFFF) == 0; - if deep || (steps & 0x3F) == 0 { - let used = if deep { self.run_alloc_bytes_full() as usize } else { self.run_alloc_bytes() }; - if used > cap { - vm_warn!("dispatch: memory limit {cap} exceeded (used {used}) at pc={}", self.pc); - self.profiling.set_instruction_count(steps); - return Err(format!("VM memory limit {cap} exceeded (used {used}) at pc={}", self.pc)); - } - } - } - if self.pc >= self.bytecode.len() { - let tail: Vec = self - .bytecode - .iter() - .enumerate() - .rev() - .take(5) - .map(|(i, &instr)| format!("{i}:{:?}", opcode::opcode(instr))) - .collect(); - let fn_names: Vec = self - .frames - .iter() - .map(|f| { - self.kernel_core - .perm_interner() - .lookup(f.function_name) - .map(|s| s.to_string()) - .unwrap_or_else(|| "?".into()) - }) - .collect(); - vm_error!( - "dispatch: program counter out of bounds pc={} len={} frames={} cell_stack={} tail={:?} fns={:?}", - self.pc, - self.bytecode.len(), - self.frames.len(), - self.cell_stack.len(), - tail, - fn_names - ); - vm_error!("dispatch: program counter out of bounds pc={} len={}", self.pc, self.bytecode.len()); - self.profiling.instruction_count = steps; - return Err("program counter out of bounds".into()); - } - - let instr = self.bytecode[self.pc]; - let op = opcode::opcode(instr); - let rd = opcode::rd(instr) as usize; - let a = opcode::a(instr) as usize; - let b = opcode::b(instr) as usize; - self.pc += 1; - - match op { - OpCode::NOP => {} - - OpCode::HALT => { - vm_trace!("HALT: regs[0]={:?}", self.regs[0]); - self.profiling.set_instruction_count(steps); - return Ok(self.regs[0]); - } - - OpCode::LOAD_CONST => { - self.dispatch_load_const(rd, instr)?; - } - - OpCode::LOAD_GLOBAL => match self.dispatch_load_global(rd, instr) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::LOAD_GLOBAL_TYPEOF => { - self.dispatch_load_global_typeof(rd, instr)?; - } - - OpCode::CREATE_CLOSURE => { - self.dispatch_create_closure(rd, instr)?; - } - OpCode::MAKE_CELL => { - self.dispatch_make_cell(rd, instr)?; - } - OpCode::MAKE_CELL_FRESH => { - self.dispatch_make_cell_fresh(rd, instr)?; - } - OpCode::CELL_GET => { - self.dispatch_cell_get(rd, a, b)?; - } - OpCode::CELL_SET => { - self.dispatch_cell_set(a, b)?; - } - OpCode::LOAD_UPVALUE => { - self.dispatch_load_upvalue(rd, instr)?; - } - OpCode::STORE_UPVALUE => { - self.dispatch_store_upvalue(rd, a, b)?; - } - OpCode::CREATE_REGEXP => match self.dispatch_create_regexp(rd, a, b) { - Ok(Some(result)) => return Ok(result), - Ok(None) => {} - Err(e) => return Err(e), - }, - - OpCode::ADD => { - self.dispatch_add(rd, a, b)?; - } - - OpCode::CONCAT_N => { - self.dispatch_concat_n(rd, a)?; - } - - OpCode::SUB => { - binary_arith!(self, a, b, rd, -, false); - } - - OpCode::MUL => { - binary_arith!(self, a, b, rd, *, false); - } - - OpCode::DIV => { - binary_arith!(self, a, b, rd, /, true); - } - - OpCode::MOD => { - binary_arith!(self, a, b, rd, %, true); - } - - OpCode::EXP => { - self.dispatch_exp(rd, a, b)?; - } - - OpCode::NEG => { - self.dispatch_neg(rd, a)?; - } - - OpCode::BIT_AND => { - self.dispatch_bit_and(rd, a, b)?; - } - - OpCode::BIT_OR => { - self.dispatch_bit_or(rd, a, b)?; - } - - OpCode::BIT_XOR => { - self.dispatch_bit_xor(rd, a, b)?; - } - - OpCode::SHL => { - self.dispatch_shl(rd, a, b)?; - } - - OpCode::SHR => { - self.dispatch_shr(rd, a, b)?; - } - - OpCode::USHR => { - self.dispatch_ushr(rd, a, b)?; - } - - OpCode::BIT_NOT => { - self.dispatch_bit_not(rd, a)?; - } - - OpCode::EQ => { - self.dispatch_eq(rd, a, b)?; - } - - OpCode::NEQ => { - self.dispatch_neq(rd, a, b)?; - } - - OpCode::LT => { - self.dispatch_lt(rd, a, b)?; - } - - OpCode::GT => { - self.dispatch_gt(rd, a, b)?; - } - - OpCode::LTE => { - self.dispatch_lte(rd, a, b)?; - } - - OpCode::GTE => { - self.dispatch_gte(rd, a, b)?; - } - - OpCode::STRICT_EQ => { - self.dispatch_strict_eq(rd, a, b); - } - - OpCode::STRICT_NEQ => { - self.dispatch_strict_neq(rd, a, b); - } - - OpCode::UNARY_PLUS => { - self.dispatch_unary_plus(rd, a)?; - } - - OpCode::JMP => { - self.dispatch_jmp(instr); - } - - OpCode::BREAK => { - self.dispatch_break(instr)?; - } - - OpCode::CONTINUE => { - self.dispatch_continue(instr)?; - } - - OpCode::JMP_IF_FALSE => { - self.dispatch_jmp_if_false(rd, instr); - } - - OpCode::JMP_IF_TRUE => { - self.dispatch_jmp_if_true(rd, instr); - } - - OpCode::JMP_IF_NULLISH => { - self.dispatch_jmp_if_nullish(rd, instr); - } - - OpCode::LOAD_VAR => match self.dispatch_load_var(rd, a) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::STORE_VAR => match self.dispatch_store_var(rd, a, b) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::MOV => { - vm_trace!("MOV r{} = r{}", rd, a); - self.regs[rd] = self.regs[a]; - } - - OpCode::SPILL => { - self.dispatch_spill(rd)?; - } - - OpCode::UNSPILL => { - self.dispatch_unspill(rd)?; - } - - OpCode::CALL => match self.dispatch_call(rd, a, b) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::CALL_NATIVE => { - self.dispatch_call_native(rd, a, b)?; - } - - OpCode::CALL_SPREAD => match self.dispatch_call_spread(rd, a, b) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::NEW_EXPRESSION => match self.dispatch_new_expression(rd, a, b) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::NEW_EXPRESSION_SPREAD => match self.dispatch_new_expression_spread(rd, a, b) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::SUPER_CALL => match self.dispatch_super_call(rd, a) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::SUPER_CALL_SPREAD => match self.dispatch_super_call_spread(rd, a) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::SUPER_GET_PROP | OpCode::SUPER_STATIC_GET_PROP => { - match self.dispatch_super_get_prop(rd, a, b) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - } - } - - OpCode::SET_HOME_OBJECT => match self.dispatch_set_home_object(rd, a) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::DEFINE_ACCESSOR => { - self.dispatch_define_accessor(rd, a, b)?; - } - - OpCode::DEFINE_ACCESSOR_DYNAMIC => { - self.dispatch_define_accessor_dynamic(rd, a, b)?; - } - - OpCode::DEFINE_PROP => { - self.dispatch_define_prop(rd, a, b)?; - } - - OpCode::DEFINE_GLOBAL_PROP => { - self.dispatch_define_global_prop(rd, a, b)?; - } - - OpCode::DEFINE_GLOBAL_PROP_C => { - let key_idx = self.bytecode[self.pc] as u16; - self.pc += 1; - self.dispatch_define_global_prop_c(a, key_idx)?; - } - - OpCode::DEFINE_GLOBAL_PROP_IF_ABSENT => { - self.dispatch_define_global_prop_if_absent(rd, a, b)?; - } - - OpCode::DEFINE_GLOBAL_PROP_C_IF_ABSENT => { - let key_idx = self.bytecode[self.pc] as u16; - self.pc += 1; - self.dispatch_define_global_prop_c_if_absent(a, key_idx)?; - } - - OpCode::DELETE_GLOBAL_PROP_C => { - let key_idx = self.bytecode[self.pc] as u16; - self.pc += 1; - self.dispatch_delete_global_prop_c(rd, a, key_idx)?; - } - - OpCode::CAN_DECLARE_GLOBAL_FUNC => { - self.dispatch_can_declare_global_func(rd, b)?; - } - - OpCode::DEFINE_GLOBAL_FUNC_BIND => { - self.dispatch_define_global_func_bind(rd, a, b)?; - } - - OpCode::DEFINE_PROP_ATTRS => { - let attrs = self.bytecode[self.pc] as u8; - self.pc += 1; - self.dispatch_define_prop_attrs(rd, a, b, attrs)?; - } - - OpCode::DEFINE_ACCESSOR_ATTRS => { - let key_word = self.bytecode[self.pc]; - let attrs = self.bytecode[self.pc + 1] as u8; - self.pc += 2; - self.dispatch_define_accessor_attrs(rd, a, b, key_word, attrs)?; - } - - OpCode::RETURN => match self.dispatch_return(instr) { - Ok(Some(result)) => return Ok(result), - Ok(None) => {} - Err(e) => return Err(e), - }, - - OpCode::IC_GET_PROP - | OpCode::IC_SET_PROP - | OpCode::GET_PROP - | OpCode::SET_PROP - | OpCode::SET_PROP_BATCH - | OpCode::GET_PROP_DYNAMIC - | OpCode::SET_PROP_DYNAMIC - | OpCode::SET_ELEM - | OpCode::GET_PRIVATE - | OpCode::SET_PRIVATE - | OpCode::INIT_PRIVATE - | OpCode::PRIVATE_BRAND_IN => { - self.dispatch_property_op(op, rd, a, b)?; - } - - OpCode::NEW_OBJECT => { - self.dispatch_new_object(rd, instr)?; - } - - OpCode::NEW_SESSION_OBJECT => { - self.dispatch_new_session_object(rd)?; - } - - OpCode::CREATE_ARGUMENTS => { - self.dispatch_create_arguments(rd)?; - } - - OpCode::CREATE_REST_ARRAY => { - let fixed_count = opcode::b(instr) as usize; - self.dispatch_create_rest_array(rd, fixed_count)?; - } - - OpCode::NEW_ARRAY => { - self.dispatch_new_array(rd, instr); - } - - OpCode::COMPOUND_ADD => { - self.dispatch_compound_add(rd, a)?; - } - - OpCode::COMPOUND_SUB => { - self.dispatch_compound_sub(rd, a)?; - } - - OpCode::COMPOUND_MUL => { - self.dispatch_compound_mul(rd, a)?; - } - - OpCode::COMPOUND_DIV => { - self.dispatch_compound_div(rd, a)?; - } - - OpCode::COMPOUND_MOD => { - self.dispatch_compound_mod(rd, a)?; - } - - OpCode::COMPOUND_EXP => { - self.dispatch_compound_exp(rd, a)?; - } - - OpCode::COMPOUND_AND => { - self.dispatch_compound_bit_and(rd, a)?; - } - - OpCode::COMPOUND_OR => { - self.dispatch_compound_bit_or(rd, a)?; - } - - OpCode::COMPOUND_XOR => { - self.dispatch_compound_bit_xor(rd, a)?; - } - - OpCode::COMPOUND_SHL => { - self.dispatch_compound_shl(rd, a)?; - } - - OpCode::COMPOUND_SHR => { - self.dispatch_compound_shr(rd, a)?; - } - - OpCode::COMPOUND_USHR => { - self.dispatch_compound_ushr(rd, a)?; - } - - OpCode::TYPEOF => { - self.dispatch_typeof(rd, a); - } - - OpCode::TO_OBJECT => { - self.dispatch_to_object(rd)?; - } - - OpCode::VOID => { - self.dispatch_void(rd); - } - - OpCode::TEMPLATE_STR => { - self.dispatch_template_str(rd)?; - } - - OpCode::GET_TEMPLATE_OBJECT => { - self.dispatch_get_template_object(rd)?; - } - - OpCode::DELETE_PROP_STATIC => match self.dispatch_delete_prop_static(rd) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::DELETE_PROP_DYNAMIC => match self.dispatch_delete_prop_dynamic(rd, b) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::INSTANCEOF => { - self.dispatch_instanceof(rd, a, b)?; - } - - OpCode::IN => { - self.dispatch_in(rd, a, b)?; - } - - OpCode::NOT => { - self.dispatch_not(rd, a); - } - - OpCode::AND => { - self.dispatch_and(rd, a, b); - } - - OpCode::OR => { - self.dispatch_or(rd, a, b); - } - - OpCode::NULLISH => { - self.dispatch_nullish(rd, a, b); - } - - OpCode::INC_PRE => { - self.dispatch_inc_pre(rd, a)?; - } - - OpCode::INC_POST => { - self.dispatch_inc_post(rd, a)?; - } - - OpCode::DEC_PRE => { - self.dispatch_dec_pre(rd, a)?; - } - - OpCode::DEC_POST => { - self.dispatch_dec_post(rd, a)?; - } - - OpCode::MEMBER_INC - | OpCode::MEMBER_DEC - | OpCode::DYN_MEMBER_INC - | OpCode::DYN_MEMBER_DEC - | OpCode::COMPOUND_MEMBER_ADD - | OpCode::COMPOUND_MEMBER_SUB - | OpCode::COMPOUND_MEMBER_MUL - | OpCode::COMPOUND_MEMBER_DIV - | OpCode::COMPOUND_MEMBER_MOD - | OpCode::COMPOUND_MEMBER_EXP - | OpCode::COMPOUND_MEMBER_BIT_AND - | OpCode::COMPOUND_MEMBER_BIT_OR - | OpCode::COMPOUND_MEMBER_BIT_XOR - | OpCode::COMPOUND_MEMBER_SHL - | OpCode::COMPOUND_MEMBER_SHR - | OpCode::COMPOUND_MEMBER_USHR => { - self.dispatch_member_op(op, rd, a, b)?; - } - - OpCode::FOR_IN_INIT => { - self.dispatch_for_in_init(a)?; - } - - OpCode::FOR_IN_NEXT => { - self.dispatch_for_in_next(rd)?; - } - - OpCode::FOR_IN_DONE => { - self.dispatch_for_in_done(rd); - } - - OpCode::FOR_IN_CLEANUP => { - self.dispatch_for_in_cleanup(); - } - - OpCode::FOR_OF_INIT => { - self.dispatch_for_of_init(a)?; - } - - OpCode::FOR_OF_NEXT => { - self.dispatch_for_of_next(rd)?; - } - - OpCode::FOR_OF_DONE => { - self.dispatch_for_of_done(rd)?; - } - - OpCode::FOR_OF_CLOSE => { - self.dispatch_for_of_close()?; - } - - OpCode::FOR_AWAIT_OF_INIT => { - self.dispatch_for_await_of_init(a)?; - } - - OpCode::FOR_AWAIT_OF_NEXT => { - self.dispatch_for_await_of_next(rd)?; - } - - OpCode::FOR_AWAIT_OF_DONE => { - self.dispatch_for_await_of_done(rd, a)?; - } - - OpCode::FOR_AWAIT_OF_CLOSE => { - self.dispatch_for_await_of_close()?; - // 异步 IteratorClose 可能经 await 挂起(return() 的 promise), - // 挂起时须像 AWAIT 一样让内嵌 dispatch 返回,由恢复方快照状态。 - if self.async_suspended || self.async_gen_suspended { - self.profiling.set_instruction_count(steps); - return Ok(JsValue::undefined()); - } - } - - OpCode::REST_OBJECT => { - self.dispatch_rest_object(rd, a, b)?; - } - - OpCode::SPREAD_OBJECT => { - self.dispatch_spread_object(rd, a)?; - } - - OpCode::THROW => match self.dispatch_throw(rd) { - Ok(true) => continue, - Ok(false) => {} - Err(e) => return Err(e), - }, - - OpCode::TRY_BEGIN => { - self.dispatch_try_begin(instr); - } - - OpCode::TRY_END => { - self.dispatch_try_end(); - } - - OpCode::TRY_FINALLY_BEGIN => { - self.dispatch_try_finally_begin(instr); - } - - OpCode::TRY_FINALLY_ENTER => { - self.dispatch_try_finally_enter(); - } - - OpCode::TRY_FINALLY_END => match self.dispatch_try_finally_end() { - Ok(Some(result)) => return Ok(result), - Ok(None) => {} - Err(e) => return Err(e), - }, - - OpCode::YIELD => { - // 生成器让出:把让出值存入信号,内嵌 dispatch 返回,恢复方快照挂起状态。 - let value = self.regs[rd]; - self.generator_suspended = Some(value); - self.profiling.set_instruction_count(steps); - return Ok(JsValue::undefined()); - } - - OpCode::YIELD_STAR => { - // `yield*` 委托:取内层迭代器并推进一步。 - // 未 done → 挂起让出(存委托迭代器);done → 委托完成值写 reg 0 继续外层; - // unwind 捕获到异常 → 继续 dispatch(已展开到 catch/finally)。 - match self.dispatch_yield_star(rd)? { - crate::generator::YieldStarOutcome::Suspend(value) => { - self.generator_suspended = Some(value); - self.profiling.set_instruction_count(steps); - return Ok(JsValue::undefined()); - } - crate::generator::YieldStarOutcome::Continue(value) => { - self.regs[0] = value; - } - crate::generator::YieldStarOutcome::Unwind => {} - } - } - - OpCode::SUSPEND_BODY => { - // 生成器调用时参数初始化结束:挂起在 body 起点;正常 next() 恢复时直接穿过。 - if self.generator_init_step { - self.generator_body_started = true; - self.profiling.set_instruction_count(steps); - return Ok(JsValue::undefined()); - } - } - - OpCode::AWAIT => { - // 异步帧挂起:登记恢复反应后内嵌 dispatch 返回,恢复方快照挂起状态。 - self.dispatch_await(rd)?; - self.profiling.set_instruction_count(steps); - return Ok(JsValue::undefined()); - } - - _ => { - return Err(format!("opcode {op} not yet implemented")); - } - } - } - } } impl Default for Vm { @@ -1203,357 +409,6 @@ impl Default for Vm { } } -impl oxide_runtime_api::VmHost for Vm { - fn reg(&self, idx: u8) -> JsValue { - self.reg(idx) - } - fn set_reg(&mut self, idx: u8, val: JsValue) { - self.set_reg(idx, val); - } - fn native_overflow_count(&self) -> usize { - self.native_overflow_count - } - fn native_overflow_at(&self, i: usize) -> JsValue { - self.spill_stack[self.native_overflow_base + i] - } - fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject { - self.alloc_object(obj) - } - fn new_string(&mut self, s: &str) -> JsValue { - self.new_string(s) - } - fn new_string_owned(&mut self, s: String) -> JsValue { - Vm::new_string_owned(self, s) - } - fn new_bigint(&mut self, v: num_bigint::BigInt) -> JsValue { - Vm::new_bigint(self, v) - } - fn bigint_value(&mut self, val: JsValue) -> &num_bigint::BigInt { - Vm::bigint_value(self, val) - } - fn kernel_core(&self) -> &Arc { - self.kernel_core() - } - fn session(&self) -> &KernelSession { - self.session() - } - fn epoch(&self) -> &Epoch { - self.epoch() - } - fn take_uncaught_value(&mut self) -> Option { - self.last_uncaught_value.take() - } - fn restore_uncaught_value(&mut self, value: Option) { - self.last_uncaught_value = value; - } - fn property_key_si(&mut self, val: JsValue) -> u32 { - // Object-key conversion (to_string_full) failures degrade to the empty key here: - // this trait path is used by Reflect/Object builtins; computed property access - // uses the inherent Result-returning version to preserve the full exception. - self.property_key_si(val) - .unwrap_or_else(|_| self.kernel_core.perm_interner().intern("").0) - } - fn to_property_key_si(&mut self, val: JsValue) -> Result { - // ToPropertyKey 完整路径:转换异常(对象 ToPrimitive 抛错 / Symbol 处理) - // 原样返回,供需传播异常的 builtins(groupBy / __defineGetter__ 等)使用。 - self.property_key_si(val) - } - fn string_key_si(&mut self, s: &str) -> u32 { - // 与运行时字符串键同口径:含 FFFD 的键文本按 encode_key 形态入键空间 - // (FFFD 键的 JSON.parse 键与字面量/拼接构造的运行时键身份一致)。 - self.string_key_text(s) - } - fn string_units(&self, val: JsValue) -> std::borrow::Cow<'_, [u16]> { - // SAFETY: 调用方保证 val 为字符串值。perm 串由内核持有永不释放;session - // 串仅经 &mut self 路径(new_string/GC)释放,&self 借用期间编译器强制 - // 不存在 &mut 存续,字符串不会在借用期内回收。 - unsafe { (*val.as_string_ptr()).units() } - } - fn new_string_units(&mut self, units: &[u16]) -> JsValue { - Vm::new_string_units(self, units) - } - fn new_string_units_owned(&mut self, units: Vec) -> JsValue { - Vm::new_string_units_owned(self, units) - } - fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option { - self.resolve_property(obj, prop_name_si) - } - fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option { - self.get_own_property_slot(obj, prop_name_si) - } - fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result { - self.ordinary_get(obj, prop_name_si, receiver) - } - fn ordinary_set( - &mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, strict: bool, - ) -> Result<(), String> { - self.ordinary_set(obj, prop_name_si, val, receiver, strict) - } - fn define_data_property( - &mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes, - ) -> Result<(), String> { - self.define_data_property(obj, prop_name_si, val, attributes) - } - fn define_accessor_property( - &mut self, obj: &mut JsObject, prop_name_si: u32, get: JsValue, set: JsValue, attributes: PropAttributes, - ) -> Result<(), String> { - self.define_accessor_property(obj, prop_name_si, get, set, attributes) - } - fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) { - self.set_or_create_prop_value(obj, prop_name_si, val) - } - fn sync_global_builtin_mirror(&mut self, obj: &JsObject, key_si: u32, val: JsValue) { - self.sync_global_builtin_mirror(obj, key_si, val) - } - fn lookup_str(&self, val: JsValue) -> Option { - self.lookup_str(val) - } - fn coerce_primitive_bounded(&mut self, value: JsValue, prefer_string: bool) -> Result { - self.coerce_primitive_bounded(value, prefer_string) - } - fn coerce_number_bounded(&mut self, value: JsValue) -> Result { - self.coerce_number_bounded(value) - } - fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result { - self.call_function_sync(callee, receiver, args) - } - fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result, String> { - self.checked_object_ptr(val, error_msg) - } - fn raise_type_error(&mut self, msg: &str) -> Result<(), String> { - self.raise_type_error(msg) - } - fn error_message_text(&self, kind: &str, msg: &str) -> String { - self.error_message_text(kind, msg) - } - fn call_stack_function_names(&self) -> Vec { - self.frames - .iter() - .rev() - .map(|f| { - self.kernel_core - .perm_interner() - .lookup(f.function_name) - .unwrap_or("") - .to_string() - }) - .collect() - } - fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue { - self.promote_if_needed_for_write_ptr(target_ptr, value) - } - fn step_rng(&mut self) { - self.step_rng() - } - fn math_rng_value(&self) -> f64 { - self.math_rng_value() - } - fn sub_module_function_name(&self, gen: u32, sub_idx: u16) -> String { - self.tables - .get(&gen) - .and_then(|t| t.modules.get(sub_idx as usize)) - .and_then(|m| m.function_name.clone()) - .unwrap_or_default() - } - fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result { - self.create_dynamic_function(params, body) - } - fn create_dynamic_script(&mut self, code: &str) -> Result { - self.create_dynamic_script(code) - } - fn symbol_intern(&mut self, desc: Option) -> u32 { - self.symbols.intern(desc) - } - fn symbol_description(&self, idx: u32) -> Option<&str> { - self.symbols.description(idx) - } - fn symbol_lookup_global(&self, key: &str) -> Option { - self.symbols.lookup_global(key) - } - fn symbol_register_global(&mut self, key: String, idx: u32) { - self.symbols.register_global(key, idx) - } - fn symbol_key_for_id(&self, idx: u32) -> Option { - self.symbols.key_for_id(idx) - } -} - -impl Vm { - /// 动态编译函数(`Function` 构造器路径):把参数列表与函数体 wrap 成匿名函数 - /// 源码,走完整编译链后取匿名函数模块追加进 VM 平表,返回对应函数对象。 - /// - /// # 源契约 - /// `params` 与 `body` 须为 `string_forge::source_escape` 产物(源码域转义 - /// 形态):孤立 surrogate / FFFD 单元以 `\uXXXX` 转义文本承载,反斜杠 - /// 原样透传。 - /// - /// # 步骤 - /// 1. wrap 源码 `function anonymous(p...) { body }`,parse + compile。 - /// 2. 取 `sub_modules[0]`(匿名函数模块),把其子树 flat_id 重编号到平表末尾 - /// 并重写子树内每条 `CREATE_CLOSURE` 的 imm16。 - /// 3. 同步扩容当前代际的常量缓存,建函数对象并设置 name/length。 - /// - /// # 边界与前提 - /// - 编译或解析失败返回 `Err`(由 builtin 层转 SyntaxError)。 - /// - 追加的子树原 flat_id 自 1 连续(flatten 后 1=匿名体,2…=其嵌套函数); - /// 新 id = 平表长度 + (old - 1)。 - /// - 动态函数在函数对象存活期间跨 run 有效:扩展落当前代际平表,存活函数 - /// 对象按代际引用保活该表;full_reset 重建 session 后失效。 - /// - /// # 副作用 - /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 - pub fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result { - // wrap 源码:末尾换行防止 body 以行注释结尾吞掉右花括号。 - let params_str = params.join(", "); - let source = format!("function anonymous({params_str}) {{\n{body}\n}}"); - - let allocator = oxide_parser::Allocator::default(); - let program = oxide_parser::parse(&allocator, &source) - .map_err(|errs| errs.into_iter().map(|e| e.message).collect::>().join("\n"))?; - // 动态路径源契约:调用方以源码域转义(`string_forge::source_escape`) - // 形态传源;正则字面量源切片经 `source_escape_to_key` 还原注入 - // marker 后入池(编码源口径,区别于静态源的 `pool_key_plain`)。 - let mut module = oxide_compiler::compiler::Compiler::new() - .with_source_encoded(true) - .compile(&program)?; - let anonymous = module.sub_modules.remove(0); - // 形参数以编译结果为准:单个实参 "a,b,c" 拼接后解析为 3 个形参 - // (ES 动态函数把非末位实参以逗号连接成参数串再解析)。 - let formal_count = anonymous.n_args as i32; - - // 子树重编号 + 追加:base = 当前代际平表长度,DFS 前序压入,push 序即新 - // flat_id。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 - let table = self.current_table_mut(); - let base = table.modules.len() as u32; - let mut added = Vec::new(); - rehome_subtree(&anonymous, base, &mut added); - Arc::make_mut(&mut table.modules).extend(added); - // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 - table.immutables.resize(table.modules.len(), OnceLock::new()); - - let func_val = self.create_function_object(base, self.current_gen, false, false, false, false); - let func_obj = unsafe { &mut *func_val.as_js_object_ptr() }; - let length_si = self.kernel_core.perm_interner().intern("length").0; - let name_si = self.kernel_core.perm_interner().intern("name").0; - // length/name 为不可写不可枚举可配置,且 length 先于 name(规范属性顺序)。 - let attrs = PropAttributes::new(false, false, true); - let length_val = JsValue::int(formal_count); - let name_val = self.new_string("anonymous"); - self.define_data_property(func_obj, length_si, length_val, attrs)?; - self.define_data_property(func_obj, name_si, name_val, attrs)?; - Ok(func_val) - } - - /// 动态编译脚本(eval 脚本模式):把源码按脚本模式编译,var/函数声明落全局对象 - /// (属性 configurable:true,区别于普通脚本顶层的 false)。 - /// - /// # 步骤 - /// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true, - /// Compiler 置 is_eval_script=true)。 - /// 2. 整棵模块树(根 flat_id=0 + 嵌套函数)追加进平表:`rehome_subtree(&module, base+1)`, - /// 使根落 base、子函数 old→base+old,CREATE_CLOSURE imm16 同步重写。 - /// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。 - /// - /// # 边界与前提 - /// - 顶层 return 不报 SyntaxError(emit 无此检查,与 CLI 脚本路径一致)——已知偏差。 - /// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。 - /// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。 - /// - /// # 副作用 - /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 - pub fn create_dynamic_script(&mut self, code: &str) -> Result { - let allocator = oxide_parser::Allocator::default(); - let program = oxide_parser::parse(&allocator, code) - .map_err(|errs| errs.into_iter().map(|e| e.message).collect::>().join("\n"))?; - // eval 脚本:顶层 var/function 声明落全局属性 configurable:true。 - // 动态路径源契约同 `create_dynamic_function`(源码域转义形态传源)。 - let module = oxide_compiler::compiler::Compiler::new() - .with_eval_script(true) - .with_source_encoded(true) - .compile(&program)?; - // 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0 - // 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 - let table = self.current_table_mut(); - let base = table.modules.len() as u32; - let mut added = Vec::new(); - rehome_subtree(&module, base + 1, &mut added); - Arc::make_mut(&mut table.modules).extend(added); - // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 - table.immutables.resize(table.modules.len(), OnceLock::new()); - Ok(self.create_function_object(base, self.current_gen, false, false, false, false)) - } - - /// 动态编译脚本(普通脚本模式):顶层 var/function 声明落全局对象 - /// configurable:false,与静态脚本顶层一致(区别于 [`create_dynamic_script`] - /// 的 eval 脚本 configurable:true 面)。 - /// - /// 供 test262 宿主 `$262.evalScript` 使用:按 test262 harness 规范它是 - /// 当前 realm 的一段普通 script,不是 eval。 - /// - /// # 步骤 - /// 1. parse(脚本模式)→ compile(emit_program 置 is_global_scope=true, - /// 不设 is_eval_script,与普通脚本同一编译臂)。 - /// 2. 整棵模块树追加进平表:同 [`create_dynamic_script`] - /// (`rehome_subtree(&module, base+1)`,根落 base、子函数 old→base+old)。 - /// 3. 扩容当前代际的常量缓存,建函数对象(sub_module_index = base)返回。 - /// - /// # 边界与前提 - /// - 编译或解析失败返回 `Err`(由宿主层转 SyntaxError)。 - /// - 动态模块在函数对象存活期间跨 run 有效(同 create_dynamic_function)。 - /// - 返回函数对象仅供内部同步调用,不设 name/length(用户不可见)。 - /// - /// # 副作用 - /// - 扩展当前代际平表(`tables[current_gen]`)与常量缓存。 - pub fn create_plain_dynamic_script(&mut self, code: &str) -> Result { - let allocator = oxide_parser::Allocator::default(); - let program = oxide_parser::parse(&allocator, code) - .map_err(|errs| errs.into_iter().map(|e| e.message).collect::>().join("\n"))?; - // 普通脚本:顶层 var/function 声明落全局属性 configurable:false, - // let/const 落全局词法环境。动态路径源契约同 `create_dynamic_function` - // (源码域转义形态传源)。 - let module = oxide_compiler::compiler::Compiler::new() - .with_source_encoded(true) - .compile(&program)?; - // 根模块 flat_id=0 传 base+1,重编号后落 base(避开 sub_module_index()==0 - // 守卫)。make_mut 彼时平表 Arc 强引用唯一持有者是本表,原地扩展不分叉。 - let table = self.current_table_mut(); - let base = table.modules.len() as u32; - let mut added = Vec::new(); - rehome_subtree(&module, base + 1, &mut added); - Arc::make_mut(&mut table.modules).extend(added); - // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 - table.immutables.resize(table.modules.len(), OnceLock::new()); - Ok(self.create_function_object(base, self.current_gen, false, false, false, false)) - } -} - -/// 把 flatten 后的子模块子树重编号到平表偏移 `base`:DFS 前序拷贝进 `out`, -/// 新 flat_id = base + (old - 1),子树内每条 `CREATE_CLOSURE` 的 imm16 同步重写。 -/// 原子树 flat_id 自 1 连续,因此拷贝顺序即新 id 顺序,`out` 下标对齐平表槽位。 -fn rehome_subtree(module: &CompiledModule, base: u32, out: &mut Vec>) { - let new_id = base + module.flat_id - 1; - let mut bytecode = module.bytecode.to_vec(); - for instr in &mut bytecode { - if opcode::opcode(*instr) == OpCode::CREATE_CLOSURE { - let old = opcode::imm16(*instr) as u32; - let new_flat = base + (old - 1); - *instr = opcode::encode( - OpCode::CREATE_CLOSURE, - opcode::rd(*instr), - (new_flat & 0xFF) as u8, - ((new_flat >> 8) & 0xFF) as u8, - ); - } - } - let mut rehomed = module.clone(); - rehomed.bytecode = Arc::from(bytecode); - rehomed.flat_id = new_id; - out.push(Arc::new(rehomed)); - for sub in &module.sub_modules { - rehome_subtree(sub, base, out); - } -} - #[cfg(test)] impl Vm { /// 测试用:当前代际的子模块平表只读视图(消费点全在测试钉)。 diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs new file mode 100644 index 00000000..d612a588 --- /dev/null +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -0,0 +1,185 @@ +//! `oxide_runtime_api::VmHost` 委托实现:宿主接口各项委托到 `Vm` 同名固有方法。 + +use std::sync::Arc; + +use oxide_kernel::kernel::{KernelCore, KernelSession}; +use oxide_types::mem::Epoch; +use oxide_types::object::{JsObject, PropAttributes}; +use oxide_types::value::JsValue; + +use super::Vm; + +impl oxide_runtime_api::VmHost for Vm { + fn reg(&self, idx: u8) -> JsValue { + self.reg(idx) + } + fn set_reg(&mut self, idx: u8, val: JsValue) { + self.set_reg(idx, val); + } + fn native_overflow_count(&self) -> usize { + self.native_overflow_count + } + fn native_overflow_at(&self, i: usize) -> JsValue { + self.spill_stack[self.native_overflow_base + i] + } + fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject { + self.alloc_object(obj) + } + fn new_string(&mut self, s: &str) -> JsValue { + self.new_string(s) + } + fn new_string_owned(&mut self, s: String) -> JsValue { + Vm::new_string_owned(self, s) + } + fn new_bigint(&mut self, v: num_bigint::BigInt) -> JsValue { + Vm::new_bigint(self, v) + } + fn bigint_value(&mut self, val: JsValue) -> &num_bigint::BigInt { + Vm::bigint_value(self, val) + } + fn kernel_core(&self) -> &Arc { + self.kernel_core() + } + fn session(&self) -> &KernelSession { + self.session() + } + fn epoch(&self) -> &Epoch { + self.epoch() + } + fn take_uncaught_value(&mut self) -> Option { + self.last_uncaught_value.take() + } + fn restore_uncaught_value(&mut self, value: Option) { + self.last_uncaught_value = value; + } + fn property_key_si(&mut self, val: JsValue) -> u32 { + // Object-key conversion (to_string_full) failures degrade to the empty key here: + // this trait path is used by Reflect/Object builtins; computed property access + // uses the inherent Result-returning version to preserve the full exception. + self.property_key_si(val) + .unwrap_or_else(|_| self.kernel_core.perm_interner().intern("").0) + } + fn to_property_key_si(&mut self, val: JsValue) -> Result { + // ToPropertyKey 完整路径:转换异常(对象 ToPrimitive 抛错 / Symbol 处理) + // 原样返回,供需传播异常的 builtins(groupBy / __defineGetter__ 等)使用。 + self.property_key_si(val) + } + fn string_key_si(&mut self, s: &str) -> u32 { + // 与运行时字符串键同口径:含 FFFD 的键文本按 encode_key 形态入键空间 + // (FFFD 键的 JSON.parse 键与字面量/拼接构造的运行时键身份一致)。 + self.string_key_text(s) + } + fn string_units(&self, val: JsValue) -> std::borrow::Cow<'_, [u16]> { + // SAFETY: 调用方保证 val 为字符串值。perm 串由内核持有永不释放;session + // 串仅经 &mut self 路径(new_string/GC)释放,&self 借用期间编译器强制 + // 不存在 &mut 存续,字符串不会在借用期内回收。 + unsafe { (*val.as_string_ptr()).units() } + } + fn new_string_units(&mut self, units: &[u16]) -> JsValue { + Vm::new_string_units(self, units) + } + fn new_string_units_owned(&mut self, units: Vec) -> JsValue { + Vm::new_string_units_owned(self, units) + } + fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option { + self.resolve_property(obj, prop_name_si) + } + fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option { + self.get_own_property_slot(obj, prop_name_si) + } + fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result { + self.ordinary_get(obj, prop_name_si, receiver) + } + fn ordinary_set( + &mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, strict: bool, + ) -> Result<(), String> { + self.ordinary_set(obj, prop_name_si, val, receiver, strict) + } + fn define_data_property( + &mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes, + ) -> Result<(), String> { + self.define_data_property(obj, prop_name_si, val, attributes) + } + fn define_accessor_property( + &mut self, obj: &mut JsObject, prop_name_si: u32, get: JsValue, set: JsValue, attributes: PropAttributes, + ) -> Result<(), String> { + self.define_accessor_property(obj, prop_name_si, get, set, attributes) + } + fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) { + self.set_or_create_prop_value(obj, prop_name_si, val) + } + fn sync_global_builtin_mirror(&mut self, obj: &JsObject, key_si: u32, val: JsValue) { + self.sync_global_builtin_mirror(obj, key_si, val) + } + fn lookup_str(&self, val: JsValue) -> Option { + self.lookup_str(val) + } + fn coerce_primitive_bounded(&mut self, value: JsValue, prefer_string: bool) -> Result { + self.coerce_primitive_bounded(value, prefer_string) + } + fn coerce_number_bounded(&mut self, value: JsValue) -> Result { + self.coerce_number_bounded(value) + } + fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result { + self.call_function_sync(callee, receiver, args) + } + fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result, String> { + self.checked_object_ptr(val, error_msg) + } + fn raise_type_error(&mut self, msg: &str) -> Result<(), String> { + self.raise_type_error(msg) + } + fn error_message_text(&self, kind: &str, msg: &str) -> String { + self.error_message_text(kind, msg) + } + fn call_stack_function_names(&self) -> Vec { + self.frames + .iter() + .rev() + .map(|f| { + self.kernel_core + .perm_interner() + .lookup(f.function_name) + .unwrap_or("") + .to_string() + }) + .collect() + } + fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue { + self.promote_if_needed_for_write_ptr(target_ptr, value) + } + fn step_rng(&mut self) { + self.step_rng() + } + fn math_rng_value(&self) -> f64 { + self.math_rng_value() + } + fn sub_module_function_name(&self, gen: u32, sub_idx: u16) -> String { + self.tables + .get(&gen) + .and_then(|t| t.modules.get(sub_idx as usize)) + .and_then(|m| m.function_name.clone()) + .unwrap_or_default() + } + fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result { + self.create_dynamic_function(params, body) + } + fn create_dynamic_script(&mut self, code: &str) -> Result { + self.create_dynamic_script(code) + } + fn symbol_intern(&mut self, desc: Option) -> u32 { + self.symbols.intern(desc) + } + fn symbol_description(&self, idx: u32) -> Option<&str> { + self.symbols.description(idx) + } + fn symbol_lookup_global(&self, key: &str) -> Option { + self.symbols.lookup_global(key) + } + fn symbol_register_global(&mut self, key: String, idx: u32) { + self.symbols.register_global(key, idx) + } + fn symbol_key_for_id(&self, idx: u32) -> Option { + self.symbols.key_for_id(idx) + } +}