From d77649e8cfb51c8cadfbf94db566508332fda256 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 15 Aug 2026 12:13:41 +0800 Subject: [PATCH] =?UTF-8?q?test:=20NEW=20=E6=94=B6=E6=95=9B=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=8E=8B=E5=B8=A7=E5=8F=82=E6=95=B0=E9=87=8D=E5=8F=A0?= =?UTF-8?q?=E5=87=A0=E4=BD=95=E5=88=A4=E5=88=AB=E5=9B=9E=E5=BD=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一入口 spill 前置 + 形参源改读 spill 区已闭合 CALL/NEW/SUPER 三路径, NEW 入口(vm_dispatch_misc 收敛后)与普通调用同享该修复。补经 NEW_EXPRESSION 入口构造的重叠几何单元测试(实参源 regs[1..3) 与 callee 形参写入区 regs[2..4) 重叠,first < param_base):形参与 spill 实参区 (arguments 对象源)须取原始实参值,并校验 NEW 帧契约(construct_result_reg/ constructed_this/derived 标志)。该测试在重叠修复回退后真实 Red。 --- crates/oxide_vm/src/vm.rs | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 667d1d7..d8490d0 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -2667,4 +2667,47 @@ mod tests { assert_eq!(vm.spill_stack[base], JsValue::int(10), "spill 实参区 arg0"); assert_eq!(vm.spill_stack[base + 1], JsValue::int(20), "spill 实参区 arg1"); } + + #[test] + fn dispatch_new_expression_param_overlap_reads_spill_first() { + // NEW 收敛路径同款重叠几何(实参源 regs[1..3) 与 callee 形参写入区 regs[2..4) + // 重叠,first < param_base):经 NEW_EXPRESSION 入口压帧,形参与 spill 实参区 + // (arguments 对象源)必须取原始实参值,不得先写后读串值。 + let mut vm = Vm::new(); + // sub_modules[1] = 构造器:2 个形参,param_base=2(与调用方实参槽 2 重叠) + let mut ctor_mod = CompiledModule::new(); + ctor_mod.n_args = 2; + ctor_mod.param_base = 2; + ctor_mod.n_registers = 5; + ctor_mod.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::RETURN, 0, 0, 0)]); + vm.sub_modules = Arc::new(vec![Arc::new(CompiledModule::new()), Arc::new(ctor_mod)]); + vm.immutables_cache + .extend((0..vm.sub_modules.len().saturating_sub(vm.immutables_cache.len())).map(|_| OnceLock::new())); + vm.active_reg_limit = 8; + // NEW_EXPRESSION 指令:ext 低 8 位 = 实参个数 2,高 8 位 = 窗口 0(全量) + vm.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::NEW_EXPRESSION, 0, 0, 0), 2]); + vm.pc = 0; + // 调用方实参区 regs[1..3):arg0=10, arg1=20;regs[2] 同时是 callee 形参槽(param_base=2) + vm.regs[1] = JsValue::int(10); + vm.regs[2] = JsValue::int(20); + vm.regs[5] = vm.create_function_object(1, false, false, false, false); + + vm.dispatch_new_expression(0, 5, 1).expect("NEW 压帧成功"); + + let frame = vm.frames.last().expect("压帧后应有帧"); + // 形参从 spill 实参区取源:regs[2]=arg0=10,regs[3]=arg1=20(不得被先写覆盖) + assert_eq!(vm.regs[2], JsValue::int(10), "形参 a 应为实参 arg0"); + assert_eq!(vm.regs[3], JsValue::int(20), "形参 b 应为实参 arg1(不受先写覆盖)"); + // spill 实参区(arguments 对象源)保持原实参值 + let base = frame.arguments_base as usize; + assert_eq!(vm.spill_stack[base], JsValue::int(10), "spill 实参区 arg0"); + assert_eq!(vm.spill_stack[base + 1], JsValue::int(20), "spill 实参区 arg1"); + // NEW 帧契约:构造结果寄存器与 constructed_this 随帧携带 + assert_eq!(frame.construct_result_reg, Some(0), "构造结果写回 regs[0]"); + assert!( + frame.constructed_this.is_some_and(|v| v.is_object()), + "基类构造路径 constructed_this 应为新对象" + ); + assert_eq!(frame.is_derived_constructor, false, "普通函数非 derived 构造器"); + } }