From 2777b00c82c142942e999e8d75054480888308d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Sat, 8 Aug 2026 10:33:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E7=94=A8=E5=AE=9E=E5=8F=82=20?= =?UTF-8?q?spread=EF=BC=88f(...args)=20/=20new=20F(...args)=20/=20super(..?= =?UTF-8?q?.args)=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_bytecode/src/opcode.rs | 3 + .../tests/compile_expr_tests.rs | 20 + crates/oxide_emit/src/closure.rs | 49 ++- crates/oxide_emit/src/expr/call.rs | 101 +++-- crates/oxide_emit/src/expr/function/mod.rs | 28 +- crates/oxide_emit/src/prepass.rs | 21 +- crates/oxide_emit/src/shared/chain.rs | 57 +-- crates/oxide_ir/src/contract.rs | 42 ++- crates/oxide_ir/src/inst.rs | 54 +++ crates/oxide_regalloc/src/graph.rs | 10 + crates/oxide_regalloc/src/rewrite.rs | 60 ++- crates/oxide_vm/src/dispatch/call.rs | 357 +++++++++++++++++- crates/oxide_vm/src/vm.rs | 18 + crates/oxide_vm/tests/call_spread_tests.rs | 131 +++++++ 14 files changed, 811 insertions(+), 140 deletions(-) create mode 100644 crates/oxide_vm/tests/call_spread_tests.rs diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index 008d308..f6143ff 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -140,6 +140,9 @@ define_opcodes! { CREATE_CLOSURE = 0x4A => "CREATE_CLOSURE", CREATE_REGEXP = 0x4B => "CREATE_REGEXP", STORE_UPVALUE = 0x4C => "STORE_UPVALUE", + CALL_SPREAD = 0x4D => "CALL_SPREAD", + NEW_EXPRESSION_SPREAD = 0x4E => "NEW_EXPRESSION_SPREAD", + SUPER_CALL_SPREAD = 0x4F => "SUPER_CALL_SPREAD", // ── Object Property (0x50-0x5F) ── IC_GET_PROP = 0x50 => "IC_GET_PROP", diff --git a/crates/oxide_compiler/tests/compile_expr_tests.rs b/crates/oxide_compiler/tests/compile_expr_tests.rs index aecbb7a..499d562 100644 --- a/crates/oxide_compiler/tests/compile_expr_tests.rs +++ b/crates/oxide_compiler/tests/compile_expr_tests.rs @@ -528,6 +528,26 @@ fn compile_template_literal_emits_template_str() { ); } +#[test] +fn compile_call_spread_emits_spread_opcodes() { + // 普通调用 / new 表达式含 spread 实参 → 发 spread 变体 opcode + let module = compile_source("function f(){}; f(...[1,2]); new f(...[3]);"); + assert!( + module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::CALL_SPREAD), + "f(...args) should emit CALL_SPREAD" + ); + assert!( + module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::NEW_EXPRESSION_SPREAD), + "new f(...args) should emit NEW_EXPRESSION_SPREAD" + ); + // 无 spread 的调用不发 spread 变体 + let plain = compile_source("function f(a){}; f(1);"); + assert!( + !plain.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::CALL_SPREAD), + "plain call should not emit CALL_SPREAD" + ); +} + #[test] fn compile_dynamic_member_ops() { let module = compile_source("let obj = {}, k = 'x', arr = [1]; obj[k]; obj[k] = 1; arr[0];"); diff --git a/crates/oxide_emit/src/closure.rs b/crates/oxide_emit/src/closure.rs index 24539e6..9c5055a 100644 --- a/crates/oxide_emit/src/closure.rs +++ b/crates/oxide_emit/src/closure.rs @@ -305,17 +305,13 @@ impl Emitter { Expression::CallExpression(ce) => { self.collect_capture_names_expr(&ce.callee, ref_set, shadow, out); for a in &ce.arguments { - if let Some(e) = a.as_expression() { - self.collect_capture_names_expr(e, ref_set, shadow, out); - } + self.collect_capture_names_arg(a, ref_set, shadow, out); } } Expression::NewExpression(ne) => { self.collect_capture_names_expr(&ne.callee, ref_set, shadow, out); for a in &ne.arguments { - if let Some(e) = a.as_expression() { - self.collect_capture_names_expr(e, ref_set, shadow, out); - } + self.collect_capture_names_arg(a, ref_set, shadow, out); } } Expression::SequenceExpression(se) => { @@ -389,15 +385,25 @@ impl Emitter { oxide_parser::ChainElement::CallExpression(call) => { self.collect_capture_names_expr(&call.callee, ref_set, shadow, out); for a in &call.arguments { - if let Some(e) = a.as_expression() { - self.collect_capture_names_expr(e, ref_set, shadow, out); - } + self.collect_capture_names_arg(a, ref_set, shadow, out); } } _ => {} } } + /// 遍历调用实参:静态实参与 spread 内部表达式都纳入捕获扫描。 + fn collect_capture_names_arg( + &self, arg: &oxide_parser::Argument, ref_set: &HashSet, shadow: &HashSet, + out: &mut HashSet, + ) { + if let Some(e) = arg.as_expression() { + self.collect_capture_names_expr(e, ref_set, shadow, out); + } else if let oxide_parser::Argument::SpreadElement(sp) = arg { + self.collect_capture_names_expr(&sp.argument, ref_set, shadow, out); + } + } + /// 分析本函数:哪些绑定被任意深度嵌套函数捕获 → captured_bindings。 pub(crate) fn collect_captured_bindings( &self, stmts: &[Statement], extra_exprs: &[&oxide_parser::Expression], own: &HashSet, @@ -572,9 +578,7 @@ impl Emitter { Expression::CallExpression(ce) => { self.collect_captured_expr(&ce.callee, own, out); for a in &ce.arguments { - if let Some(e) = a.as_expression() { - self.collect_captured_expr(e, own, out); - } + self.collect_captured_arg(a, own, out); } } Expression::BinaryExpression(be) => { @@ -614,9 +618,7 @@ impl Emitter { Expression::NewExpression(ne) => { self.collect_captured_expr(&ne.callee, own, out); for a in &ne.arguments { - if let Some(e) = a.as_expression() { - self.collect_captured_expr(e, own, out); - } + self.collect_captured_arg(a, own, out); } } Expression::ComputedMemberExpression(m) => { @@ -659,22 +661,29 @@ impl Emitter { oxide_parser::ChainElement::CallExpression(c) => { self.collect_captured_expr(&c.callee, own, out); for a in &c.arguments { - if let Some(e) = a.as_expression() { - self.collect_captured_expr(e, own, out); - } + self.collect_captured_arg(a, own, out); } } _ => {} } } + /// 遍历调用实参:静态实参与 spread 内部表达式都纳入捕获判定。 + fn collect_captured_arg(&self, arg: &oxide_parser::Argument, own: &HashSet, out: &mut HashSet) { + if let Some(e) = arg.as_expression() { + self.collect_captured_expr(e, own, out); + } else if let oxide_parser::Argument::SpreadElement(sp) = arg { + self.collect_captured_expr(&sp.argument, own, out); + } + } + /// 分析子函数 body:引用父级被捕获绑定的名字 → upvalue_captures。 /// 名字若在父 `captured_bindings`(父 own cell)则 `cell_idx` 索引定义方表; /// 若在父 `upvalue_captures`(父自身从更外层捕获)则链式标记 `parent_uv_idx`, /// 运行时从父闭包 upvalues 取 cell。enclosing_reg 由父 emit 完成后填充。 pub(crate) fn collect_upvalue_names( - &self, body_stmts: &[Statement], extra_exprs: &[&oxide_parser::Expression], parent_captured: &BTreeMap, - parent_upvalues: &[UpvalueCapture], sub_own: &HashSet, + &self, body_stmts: &[Statement], extra_exprs: &[&oxide_parser::Expression], + parent_captured: &BTreeMap, parent_upvalues: &[UpvalueCapture], sub_own: &HashSet, ) -> Vec { let mut parent_names: HashSet = parent_captured.keys().cloned().collect(); for u in parent_upvalues { diff --git a/crates/oxide_emit/src/expr/call.rs b/crates/oxide_emit/src/expr/call.rs index 4d3b752..9730be8 100644 --- a/crates/oxide_emit/src/expr/call.rs +++ b/crates/oxide_emit/src/expr/call.rs @@ -14,19 +14,19 @@ impl Emitter { if !ctx.in_derived_constructor { return Err("super() only supported in derived constructors".into()); } - let mut arg_regs = Vec::new(); - for arg in &call.arguments { - if let Some(expr) = arg.as_expression() { - arg_regs.push(self.emit_expression(expr, ctx)?); - } - } - let first_arg_reg = if arg_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut arg_regs, ctx) }; + let words = self.emit_call_args(&call.arguments, ctx)?; let result_reg = ctx.alloc_reg(); - ctx.inst(Inst::super_call( - Operand::Reg(result_reg), - Operand::Reg(first_arg_reg), - arg_regs.len() as u8, - )); + if words.iter().any(|w| w >> 31 == 1) { + ctx.inst(Inst::super_call_spread(Operand::Reg(result_reg), &words)); + } else { + let mut static_regs = words; + let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) }; + ctx.inst(Inst::super_call( + Operand::Reg(result_reg), + Operand::Reg(first_arg_reg), + static_regs.len() as u8, + )); + } if let Some(mut field_buffer) = ctx.field_buffer.take() { let insert_inst = ctx.insts.len(); ctx.insts.append(&mut field_buffer.insts); @@ -92,36 +92,37 @@ impl Emitter { (callee_reg, this_reg) } }; - let mut arg_regs = Vec::new(); - for arg in &call.arguments { - if let Some(expr) = arg.as_expression() { - arg_regs.push(self.emit_expression(expr, ctx)?); + let words = self.emit_call_args(&call.arguments, ctx)?; + if words.iter().any(|w| w >> 31 == 1) { + // 任一实参是 spread → 运行期物化完整实参(有序字逐个读寄存器 / 迭代展开)。 + ctx.inst(Inst::call_spread(Operand::Reg(callee_reg), Operand::Reg(this_reg), &words)); + } else { + let mut static_regs = words; + let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) }; + let op = match &call.callee { + Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE, + _ => OpCode::CALL, + }; + match op { + OpCode::CALL => { + ctx.inst(Inst::call( + Operand::Reg(callee_reg), + Operand::Reg(this_reg), + Operand::Reg(first_arg_reg), + static_regs.len() as u8, + )); + } + OpCode::CALL_NATIVE => { + ctx.inst(Inst::call_native( + Operand::Reg(callee_reg), + Operand::Reg(this_reg), + Operand::Reg(first_arg_reg), + static_regs.len() as u8, + )); + } + _ => unreachable!(), } } - let first_arg_reg = if arg_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut arg_regs, ctx) }; - let op = match &call.callee { - Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE, - _ => OpCode::CALL, - }; - match op { - OpCode::CALL => { - ctx.inst(Inst::call( - Operand::Reg(callee_reg), - Operand::Reg(this_reg), - Operand::Reg(first_arg_reg), - arg_regs.len() as u8, - )); - } - OpCode::CALL_NATIVE => { - ctx.inst(Inst::call_native( - Operand::Reg(callee_reg), - Operand::Reg(this_reg), - Operand::Reg(first_arg_reg), - arg_regs.len() as u8, - )); - } - _ => unreachable!(), - } let result_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::None, Operand::None)); Ok(result_reg) @@ -139,7 +140,7 @@ impl Emitter { /// 各参数由独立 vreg 承载,复杂表达式(对象/数组字面量、嵌套调用)的临时寄存器 /// 会使参数 vreg 不连续——检测到不连续时用 MOV 打包到新连续块。 /// 返回首参寄存器;参数为空时返回 0。 -fn pack_arg_regs(arg_regs: &mut [u32], ctx: &mut CompileCtx) -> u32 { +pub(crate) fn pack_arg_regs(arg_regs: &mut [u32], ctx: &mut CompileCtx) -> u32 { let consecutive = arg_regs.windows(2).all(|w| w[1] == w[0] + 1); if consecutive { return arg_regs[0]; @@ -154,3 +155,21 @@ fn pack_arg_regs(arg_regs: &mut [u32], ctx: &mut CompileCtx) -> u32 { } base } + +impl Emitter { + /// 收集调用实参为有序实参字(保持源码求值序):静态实参 = 寄存器号, + /// spread 源 = `0x8000_0000 | 寄存器号`(高位标记区分)。 + pub(crate) fn emit_call_args( + &self, args: &[oxide_parser::Argument], ctx: &mut CompileCtx, + ) -> Result, String> { + let mut words = Vec::new(); + for arg in args { + if let Some(expr) = arg.as_expression() { + words.push(self.emit_expression(expr, ctx)?); + } else if let oxide_parser::Argument::SpreadElement(sp) = arg { + words.push(0x8000_0000 | self.emit_expression(&sp.argument, ctx)?); + } + } + Ok(words) + } +} diff --git a/crates/oxide_emit/src/expr/function/mod.rs b/crates/oxide_emit/src/expr/function/mod.rs index c1d3da3..cac6ea1 100644 --- a/crates/oxide_emit/src/expr/function/mod.rs +++ b/crates/oxide_emit/src/expr/function/mod.rs @@ -2,6 +2,7 @@ //! 函数:`emit_arrow_function_expression`、`emit_function_expression`、 //! `emit_class_expression`、`emit_new_expression`、`emit_function_domain`。 +use crate::expr::call::pack_arg_regs; use crate::{CompileCtx, Emitter, ParamSpec}; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; @@ -94,20 +95,20 @@ impl Emitter { fn emit_new_expression(&self, ne: &oxide_parser::NewExpression, ctx: &mut CompileCtx) -> Result { let constructor_reg = self.emit_expression(&ne.callee, ctx)?; - let mut arg_regs = Vec::new(); - for arg in &ne.arguments { - if let Some(expr) = arg.as_expression() { - arg_regs.push(self.emit_expression(expr, ctx)?); - } - } - let first_arg_reg = if arg_regs.is_empty() { 0u32 } else { arg_regs[0] }; + let words = self.emit_call_args(&ne.arguments, ctx)?; let r = ctx.alloc_reg(); - ctx.inst(Inst::new_expression( - Operand::Reg(r), - Operand::Reg(constructor_reg), - Operand::Reg(first_arg_reg), - arg_regs.len() as u8, - )); + if words.iter().any(|w| w >> 31 == 1) { + ctx.inst(Inst::new_expression_spread(Operand::Reg(r), Operand::Reg(constructor_reg), &words)); + } else { + let mut static_regs = words; + let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) }; + ctx.inst(Inst::new_expression( + Operand::Reg(r), + Operand::Reg(constructor_reg), + Operand::Reg(first_arg_reg), + static_regs.len() as u8, + )); + } Ok(r) } @@ -121,4 +122,3 @@ impl Emitter { } } } - diff --git a/crates/oxide_emit/src/prepass.rs b/crates/oxide_emit/src/prepass.rs index 44d2526..468057b 100644 --- a/crates/oxide_emit/src/prepass.rs +++ b/crates/oxide_emit/src/prepass.rs @@ -135,17 +135,13 @@ impl Emitter { Expression::CallExpression(call) => { self.pre_scan_builtin_expr(&call.callee, ctx); for arg in &call.arguments { - if let Some(e) = arg.as_expression() { - self.pre_scan_builtin_expr(e, ctx); - } + self.pre_scan_builtin_arg(arg, ctx); } } Expression::NewExpression(ne) => { self.pre_scan_builtin_expr(&ne.callee, ctx); for arg in &ne.arguments { - if let Some(e) = arg.as_expression() { - self.pre_scan_builtin_expr(e, ctx); - } + self.pre_scan_builtin_arg(arg, ctx); } } Expression::LogicalExpression(log) => { @@ -254,15 +250,22 @@ impl Emitter { oxide_parser::ChainElement::CallExpression(call) => { self.pre_scan_builtin_expr(&call.callee, ctx); for arg in &call.arguments { - if let Some(e) = arg.as_expression() { - self.pre_scan_builtin_expr(e, ctx); - } + self.pre_scan_builtin_arg(arg, ctx); } } _ => {} } } + /// 遍历调用实参:spread 内部表达式也纳入 builtin 预扫描。 + fn pre_scan_builtin_arg(&self, arg: &oxide_parser::Argument, ctx: &mut CompileCtx) { + if let Some(e) = arg.as_expression() { + self.pre_scan_builtin_expr(e, ctx); + } else if let oxide_parser::Argument::SpreadElement(sp) = arg { + self.pre_scan_builtin_expr(&sp.argument, ctx); + } + } + pub(crate) fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) { for statement in statements { let Statement::FunctionDeclaration(function) = statement else { diff --git a/crates/oxide_emit/src/shared/chain.rs b/crates/oxide_emit/src/shared/chain.rs index 69d1b14..f252a45 100644 --- a/crates/oxide_emit/src/shared/chain.rs +++ b/crates/oxide_emit/src/shared/chain.rs @@ -1,6 +1,7 @@ //! 可选链(`?.`)emit:短路守卫、链式成员访问/调用与逻辑赋值短路测试。 //! 函数:`emit_chainable_expression`、`emit_optional_guard`、`emit_chain_call` 等。 +use crate::expr::call::pack_arg_regs; use crate::{CompileCtx, Emitter}; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; @@ -106,36 +107,36 @@ impl Emitter { self.emit_optional_guard(callee_reg, label, ctx)?; } } - let mut arg_regs = Vec::new(); - for arg in &call.arguments { - if let Some(expr) = arg.as_expression() { - arg_regs.push(self.emit_expression(expr, ctx)?); + let words = self.emit_call_args(&call.arguments, ctx)?; + if words.iter().any(|w| w >> 31 == 1) { + ctx.inst(Inst::call_spread(Operand::Reg(callee_reg), Operand::Reg(this_reg), &words)); + } else { + let mut static_regs = words; + let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) }; + let op = match &call.callee { + Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE, + _ => OpCode::CALL, + }; + match op { + OpCode::CALL => { + ctx.inst(Inst::call( + Operand::Reg(callee_reg), + Operand::Reg(this_reg), + Operand::Reg(first_arg_reg), + static_regs.len() as u8, + )); + } + OpCode::CALL_NATIVE => { + ctx.inst(Inst::call_native( + Operand::Reg(callee_reg), + Operand::Reg(this_reg), + Operand::Reg(first_arg_reg), + static_regs.len() as u8, + )); + } + _ => unreachable!(), } } - let first_arg_reg = if arg_regs.is_empty() { 0u32 } else { arg_regs[0] }; - let op = match &call.callee { - Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE, - _ => OpCode::CALL, - }; - match op { - OpCode::CALL => { - ctx.inst(Inst::call( - Operand::Reg(callee_reg), - Operand::Reg(this_reg), - Operand::Reg(first_arg_reg), - arg_regs.len() as u8, - )); - } - OpCode::CALL_NATIVE => { - ctx.inst(Inst::call_native( - Operand::Reg(callee_reg), - Operand::Reg(this_reg), - Operand::Reg(first_arg_reg), - arg_regs.len() as u8, - )); - } - _ => unreachable!(), - } let result_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::None, Operand::None)); Ok(result_reg) diff --git a/crates/oxide_ir/src/contract.rs b/crates/oxide_ir/src/contract.rs index f2ce336..c2b67f9 100644 --- a/crates/oxide_ir/src/contract.rs +++ b/crates/oxide_ir/src/contract.rs @@ -18,7 +18,7 @@ impl Inst { pub fn def_reg(&self) -> Option { match self.op { // CALL 系:结果隐式写 reg 0,rd 是 callee 的 use - OpCode::CALL | OpCode::CALL_NATIVE => Some(0), + OpCode::CALL | OpCode::CALL_NATIVE | OpCode::CALL_SPREAD => Some(0), // GET_PROP 系:结果写 a/b 槽而非 rd OpCode::GET_PROP | OpCode::IC_GET_PROP => reg_of(&self.a), OpCode::GET_PROP_DYNAMIC => reg_of(&self.b), @@ -91,6 +91,25 @@ impl Inst { let nargs = self.ext.first().copied().unwrap_or(0); push_range(&mut uses, reg_of(&self.a), nargs); } + // spread 调用系:ext 内嵌有序实参字(静态寄存器号 / `0x8000_0000|reg` spread 源) + OpCode::CALL_SPREAD => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.a); + for &w in self.ext.iter().skip(1) { + uses.push(w & 0x7FFF_FFFF); + } + } + OpCode::NEW_EXPRESSION_SPREAD => { + push_operand(&mut uses, &self.a); + for &w in self.ext.iter().skip(1) { + uses.push(w & 0x7FFF_FFFF); + } + } + OpCode::SUPER_CALL_SPREAD => { + for &w in self.ext.iter().skip(1) { + uses.push(w & 0x7FFF_FFFF); + } + } // GET_PROP 系:结果写 a/b 槽,rd=obj 是 use OpCode::GET_PROP => { push_operand(&mut uses, &self.rd); @@ -435,6 +454,24 @@ mod tests { assert_eq!(inst.use_regs().as_slice(), &[0]); } + #[test] + fn spread_call_def_and_uses() { + // CALL_SPREAD:结果隐式写 reg 0;use = callee + this + ext 有序实参字(静态/spread 同编) + let call = Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[5, 0x8000_0000 | 9, 300]); + assert_eq!(call.def_reg(), Some(0)); + assert_eq!(call.use_regs().as_slice(), &[0, 1, 5, 9, 300]); + + // NEW_EXPRESSION_SPREAD:rd 即 def;use = ctor + 实参字 + let ne = Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[7, 0x8000_0000 | 12]); + assert_eq!(ne.def_reg(), Some(3)); + assert_eq!(ne.use_regs().as_slice(), &[0, 7, 12]); + + // SUPER_CALL_SPREAD:无 callee/this 槽 + let sc = Inst::super_call_spread(Operand::Reg(3), &[4, 0x8000_0000 | 8]); + assert_eq!(sc.def_reg(), Some(3)); + assert_eq!(sc.use_regs().as_slice(), &[4, 8]); + } + #[test] fn void_defs_rd_no_use() { // VM 的 VOID handler 不读 a 槽 @@ -630,6 +667,9 @@ mod tests { assert!(!Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0).is_pure(&f)); assert!(!Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 0).is_pure(&f)); assert!(!Inst::super_call(Operand::Reg(3), Operand::Reg(1), 0).is_pure(&f)); + assert!(!Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[3]).is_pure(&f)); + assert!(!Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[2]).is_pure(&f)); + assert!(!Inst::super_call_spread(Operand::Reg(3), &[2]).is_pure(&f)); assert!(!Inst::new(OpCode::CREATE_REGEXP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); assert!(!Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None).is_pure(&f)); assert!(!Inst::new(OpCode::THROW, Operand::Reg(2), Operand::None, Operand::None).is_pure(&f)); diff --git a/crates/oxide_ir/src/inst.rs b/crates/oxide_ir/src/inst.rs index f2e7feb..a3fe203 100644 --- a/crates/oxide_ir/src/inst.rs +++ b/crates/oxide_ir/src/inst.rs @@ -116,6 +116,26 @@ impl Inst { Self::with_ext(OpCode::SUPER_CALL, result, first_arg, Operand::None, &[nargs as u32]) } + /// spread 实参调用:rd=callee,a=this,b 槽未用。 + /// ext=[nstatic|(nspread<<8), 有序实参字…];每个实参字:静态实参为寄存器号, + /// spread 源为 `0x8000_0000 | 寄存器号`(高位标记),按源码求值序排列。 + pub fn call_spread(callee: Operand, this: Operand, words: &[u32]) -> Self { + let ext = spread_ext(words); + Self::with_ext(OpCode::CALL_SPREAD, callee, this, Operand::None, &ext) + } + + /// spread 实参 `new`:结果写入 `result`,构造函数为 `constructor`。 + pub fn new_expression_spread(result: Operand, constructor: Operand, words: &[u32]) -> Self { + let ext = spread_ext(words); + Self::with_ext(OpCode::NEW_EXPRESSION_SPREAD, result, constructor, Operand::None, &ext) + } + + /// 派生类构造中的 `super(...spread)`:结果写入 `result`。 + pub fn super_call_spread(result: Operand, words: &[u32]) -> Self { + let ext = spread_ext(words); + Self::with_ext(OpCode::SUPER_CALL_SPREAD, result, Operand::None, Operand::None, &ext) + } + // ── 其他带 ext 字 ── /// 定义访问器属性:home 为宿主对象,get/set 为访问器函数寄存器,key_idx 为属性名常量下标。 @@ -217,6 +237,17 @@ impl Inst { } } +/// spread 调用系 ext 构造:首字打包 `nstatic | (nspread << 8)`,后续按源码求值序排列 +/// 每个实参字(静态实参 = 寄存器号,spread 源 = `0x8000_0000 | 寄存器号`)。 +fn spread_ext(words: &[u32]) -> SmallVec<[u32; 4]> { + let nspread = words.iter().filter(|w| *w >> 31 == 1).count() as u32; + let nstatic = (words.len() as u32) - nspread; + let mut ext = SmallVec::with_capacity(1 + words.len()); + ext.push(nstatic | (nspread << 8)); + ext.extend_from_slice(words); + ext +} + #[cfg(test)] mod tests { use super::*; @@ -282,6 +313,29 @@ mod tests { assert_eq!(rest.ext.as_slice(), &[7]); } + #[test] + fn spread_call_constructors_carry_header_and_source_regs() { + let call = Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[10, 0x8000_0000 | 300]); + assert_eq!(call.op, OpCode::CALL_SPREAD); + assert_eq!(call.rd, Operand::Reg(0)); + assert_eq!(call.a, Operand::Reg(1)); + assert_eq!(call.b, Operand::None); + assert_eq!(call.ext.as_slice(), &[1 | (1 << 8), 10, 0x8000_0000 | 300]); + + let ne = Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[0x8000_0000 | 7]); + assert_eq!(ne.op, OpCode::NEW_EXPRESSION_SPREAD); + assert_eq!(ne.rd, Operand::Reg(3)); + assert_eq!(ne.a, Operand::Reg(0)); + assert_eq!(ne.ext.as_slice(), &[1 << 8, 0x8000_0000 | 7]); + + let sc = Inst::super_call_spread(Operand::Reg(3), &[1, 0x8000_0000 | 5]); + assert_eq!(sc.op, OpCode::SUPER_CALL_SPREAD); + assert_eq!(sc.rd, Operand::Reg(3)); + assert_eq!(sc.a, Operand::None); + assert_eq!(sc.b, Operand::None); + assert_eq!(sc.ext.as_slice(), &[1 | (1 << 8), 1, 0x8000_0000 | 5]); + } + #[test] fn regalloc_constructors_carry_expected_slots() { let mov = Inst::inst_mov(Operand::Reg(3), Operand::Reg(7)); diff --git a/crates/oxide_regalloc/src/graph.rs b/crates/oxide_regalloc/src/graph.rs index 41335ea..9ed54f0 100644 --- a/crates/oxide_regalloc/src/graph.rs +++ b/crates/oxide_regalloc/src/graph.rs @@ -70,6 +70,15 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet { } } } + // spread 调用系:ext 首字之后每个有序实参字是寄存器号(spread 源带高位标记) + if matches!(inst.op, OpCode::CALL_SPREAD | OpCode::NEW_EXPRESSION_SPREAD | OpCode::SUPER_CALL_SPREAD) { + for &w in inst.ext.iter().skip(1) { + let r = w & 0x7FFF_FFFF; + if r != 0 { + real.insert(r); + } + } + } } real } @@ -116,6 +125,7 @@ pub(super) fn build( } // ── 可分配色集 ── + // 窗口只为参数连续性 MOV 桥预留;spread 调用实参经 ext 逐个读寄存器,无连续性要求。 let max_nargs = f .insts .iter() diff --git a/crates/oxide_regalloc/src/rewrite.rs b/crates/oxide_regalloc/src/rewrite.rs index 1b96ece..3b48a0b 100644 --- a/crates/oxide_regalloc/src/rewrite.rs +++ b/crates/oxide_regalloc/src/rewrite.rs @@ -134,7 +134,8 @@ pub(super) fn run(f: &mut IRFunction, map: &AllocMap) { fn rewrite_inst( f: &IRFunction, inst: &Inst, map: &AllocMap, slot_color: &BTreeMap, i: usize, ) -> (Inst, Vec<(u32, u32)>) { - let nargs = inst.ext.first().copied().unwrap_or(0); + let raw_nargs = inst.ext.first().copied().unwrap_or(0); + let nargs = raw_nargs; let first_arg = match inst.op { OpCode::CALL | OpCode::CALL_NATIVE | OpCode::NEW_EXPRESSION => Some(inst.b), OpCode::SUPER_CALL => Some(inst.a), @@ -197,26 +198,34 @@ fn rewrite_inst( let mut a = rewrite(inst.a); let mut b = rewrite(inst.b); // TEMPLATE_STR 的 ext 编码表达式寄存器(seg>>31==1 时低 8 位为 expr_reg)——必须随 - // RegAlloc 重映射,否则读旧 vreg 号对应的物理槽(错值)。 - let ext = if inst.op == OpCode::TEMPLATE_STR { - let mut ext = inst.ext.clone(); - for seg in ext.iter_mut().skip(1) { - if *seg >> 31 == 1 { - let r = *seg & 0xFF; - let nr = if let Some(&c) = slot_color.get(&r) { - c - } else { - match map.map.get(&r) { - Some(Alloc::Phys(p)) => *p, - _ => r, // 非真实 vreg / spilled 未覆盖(防御保留原号) - } - }; - *seg = (*seg & !0xFFu32) | (nr & 0xFF); + // RegAlloc 重映射,否则读旧 vreg 号对应的物理槽(错值)。spread 调用系 ext[1..] 每个 + // 字是完整 spread 源 vreg,同样需重映射到物理号。 + let ext = match inst.op { + OpCode::TEMPLATE_STR => { + let mut ext = inst.ext.clone(); + for seg in ext.iter_mut().skip(1) { + if *seg >> 31 == 1 { + let r = *seg & 0xFF; + let nr = remap_ext_reg(r, slot_color, map); + *seg = (*seg & !0xFFu32) | (nr & 0xFF); + } } + ext } - ext - } else { - inst.ext.clone() + OpCode::CALL_SPREAD | OpCode::NEW_EXPRESSION_SPREAD | OpCode::SUPER_CALL_SPREAD => { + // 有序实参字:静态字直接重映射,spread 字保留高位标记、低 31 位重映射。 + let mut ext = inst.ext.clone(); + for seg in ext.iter_mut().skip(1) { + if *seg >> 31 == 1 { + let nr = remap_ext_reg(*seg & 0x7FFF_FFFF, slot_color, map); + *seg = 0x8000_0000 | nr; + } else { + *seg = remap_ext_reg(*seg, slot_color, map); + } + } + ext + } + _ => inst.ext.clone(), }; // 调用点首参槽改指 arg_window_base(桥接后) if let Some(ab) = arg_base { @@ -257,6 +266,19 @@ fn rewrite_inst( (Inst { op: inst.op, rd, a, b, ext }, arg_movs) } +/// ext 内嵌寄存器重映射:优先用本指令点的 fresh 色(spilled vreg 的 use 点 UNSPILL), +/// 其次 AllocMap 物理色;未映射(防御)保留原号。 +fn remap_ext_reg(r: u32, slot_color: &BTreeMap, map: &AllocMap) -> u32 { + if let Some(&c) = slot_color.get(&r) { + c + } else { + match map.map.get(&r) { + Some(Alloc::Phys(p)) => *p, + _ => r, + } + } +} + /// terminator 判定:跳转族 + RETURN/HALT/THROW(def 结果必死,SPILL 跳过)。 fn is_terminator(op: OpCode) -> bool { matches!( diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 3e4939f..faaeb75 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -1,9 +1,10 @@ use crate::native::NativeFn; use crate::vm::{native_fn_ptr_to_fn, CallFrame, FrameContinuation, Vm}; use crate::{vm_debug, vm_trace}; +use oxide_builtins::iterator::make_iterator_for_value; use oxide_builtins::{builtins_debug, builtins_trace}; use oxide_bytecode::opcode; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{to_boolean, NativeResult}; use oxide_types::object::{Cell, JsObject}; use oxide_types::value::JsValue; use std::sync::Arc; @@ -130,14 +131,15 @@ impl Vm { // 函数名推断:emit 端在变量声明/对象属性赋值点设置 function_name。 let func_obj = unsafe { &mut *result.as_js_object_ptr() }; let name_si = self.kernel_core.perm_interner().intern("name").0; - let name_val = function_name.as_deref().map(|n| self.new_string(n)).unwrap_or_else(|| self.new_string("")); + let name_val = function_name + .as_deref() + .map(|n| self.new_string(n)) + .unwrap_or_else(|| self.new_string("")); self.set_or_create_prop_value(func_obj, name_si, name_val); if !upvalue_captures.is_empty() { // 链式捕获(parent_uv_idx):从父闭包(创建者)的 upvalues 取 cell。 let parent_upvalues: Vec<*mut Cell> = match self.current_callee() { - Some(callee) if callee.is_object() => { - unsafe { &*callee.as_js_object_ptr() }.upvalues_slice().to_vec() - } + Some(callee) if callee.is_object() => unsafe { &*callee.as_js_object_ptr() }.upvalues_slice().to_vec(), _ => Vec::new(), }; if let Some(current_cells) = self.cell_stack.last_mut() { @@ -156,8 +158,7 @@ impl Vm { if current_cells[cell_idx].is_null() { // 占位 Cell 保持未初始化(TDZ)直到 MAKE_CELL 置位; // 若绑定为 var 则其初始化 MAKE_CELL 在函数序言先于任何读取执行。 - let cell = - self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), false)); + let cell = self.gc_state.session_epoch.alloc(Cell::new(JsValue::undefined(), false)); current_cells[cell_idx] = cell as *mut Cell; } current_cells[cell_idx] @@ -254,7 +255,8 @@ impl Vm { if !cell.is_null() { let c = unsafe { &*cell }; if !c.is_initialized() { - return self.raise_error_kind("ReferenceError", "Cannot access variable before initialization"); + return self + .raise_error_kind("ReferenceError", "Cannot access variable before initialization"); } self.regs[rd] = c.value; return Ok(()); @@ -590,3 +592,342 @@ impl Vm { Ok(false) } } + +impl Vm { + /// 解析 spread 调用系 ext:读走 `nstatic | (nspread<<8)` 首字与 `nstatic+nspread` + /// 个有序实参字(静态字 = 寄存器号,spread 字高位标记)。 + fn read_spread_ext(&mut self) -> Vec { + let header = self.bytecode[self.pc]; + self.pc += 1; + let nstatic = (header & 0xFF) as usize; + let nspread = (header >> 8) as usize; + let mut words = Vec::with_capacity(nstatic + nspread); + for _ in 0..nstatic + nspread { + words.push(self.bytecode[self.pc] as usize); + self.pc += 1; + } + words + } + + /// 按源码求值序物化完整实参 Vec:静态字直接读寄存器,spread 字迭代展开。 + /// + /// # 步骤 + /// 1. 静态实参逐个从寄存器读入;spread 源经 `make_iterator_for_value` 迭代追加。 + /// 2. 迭代中途抛错 → 当前迭代器经 return() 关闭(IteratorClose),异常恢复为 JS + /// 异常后 unwind。 + /// 3. 展开总数超过 u16 上限(arguments_count 字段)→ RangeError。 + /// + /// # 返回值 + /// - `Ok(Some(vec))` 成功; + /// - `Ok(None)` 异常已展开(调用方按 continue 处理); + /// - `Err` 引擎错误直接传播。 + fn materialize_spread_args(&mut self, words: &[usize]) -> Result>, String> { + let mut args = Vec::with_capacity(words.len()); + let next_si = self.kernel_core.perm_interner().intern("next").0; + let done_si = self.kernel_core.perm_interner().intern("done").0; + let value_si = self.kernel_core.perm_interner().intern("value").0; + for &w in words { + if w >> 31 == 1 { + let value = self.regs[w & 0x7FFF_FFFF]; + let iterator = match make_iterator_for_value(self, value) { + Ok(it) => it, + Err(err) => { + self.exception_value = Some(err); + self.pending_error_kind = Some(self.thrown_error_kind(err)); + return self.unwind().map(|_| None); + } + }; + loop { + let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; + let next_fn = match self.ordinary_get(iter_obj, next_si, iterator) { + Ok(v) => v, + Err(e) => return self.spread_iter_error(iterator, e, "Error"), + }; + let result = match self.call_function_sync(next_fn, iterator, &[]) { + Ok(v) => v, + Err(e) => return self.spread_iter_error(iterator, e, "Error"), + }; + if !result.is_object() { + return self.spread_iter_error( + iterator, + self.error_message_text("TypeError", "iterator result is not an object"), + "TypeError", + ); + } + let result_obj = unsafe { &*result.as_js_object_ptr() }; + let done = match self.ordinary_get(result_obj, done_si, result) { + Ok(v) => to_boolean(v), + Err(e) => return self.spread_iter_error(iterator, e, "Error"), + }; + if done { + break; + } + let val = match self.ordinary_get(result_obj, value_si, result) { + Ok(v) => v, + Err(e) => return self.spread_iter_error(iterator, e, "Error"), + }; + args.push(val); + if args.len() > u16::MAX as usize { + self.raise_error_kind("RangeError", "Too many arguments in function call")?; + return Ok(None); + } + } + } else { + args.push(self.regs[w]); + if args.len() > u16::MAX as usize { + self.raise_error_kind("RangeError", "Too many arguments in function call")?; + return Ok(None); + } + } + } + Ok(Some(args)) + } + + /// spread 迭代中途异常:先关闭当前迭代器(suppress return 自身错误,保留在途异常), + /// 再把原异常(native 错误经 last_uncaught_value 恢复)重新抛出并展开。 + fn spread_iter_error( + &mut self, iterator: JsValue, msg: String, fallback_kind: &'static str, + ) -> Result>, String> { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_kind_error(self, fallback_kind, &msg)); + self.close_for_of_iterator(iterator, true)?; + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); + self.unwind().map(|_| None) + } + + /// CALL_SPREAD:运行期物化完整实参后按目标分派(native 走值传递,字节码走帧)。 + pub(crate) fn dispatch_call_spread(&mut self, rd: usize, a: usize, _b: usize) -> Result { + let callee = self.regs[rd]; + let this_value = self.regs[a]; + let words = self.read_spread_ext(); + let args = match self.materialize_spread_args(&words)? { + Some(args) => args, + None => return Ok(true), + }; + if callee.is_object() { + let obj_ptr = callee.as_js_object_ptr(); + if !obj_ptr.is_null() { + let obj = unsafe { &*obj_ptr }; + if obj.is_function() { + if obj.is_class_constructor() { + return self + .raise_type_error("class constructor cannot be invoked without 'new'") + .map(|_| true); + } + if obj.native_fn().is_some() { + match self.call_function_sync(callee, this_value, &args) { + Ok(v) => { + self.regs[0] = v; + return Ok(false); + } + Err(_) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(self, "call failed")); + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); + return self.unwind().map(|_| true); + } + } + } else if obj.sub_module_index() > 0 { + self.push_bytecode_frame( + callee, + this_value, + &args, + None, + None, + JsValue::undefined(), + FrameContinuation::None, + )?; + return Ok(true); + } + } + } + } + self.raise_type_error("CALL target is not callable").map(|_| true) + } + + /// NEW_EXPRESSION_SPREAD:物化实参后按构造函数目标分派(native 走值传递,字节码走帧)。 + pub(crate) fn dispatch_new_expression_spread(&mut self, rd: usize, a: usize, _b: usize) -> Result { + let constructor_reg = a; + let constructor = self.regs[constructor_reg]; + + if !constructor.is_object() { + return self + .raise_type_error("NEW_EXPRESSION: constructor is not an object") + .map(|_| true); + } + let ctor_ptr = constructor.as_js_object_ptr(); + if ctor_ptr.is_null() { + return self.raise_type_error("NEW_EXPRESSION: constructor is null").map(|_| true); + } + let ctor_obj = unsafe { &*ctor_ptr }; + if !ctor_obj.is_function() { + return self + .raise_type_error("NEW_EXPRESSION: constructor is not a function") + .map(|_| true); + } + if ctor_obj.is_arrow() { + return self + .raise_type_error("arrow functions cannot be used as constructors") + .map(|_| true); + } + // native 方法(非构造器)不可 new。 + if ctor_obj.native_fn().is_some() && ctor_obj.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR { + return self.raise_type_error("object is not a constructor").map(|_| true); + } + + let words = self.read_spread_ext(); + let args = match self.materialize_spread_args(&words)? { + Some(args) => args, + None => return Ok(true), + }; + + let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject; + let new_obj = self.alloc_object(JsObject::new_empty( + oxide_kernel::shape_forge::EMPTY_SHAPE_ID, + JsValue::from_js_object(proto_ptr), + )); + let proto_si = self.kernel_core.perm_interner().intern("prototype").0; + if let Some(proto_val) = self.resolve_property(ctor_obj, proto_si) { + if proto_val.is_object() { + let new_obj_mut = unsafe { &mut *new_obj }; + let proto_obj_ptr = proto_val.as_js_object_ptr(); + let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr)); + } + } + let new_obj_val = JsValue::object(new_obj as *mut u8); + + if ctor_obj.native_fn().is_some() { + // native 构造器:receiver 为新对象,值传递调用。 + match self.call_function_sync(constructor, new_obj_val, &args) { + Ok(v) => { + self.regs[rd] = if v.is_object() { v } else { new_obj_val }; + Ok(false) + } + Err(_) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(self, "constructor call failed")); + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); + self.unwind().map(|_| true) + } + } + } else if ctor_obj.sub_module_index() > 0 { + if self.frames.len() >= self.kernel_core.config.max_call_depth { + return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded")); + } + let this_value = if ctor_obj.is_derived_constructor() { + JsValue::undefined() + } else { + new_obj_val + }; + self.push_bytecode_frame( + constructor, + this_value, + &args, + Some(rd as u8), + Some(new_obj_val), + constructor, + FrameContinuation::None, + )?; + Ok(true) + } else { + let error = + oxide_builtins::error::create_error(self, "NEW_EXPRESSION: bytecode constructors not yet supported"); + self.exception_value = Some(error); + self.pending_error_kind = Some(self.thrown_error_kind(error)); + self.unwind().map(|_| true) + } + } + + /// SUPER_CALL_SPREAD:物化实参后按父构造器目标分派(native 走值传递,字节码走帧)。 + pub(crate) fn dispatch_super_call_spread(&mut self, rd: usize, _a: usize) -> Result { + let words = self.read_spread_ext(); + + let Some(frame) = self.frames.last() else { + self.raise_error_kind("ReferenceError", "super() used outside class constructor")?; + return Ok(true); + }; + if !frame.is_derived_constructor { + self.raise_error_kind("ReferenceError", "super() used outside derived constructor")?; + return Ok(true); + } + if !self.regs[254].is_undefined() { + self.raise_error_kind("ReferenceError", "super() called more than once")?; + return Ok(true); + } + let Some(derived_this) = frame.constructed_this else { + self.raise_error_kind("ReferenceError", "super() without derived this")?; + return Ok(true); + }; + let new_target = self.regs[255]; + if !new_target.is_object() { + self.raise_error_kind("TypeError", "super() new.target is not an object")?; + return Ok(true); + } + let new_target_obj = unsafe { &*new_target.as_js_object_ptr() }; + let super_ctor = new_target_obj.proto(); + if !super_ctor.is_object() { + self.raise_error_kind("TypeError", "super constructor is not an object")?; + return Ok(true); + } + let super_obj = unsafe { &*super_ctor.as_js_object_ptr() }; + if !super_obj.is_function() { + self.raise_error_kind("TypeError", "super constructor is not a function")?; + return Ok(true); + } + + let args = match self.materialize_spread_args(&words)? { + Some(args) => args, + None => return Ok(true), + }; + + if super_obj.native_fn().is_some() { + match self.call_function_sync(super_ctor, derived_this, &args) { + Ok(val) => { + // super() 返回实例的 [[Prototype]] 须设为 new.target.prototype + let instance = if val.is_object() { val } else { derived_this }; + if instance.is_object() { + self.set_constructed_proto(instance, new_target_obj)?; + } + self.regs[254] = instance; + self.regs[rd] = instance; + } + Err(_) => { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(self, "super() call failed")); + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); + self.unwind().map(|_| true)?; + } + } + Ok(false) + } else if super_obj.sub_module_index() > 0 { + if self.frames.len() >= self.kernel_core.config.max_call_depth { + return Err(self.error_message_text("RangeError", "Maximum call stack size exceeded")); + } + self.push_bytecode_frame( + super_ctor, + derived_this, + &args, + Some(254), + Some(derived_this), + new_target, + FrameContinuation::None, + )?; + Ok(true) + } else { + self.raise_error_kind("TypeError", "super constructor is not callable")?; + Ok(true) + } + } +} diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 7856ae3..12a1145 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -1110,18 +1110,36 @@ impl Vm { 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, diff --git a/crates/oxide_vm/tests/call_spread_tests.rs b/crates/oxide_vm/tests/call_spread_tests.rs new file mode 100644 index 0000000..12c0553 --- /dev/null +++ b/crates/oxide_vm/tests/call_spread_tests.rs @@ -0,0 +1,131 @@ +//! 调用实参 spread 执行语义测试:`f(...args)`、`new F(...args)`、`super(...args)`。 +//! 覆盖静态/混合实参顺序、迭代器协议、IteratorClose、native 目标、求值顺序。 + +use oxide_compiler::compiler::Compiler; +use oxide_types::value::JsValue; +use oxide_vm::vm::Vm; + +fn eval(vm: &mut Vm, source: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; + let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?; + vm.run(&module) +} + +fn to_str(vm: &Vm, val: JsValue) -> String { + if val.is_string() { + vm.lookup_str(val).unwrap_or_default() + } else { + format!("{}", val) + } +} + +fn to_num(val: JsValue) -> f64 { + if val.is_int() { + val.as_int() as f64 + } else { + val.as_double() + } +} + +#[test] +fn spread_single_literal_preserves_all_args() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(){return arguments.length}; f(...[1,2,3])").unwrap(); + assert_eq!(result.as_int(), 3); +} + +#[test] +fn spread_interleaved_static_and_spread_keeps_source_order() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(a,b,c){return [a,b,c].join('-')}; f('x', ...['y'], 'z')").unwrap(); + assert_eq!(to_str(&vm, result), "x-y-z"); +} + +#[test] +fn spread_multiple_sources_append_in_order() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(){return arguments.length}; f(...[1], 2, ...[3,4])").unwrap(); + assert_eq!(result.as_int(), 4); +} + +#[test] +fn spread_native_target() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "Math.max(...[1,5,3])").unwrap(); + assert_eq!(to_num(result), 5.0); +} + +#[test] +fn spread_new_expression_bytecode_ctor() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "new (function(a,b){this.sum=a+b})(...[2,3]).sum").unwrap(); + assert_eq!(to_num(result), 5.0); +} + +#[test] +fn spread_new_expression_native_ctor() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "new Date(...[2020,1,1]).getFullYear()").unwrap(); + assert_eq!(to_num(result), 2020.0); +} + +#[test] +fn spread_super_call_passes_args() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class A { constructor(a,b,c){ this.v=[a,b,c].join('') } }; \ + class B extends A { constructor(){ super(...[1,2,3]) } }; new B().v", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "123"); +} + +#[test] +fn spread_non_iterable_throws_type_error() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "try { (function(){})(...null); 'no-throw' } catch(e) { e.constructor.name }").unwrap(); + assert_eq!(to_str(&vm, result), "TypeError"); +} + +#[test] +fn spread_invokes_iterator_close_on_abrupt_completion() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var closed=0; try { (function(){}) (...{[Symbol.iterator]:function(){ return { \ + next:function(){ throw new Error('boom') }, \ + return:function(){ closed=1; return {} } } }}) } catch(e) {}; closed", + ) + .unwrap(); + assert_eq!(result.as_int(), 1); +} + +#[test] +fn spread_does_not_close_iterator_on_normal_completion() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var closed=0; var i=0; (function(){}) (...{[Symbol.iterator]:function(){ return { \ + next:function(){ return ++i<=2 ? {done:false,value:i} : {done:true} }, \ + return:function(){ closed=1; return {} } } }}); closed", + ) + .unwrap(); + assert_eq!(result.as_int(), 0); +} + +#[test] +fn spread_closure_capture_survives() { + let mut vm = Vm::new(); + let result = + eval(&mut vm, "function outer(){var x=42; function g(a){return a+x}; return g(...[x])} outer()").unwrap(); + assert_eq!(to_num(result), 84.0); +} + +#[test] +fn spread_string_source() { + let mut vm = Vm::new(); + let result = eval(&mut vm, "function f(){return arguments.length}; f(...'ab')").unwrap(); + assert_eq!(result.as_int(), 2); +}