From 277429e3eaef5d9029c4c8e67cff2996f004cdcd Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 16 Aug 2026 14:53:57 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BB=91=E5=AE=9A=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=86=85=E5=B5=8C=E9=BB=98=E8=AE=A4=E5=80=BC=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E8=A1=A5=E6=8D=95=E8=8E=B7=E6=89=AB=E6=8F=8F=EF=BC=8C?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=E5=87=BD=E6=95=B0=E5=BC=95=E7=94=A8=E5=A4=96?= =?UTF-8?q?=E5=B1=82=E7=BB=91=E5=AE=9A=E9=98=B2=20LOAD=5FGLOBAL=20?= =?UTF-8?q?=E5=9B=9E=E5=BD=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/closure.rs | 17 ++++-- crates/oxide_vm/tests/destructuring_tests.rs | 57 ++++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/crates/oxide_emit/src/closure.rs b/crates/oxide_emit/src/closure.rs index 9ec7792c..f6dd6f6f 100644 --- a/crates/oxide_emit/src/closure.rs +++ b/crates/oxide_emit/src/closure.rs @@ -557,9 +557,9 @@ impl Emitter { } } - /// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)中的引用:模式键是运行时 - /// 求值的表达式,嵌套函数引用外层绑定时须纳入捕获(与赋值侧/对象字面量/类字段 - /// 键遍历同口径)。 + /// 遍历绑定 pattern 的运行时求值表达式引用:计算键(`{[k]: a}` 的键)与内嵌 + /// 默认值(`{a = x}` 的 x)都是模式内求值的表达式,嵌套函数引用外层绑定时须 + /// 纳入捕获(与赋值侧/对象字面量/类字段键遍历同口径)。 fn collect_capture_names_binding_keys( &self, pattern: &oxide_parser::BindingPattern, ref_set: &HashSet, shadow: &HashSet, out: &mut HashSet, @@ -586,6 +586,9 @@ impl Emitter { } } oxide_parser::BindingPattern::AssignmentPattern(ap) => { + // 内嵌默认值表达式引用外层绑定须捕获(与 binding_pattern/赋值侧 + // maybe_default 的 init 扫描同口径);left 继续递归模式键。 + self.collect_capture_names_expr(&ap.right, ref_set, shadow, out); self.collect_capture_names_binding_keys(&ap.left, ref_set, shadow, out); } } @@ -1131,8 +1134,9 @@ impl Emitter { } } - /// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)中的引用,供父层捕获判定 - /// (声明语句/for 头的模式键与 catch/默认值路径同口径)。 + /// 遍历绑定 pattern 的运行时求值表达式引用:计算键(`{[k]: a}` 的键)与内嵌 + /// 默认值(`{a = x}` 的 x),供父层捕获判定(声明语句/for 头的模式与 catch/ + /// 默认值路径同口径)。 fn collect_captured_binding_keys( &self, pattern: &oxide_parser::BindingPattern, own: &HashSet, out: &mut HashSet, ) { @@ -1158,6 +1162,9 @@ impl Emitter { } } oxide_parser::BindingPattern::AssignmentPattern(ap) => { + // 内嵌默认值表达式引用外层绑定须建 cell(与 captured_binding_pattern + // 同口径);left 继续递归模式键。 + self.collect_captured_expr(&ap.right, own, out); self.collect_captured_binding_keys(&ap.left, own, out); } } diff --git a/crates/oxide_vm/tests/destructuring_tests.rs b/crates/oxide_vm/tests/destructuring_tests.rs index aa5652f5..2d110d53 100644 --- a/crates/oxide_vm/tests/destructuring_tests.rs +++ b/crates/oxide_vm/tests/destructuring_tests.rs @@ -349,3 +349,60 @@ fn computed_key_binding_undeclared_key_throws_reference_error() { let err = eval(&mut vm, "const {[undeclaredKey]: a} = {x:1};").unwrap_err(); assert!(err.contains("ReferenceError"), "expected ReferenceError, got {err:?}"); } + +// 跨作用域:声明语句模式内嵌默认值引用外层绑定须走闭包捕获(修复前默认值 right +// 不扫 → x 落 LOAD_GLOBAL 兜底——无全局 x 抛 ReferenceError / 有全局 x 读错值)。 +#[test] +fn binding_default_captures_outer_in_declaration() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "function f(){ let x=42; return function(){ const {a = x} = {}; return a; }; } f()()", + ) + .unwrap(); + assert_num(result, 42.0); +} + +#[test] +fn binding_default_captures_outer_with_iife() { + // 内嵌默认值含 IIFE:默认值表达式整体在模式求值语境,IIFE 内引用同样须捕获。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "function f(){ let x=42; return function(){ const {a = (()=>x)()} = {}; return a; }; } f()()", + ) + .unwrap(); + assert_num(result, 42.0); +} + +#[test] +fn binding_default_captures_outer_in_for_of_left() { + // for-of left 解构模式的内嵌默认值引用外层绑定。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "function f(){ let x=42; return function(){ let r; for (const {a = x} of [{}]) { r = a; } return r; }; } f()()", + ) + .unwrap(); + assert_num(result, 42.0); +} + +#[test] +fn binding_default_captures_outer_in_array_element() { + // 数组解构元素的内嵌默认值引用外层绑定(ArrayPattern 元素同走 AssignmentPattern 分支)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "function f(){ let x=42; return function(){ const [a = x] = []; return a; }; } f()()", + ) + .unwrap(); + assert_num(result, 42.0); +} + +#[test] +fn binding_default_same_scope_not_regress() { + // 同作用域内嵌默认值走局部寄存器读,不依赖捕获,验证不误捕获/不破坏。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "const x=7; const {a = x} = {}; a").unwrap(); + assert_num(result, 7.0); +}