diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 43d7279e..f49633e9 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -1,5 +1,5 @@ use crate::native::NativeFn; -use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm}; +use crate::vm::{native_fn_ptr_to_fn, FrameArgs, FrameContinuation, Vm}; use crate::{vm_debug, vm_trace}; use oxide_builtins::iterator::make_iterator_for_value; use oxide_builtins::{builtins_debug, builtins_trace}; @@ -7,7 +7,6 @@ use oxide_bytecode::opcode; use oxide_runtime_api::{to_boolean, NativeResult}; use oxide_types::object::{Cell, JsObject, PropAttributes}; use oxide_types::value::JsValue; -use std::sync::Arc; impl Vm { #[inline(always)] @@ -90,7 +89,7 @@ impl Vm { self.push_bytecode_frame( callee, this, - &args, + FrameArgs::Slice(&args), None, None, JsValue::undefined(), @@ -486,77 +485,21 @@ impl Vm { self.sub_modules.len() )); } - if self.frames.len() >= self.kernel_core.config.max_call_depth { - return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded")); - } - - let sub_bytecode = Arc::clone(&self.sub_modules[sub_idx].bytecode); - let sub_n_args = self.sub_modules[sub_idx].n_args as usize; - let sub_n_registers = self.sub_modules[sub_idx].n_registers; - let sub_param_base = self.sub_modules[sub_idx].param_base as usize; - let caller_active_reg_limit = self.active_reg_limit.max(1); - let caller_reg_limit = self.call_window_limit(caller_active_reg_limit, call_window); - let saved_reg_offset = self.save_stack.len() as u32; - self.save_stack.extend_from_slice(&self.regs[..caller_reg_limit as usize]); - let saved_this = self.regs[254]; - let saved_new_target = self.regs[255]; - - // 完整实参写入 spill 栈实参区(在帧的 spill 区之前),供 CREATE_ARGUMENTS 使用。 - let args_base = self.spill_stack.len() as u32; - for i in 0..arg_count { - let src_reg = first_arg_reg.wrapping_add(i as u8) as usize; - self.spill_stack.push(self.regs[src_reg]); - } - let args_count = arg_count.min(u16::MAX as usize) as u16; - - for i in 0..sub_n_args { - let src_reg = first_arg_reg.wrapping_add(i as u8) as usize; - self.regs[sub_param_base + i] = self.regs[src_reg]; - } - self.regs[254] = derived_this; - self.regs[255] = new_target; - - self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode)); - self.saved_immutables_stack.push(self.active_immutables); - - let function_name = self.sub_modules[sub_idx] - .function_name - .as_deref() - .map(|name| self.kernel_core.perm_interner().intern(name).0) - .unwrap_or(0); - - self.frames.push(CallFrame { - return_addr: self.pc, - function_name, - caller_reg_limit, - caller_active_reg_limit, - saved_reg_offset, - spill_offset: self.spill_stack.len() as u32, - arguments_base: args_base, - arguments_count: args_count, - saved_this, - saved_new_target, - callee: super_ctor, - construct_result_reg: Some(254), - constructed_this: Some(derived_this), - is_derived_constructor: super_obj.is_derived_constructor(), - continuation: FrameContinuation::None, - }); - - self.bytecode = sub_bytecode; - let subs = Arc::clone(&self.sub_modules); - self.activate_immutables(sub_idx, &subs[sub_idx].constants); - self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize)); - for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map.clone() { - 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); - } - } - - self.active_reg_limit = sub_n_registers.max(1); - self.pc = 0; + // 收敛到统一压帧入口:this = derived_this(super() 把实例交予父构造器), + // new.target 保持外层类;构造结果写回 regs[254]。 + self.push_bytecode_frame( + super_ctor, + derived_this, + FrameArgs::RegRange { + first: first_arg_reg, + count: arg_count, + }, + Some(254), + Some(derived_this), + new_target, + FrameContinuation::None, + call_window, + )?; return Ok(true); } else { self.raise_error_kind("TypeError", "super constructor is not callable")?; @@ -794,7 +737,7 @@ impl Vm { self.push_bytecode_frame( callee, this_value, - &args, + FrameArgs::Slice(&args), None, None, JsValue::undefined(), @@ -901,7 +844,7 @@ impl Vm { self.push_bytecode_frame( constructor, this_value, - &args, + FrameArgs::Slice(&args), Some(rd as u8), Some(new_obj_val), constructor, @@ -989,7 +932,7 @@ impl Vm { self.push_bytecode_frame( super_ctor, derived_this, - &args, + FrameArgs::Slice(&args), Some(254), Some(derived_this), new_target, diff --git a/crates/oxide_vm/src/dispatch/misc.rs b/crates/oxide_vm/src/dispatch/misc.rs index 6cda263b..c1adaf84 100644 --- a/crates/oxide_vm/src/dispatch/misc.rs +++ b/crates/oxide_vm/src/dispatch/misc.rs @@ -194,8 +194,8 @@ impl Vm { /// 附 length / callee 属性。第一版为 unmapped(非严格)语义,索引与形参不同步。 /// /// # 边界与前提 - /// - 实参区由 `push_bytecode_frame` / `dispatch_super_call` / `dispatch_new_expression` - /// 在推帧时写入 spill 栈;frames 为空(inline 同步调用)时读 `inline_args_*`。 + /// - 实参区由统一压帧入口 `push_bytecode_frame` 在推帧时写入 spill 栈; + /// frames 为空(inline 同步调用)时读 `inline_args_*`。 /// - 索引属性用 shape 槽存储(普通对象),length/callee 为不可枚举数据属性。 pub(crate) fn dispatch_create_arguments(&mut self, rd: usize) -> Result<(), String> { let (base, count) = match self.frames.last() { @@ -244,7 +244,7 @@ impl Vm { /// 2. 实参个数超出固定形参数的部分作为数组元素写入。 /// /// # 边界与前提 - /// - 实参来源与 CREATE_ARGUMENTS 相同(push_bytecode_frame / inline 路径写入 spill 栈)。 + /// - 实参来源与 CREATE_ARGUMENTS 相同(统一压帧入口 `push_bytecode_frame` 写入 spill 栈)。 /// - 实参不足 `fixed_count` 时数组为空。 pub(crate) fn dispatch_create_rest_array(&mut self, rd: usize, fixed_count: usize) -> Result<(), String> { let (base, count) = match self.frames.last() { diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 2515dee7..c6af511b 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -163,6 +163,42 @@ pub enum FrameContinuation { AccessorSet, } +/// 压帧实参来源:已物化切片或寄存器连续区间。 +/// +/// `RegRange` 直接引用调用方寄存器文件(CALL/NEW/SUPER_CALL 的实参在寄存器中 +/// 天然连续排列),免去临时堆 `Vec` 物化,每次调用省 1 次分配/释放。 +#[derive(Clone, Copy)] +pub(crate) enum FrameArgs<'a> { + /// 已物化实参:spread 展开、TailCall、accessor 与初始执行路径。 + Slice(&'a [JsValue]), + /// 寄存器连续区间 `regs[first .. first+count)`。 + RegRange { first: u8, count: usize }, +} + +impl FrameArgs<'_> { + /// 实参总数。 + pub(crate) fn len(&self) -> usize { + match self { + FrameArgs::Slice(s) => s.len(), + FrameArgs::RegRange { count, .. } => *count, + } + } + + /// 取第 `i` 个实参;超出实参范围返回 undefined(与切片 `get(i)` 语义一致)。 + pub(crate) fn get(&self, vm: &Vm, i: usize) -> JsValue { + match self { + FrameArgs::Slice(s) => s.get(i).copied().unwrap_or(JsValue::undefined()), + FrameArgs::RegRange { first, count } => { + if i < *count { + vm.regs[first.wrapping_add(i as u8) as usize] + } else { + JsValue::undefined() + } + } + } + } +} + /// 一次函数调用的调用帧:记录返回地址、调用方寄存器窗口与 `this`/`new.target`。 /// /// 调用方寄存器窗口在 `save_stack` 中按 `saved_reg_offset` 保存,返回时由 @@ -1161,7 +1197,7 @@ impl Vm { #[expect(clippy::too_many_arguments)] pub(crate) fn push_bytecode_frame( - &mut self, callee: JsValue, this_value: JsValue, args: &[JsValue], construct_result_reg: Option, + &mut self, callee: JsValue, this_value: JsValue, args: FrameArgs, construct_result_reg: Option, constructed_this: Option, new_target: JsValue, continuation: FrameContinuation, call_window: u8, ) -> Result<(), String> { vm_trace!( @@ -1205,7 +1241,7 @@ impl Vm { let saved_new_target = self.regs[255]; for i in 0..sub_n_args { - self.regs[sub_param_base + i] = args.get(i).copied().unwrap_or(JsValue::undefined()); + self.regs[sub_param_base + i] = args.get(self, i); } self.regs[254] = if sub_is_arrow { obj.captured_this() } else { this_value }; self.regs[255] = new_target; @@ -1222,7 +1258,14 @@ impl Vm { // 完整实参写入 spill 栈实参区(在帧的 spill 区之前):CREATE_ARGUMENTS 据此 // 构建 arguments 对象,帧恢复时随 spill 区截断一起丢弃。 let args_base = self.spill_stack.len() as u32; - self.spill_stack.extend_from_slice(args); + match args { + FrameArgs::Slice(s) => self.spill_stack.extend_from_slice(s), + FrameArgs::RegRange { first, count } => { + for i in 0..count { + self.spill_stack.push(self.regs[first.wrapping_add(i as u8) as usize]); + } + } + } let args_count = args.len().min(u16::MAX as usize) as u16; self.frames.push(CallFrame { diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index 3a2861f1..1a02c7ec 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -1,4 +1,4 @@ -use crate::vm::{Completion, FrameContinuation, TryHandler, Vm}; +use crate::vm::{Completion, FrameArgs, FrameContinuation, TryHandler, Vm}; use crate::vm_trace; use oxide_bytecode::opcode; use oxide_types::object::PropAttributes; @@ -34,35 +34,39 @@ impl Vm { return Ok(true); } else if obj.sub_module_index() > 0 { let sub_idx = obj.sub_module_index() as usize; - let args: Vec = (0..arg_count) - .map(|i| self.regs[first_arg_reg.wrapping_add(i as u8) as usize]) - .collect(); - // 异步生成器函数调用返回异步生成器迭代器对象。 - if sub_idx < self.sub_modules.len() - && self.sub_modules[sub_idx].is_generator - && self.sub_modules[sub_idx].is_async - { - let gen = - self.create_async_generator_object(callee, self.regs[this_reg as usize], &args)?; - self.regs[0] = gen; - return Ok(false); - } - // 生成器函数调用返回迭代器对象,不执行函数体。 - if sub_idx < self.sub_modules.len() && self.sub_modules[sub_idx].is_generator { - let gen = self.create_generator_object(callee, self.regs[this_reg as usize], &args)?; - self.regs[0] = gen; - return Ok(false); - } - // 异步函数调用返回 capability promise,立即同步执行 body 到首个 await。 - if sub_idx < self.sub_modules.len() && self.sub_modules[sub_idx].is_async { - let promise = self.create_async_object(callee, self.regs[this_reg as usize], &args)?; + let this_value = self.regs[this_reg as usize]; + let is_generator = sub_idx < self.sub_modules.len() && self.sub_modules[sub_idx].is_generator; + let is_async = sub_idx < self.sub_modules.len() && self.sub_modules[sub_idx].is_async; + // 生成器/异步路径需把实参物化存进状态盒;普通字节码调用直接 + // 引用寄存器连续区间,免临时堆 Vec(每次调用省 1 次分配)。 + if is_generator || is_async { + let args: Vec = (0..arg_count) + .map(|i| self.regs[first_arg_reg.wrapping_add(i as u8) as usize]) + .collect(); + // 异步生成器函数调用返回异步生成器迭代器对象。 + if is_generator && is_async { + let gen = self.create_async_generator_object(callee, this_value, &args)?; + self.regs[0] = gen; + return Ok(false); + } + // 生成器函数调用返回迭代器对象,不执行函数体。 + if is_generator { + let gen = self.create_generator_object(callee, this_value, &args)?; + self.regs[0] = gen; + return Ok(false); + } + // 异步函数调用返回 capability promise,立即同步执行 body 到首个 await。 + let promise = self.create_async_object(callee, this_value, &args)?; self.regs[0] = promise; return Ok(false); } self.push_bytecode_frame( callee, - self.regs[this_reg as usize], - &args, + this_value, + FrameArgs::RegRange { + first: first_arg_reg, + count: arg_count, + }, None, None, JsValue::undefined(), diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 6c6bed94..cdbbc08e 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -1,7 +1,5 @@ -use std::sync::Arc; - use crate::native::NativeFn; -use crate::vm::{native_fn_ptr_to_fn, CallFrame, ForInIter, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; +use crate::vm::{native_fn_ptr_to_fn, ForInIter, FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; use crate::vm_trace; use oxide_runtime_api::{to_boolean, to_string_full, NativeResult, VmHost}; use oxide_types::object::{JsObject, PropAttributes}; @@ -106,81 +104,27 @@ impl Vm { return self.raise_type_error("g is not a constructor").map(|_| true); } - if self.frames.len() >= self.kernel_core.config.max_call_depth { - return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded")); - } - let new_obj_val = JsValue::object(new_obj as *mut u8); - let sub_bytecode = Arc::clone(&self.sub_modules[sub_idx].bytecode); - let sub_n_args = self.sub_modules[sub_idx].n_args as usize; - let sub_n_registers = self.sub_modules[sub_idx].n_registers; - let sub_param_base = self.sub_modules[sub_idx].param_base as usize; - let caller_active_reg_limit = self.active_reg_limit.max(1); - let caller_reg_limit = self.call_window_limit(caller_active_reg_limit, call_window); - let saved_reg_offset = self.save_stack.len() as u32; - self.save_stack.extend_from_slice(&self.regs[..caller_reg_limit as usize]); - let saved_this = self.regs[254]; - let saved_new_target = self.regs[255]; - - // 完整实参写入 spill 栈实参区(在帧的 spill 区之前),供 CREATE_ARGUMENTS 使用。 - let args_base = self.spill_stack.len() as u32; - for i in 0..arg_count { - let src_reg = first_arg_reg.wrapping_add(i as u8) as usize; - self.spill_stack.push(self.regs[src_reg]); - } - let args_count = arg_count.min(u16::MAX as usize) as u16; - - for i in 0..sub_n_args { - let src_reg = first_arg_reg.wrapping_add(i as u8) as usize; - self.regs[sub_param_base + i] = self.regs[src_reg]; - } - self.regs[254] = if ctor_obj.is_derived_constructor() { + // 收敛到统一压帧入口:derived 构造器在 super() 前 this 为 undefined, + // 基类构造器 this = 新对象;new.target = 构造器本身。 + let this_value = if ctor_obj.is_derived_constructor() { JsValue::undefined() } else { new_obj_val }; - self.regs[255] = constructor; - - self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode)); - self.saved_immutables_stack.push(self.active_immutables); - - self.frames.push(CallFrame { - return_addr: self.pc, - function_name: self.sub_modules[sub_idx] - .function_name - .as_deref() - .map(|name| self.kernel_core.perm_interner().intern(name).0) - .unwrap_or(0), - caller_reg_limit, - caller_active_reg_limit, - saved_reg_offset, - spill_offset: self.spill_stack.len() as u32, - arguments_base: args_base, - arguments_count: args_count, - saved_this, - saved_new_target, - callee: constructor, - construct_result_reg: Some(rd as u8), - constructed_this: Some(new_obj_val), - is_derived_constructor: ctor_obj.is_derived_constructor(), - continuation: FrameContinuation::None, - }); - - self.bytecode = sub_bytecode; - let subs = Arc::clone(&self.sub_modules); - self.activate_immutables(sub_idx, &subs[sub_idx].constants); - self.cell_stack.push(Vec::with_capacity(subs[sub_idx].cells_needed as usize)); - - for (name, reg) in &self.sub_modules[sub_idx].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); - } - } - - self.active_reg_limit = sub_n_registers.max(1); - self.pc = 0; + self.push_bytecode_frame( + constructor, + this_value, + FrameArgs::RegRange { + first: first_arg_reg, + count: arg_count, + }, + Some(rd as u8), + Some(new_obj_val), + constructor, + FrameContinuation::None, + call_window, + )?; Ok(true) } else { let error = diff --git a/crates/oxide_vm/src/vm_props.rs b/crates/oxide_vm/src/vm_props.rs index 2b0e06ad..a76b0c35 100644 --- a/crates/oxide_vm/src/vm_props.rs +++ b/crates/oxide_vm/src/vm_props.rs @@ -1,4 +1,4 @@ -use crate::vm::{FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; +use crate::vm::{FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; use crate::{ic_trace, vm_trace}; use oxide_kernel::prop_forge::PropTemplate; use oxide_runtime_api as coercion; @@ -126,7 +126,7 @@ impl Vm { self.push_bytecode_frame( getter, receiver, - &[], + FrameArgs::Slice(&[]), None, None, JsValue::undefined(), @@ -326,7 +326,7 @@ impl Vm { self.push_bytecode_frame( setter, receiver, - &[val], + FrameArgs::Slice(&[val]), None, None, JsValue::undefined(), diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 24f40692..dbd2efea 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -2,7 +2,7 @@ use std::sync::{Arc, OnceLock}; use oxide_bytecode::module::CompiledModule; -use crate::vm::{CallFrame, FrameContinuation, InlineSyncState, Vm}; +use crate::vm::{CallFrame, FrameArgs, FrameContinuation, InlineSyncState, Vm}; use crate::{vm_debug, vm_info, vm_trace, vm_warn}; use oxide_types::object::JsObject; use oxide_types::value::JsValue; @@ -205,7 +205,7 @@ impl Vm { self.push_bytecode_frame( callee, this_value, - args, + FrameArgs::Slice(args), None, None, JsValue::undefined(),