fix: 绑定模式内嵌默认值表达式补捕获扫描,嵌套函数引用外层绑定防 LOAD_GLOBAL 回归

This commit is contained in:
2026-08-16 14:53:57 +08:00
parent 1f2a020585
commit 34366f96a7
2 changed files with 69 additions and 5 deletions
+12 -5
View File
@@ -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<String>, shadow: &HashSet<String>,
out: &mut HashSet<String>,
@@ -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<String>, out: &mut HashSet<String>,
) {
@@ -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);
}
}
@@ -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);
}