fix: 绑定侧解构计算键参与闭包捕获遍历防静默错值

This commit is contained in:
2026-08-16 14:30:20 +08:00
parent 7de55b25d8
commit 84f84402ba
5 changed files with 306 additions and 10 deletions
+149 -2
View File
@@ -306,6 +306,8 @@ impl Emitter {
} }
Statement::VariableDeclaration(vd) => { Statement::VariableDeclaration(vd) => {
for d in &vd.declarations { for d in &vd.declarations {
// 绑定 pattern 的计算键是运行时求值表达式,其引用须纳入捕获。
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
if let Some(init) = &d.init { if let Some(init) = &d.init {
self.collect_capture_names_expr(init, ref_set, shadow, out); self.collect_capture_names_expr(init, ref_set, shadow, out);
} }
@@ -342,6 +344,7 @@ impl Emitter {
} }
if let oxide_parser::ForStatementInit::VariableDeclaration(vd) = init { if let oxide_parser::ForStatementInit::VariableDeclaration(vd) = init {
for d in &vd.declarations { for d in &vd.declarations {
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
if let Some(i) = &d.init { if let Some(i) = &d.init {
self.collect_capture_names_expr(i, ref_set, shadow, out); self.collect_capture_names_expr(i, ref_set, shadow, out);
} }
@@ -369,12 +372,22 @@ impl Emitter {
// left 的 var 声明遮蔽外层同名绑定,body 内引用不视为捕获外层。 // left 的 var 声明遮蔽外层同名绑定,body 内引用不视为捕获外层。
let mut for_shadow = shadow.clone(); let mut for_shadow = shadow.clone();
self.collect_for_left_decl_names(&fi.left, &mut for_shadow); self.collect_for_left_decl_names(&fi.left, &mut for_shadow);
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
for d in &vd.declarations {
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
}
}
self.collect_capture_names_shadowed(std::slice::from_ref(&fi.body), ref_set, &for_shadow, out); self.collect_capture_names_shadowed(std::slice::from_ref(&fi.body), ref_set, &for_shadow, out);
} }
Statement::ForOfStatement(fo) => { Statement::ForOfStatement(fo) => {
self.collect_capture_names_expr(&fo.right, ref_set, shadow, out); self.collect_capture_names_expr(&fo.right, ref_set, shadow, out);
let mut for_shadow = shadow.clone(); let mut for_shadow = shadow.clone();
self.collect_for_left_decl_names(&fo.left, &mut for_shadow); self.collect_for_left_decl_names(&fo.left, &mut for_shadow);
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
for d in &vd.declarations {
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
}
}
self.collect_capture_names_shadowed(std::slice::from_ref(&fo.body), ref_set, &for_shadow, out); self.collect_capture_names_shadowed(std::slice::from_ref(&fo.body), ref_set, &for_shadow, out);
} }
Statement::BlockStatement(b) => self.collect_capture_names_shadowed(&b.body, ref_set, shadow, out), Statement::BlockStatement(b) => self.collect_capture_names_shadowed(&b.body, ref_set, shadow, out),
@@ -410,6 +423,7 @@ impl Emitter {
match decl { match decl {
oxide_parser::Declaration::VariableDeclaration(vd) => { oxide_parser::Declaration::VariableDeclaration(vd) => {
for d in &vd.declarations { for d in &vd.declarations {
self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out);
if let Some(init) = &d.init { if let Some(init) = &d.init {
self.collect_capture_names_expr(init, ref_set, shadow, out); self.collect_capture_names_expr(init, ref_set, shadow, out);
} }
@@ -526,6 +540,10 @@ impl Emitter {
} }
oxide_parser::BindingPattern::ObjectPattern(op) => { oxide_parser::BindingPattern::ObjectPattern(op) => {
for prop in &op.properties { for prop in &op.properties {
// 计算键是运行时求值表达式,其引用须纳入捕获。
if prop.computed {
self.collect_capture_names_expr(prop.key.to_expression(), ref_set, shadow, out);
}
self.collect_capture_names_binding_pattern(&prop.value, ref_set, shadow, out, catch_shadow); self.collect_capture_names_binding_pattern(&prop.value, ref_set, shadow, out, catch_shadow);
} }
if let Some(rest) = &op.rest { if let Some(rest) = &op.rest {
@@ -539,6 +557,40 @@ impl Emitter {
} }
} }
/// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)中的引用:模式键是运行时
/// 求值的表达式,嵌套函数引用外层绑定时须纳入捕获(与赋值侧/对象字面量/类字段
/// 键遍历同口径)。
fn collect_capture_names_binding_keys(
&self, pattern: &oxide_parser::BindingPattern, ref_set: &HashSet<String>, shadow: &HashSet<String>,
out: &mut HashSet<String>,
) {
match pattern {
oxide_parser::BindingPattern::BindingIdentifier(_) => {}
oxide_parser::BindingPattern::ArrayPattern(ap) => {
for p in ap.elements.iter().flatten() {
self.collect_capture_names_binding_keys(p, ref_set, shadow, out);
}
if let Some(rest) = &ap.rest {
self.collect_capture_names_binding_keys(&rest.argument, ref_set, shadow, out);
}
}
oxide_parser::BindingPattern::ObjectPattern(op) => {
for prop in &op.properties {
if prop.computed {
self.collect_capture_names_expr(prop.key.to_expression(), ref_set, shadow, out);
}
self.collect_capture_names_binding_keys(&prop.value, ref_set, shadow, out);
}
if let Some(rest) = &op.rest {
self.collect_capture_names_binding_keys(&rest.argument, ref_set, shadow, out);
}
}
oxide_parser::BindingPattern::AssignmentPattern(ap) => {
self.collect_capture_names_binding_keys(&ap.left, ref_set, shadow, out);
}
}
}
/// 扫描赋值目标中的引用(标识符写名,成员目标扫对象/键,解构目标递归元素)。 /// 扫描赋值目标中的引用(标识符写名,成员目标扫对象/键,解构目标递归元素)。
fn collect_capture_names_assign_target( fn collect_capture_names_assign_target(
&self, target: &oxide_parser::AssignmentTarget, ref_set: &HashSet<String>, shadow: &HashSet<String>, &self, target: &oxide_parser::AssignmentTarget, ref_set: &HashSet<String>, shadow: &HashSet<String>,
@@ -868,11 +920,49 @@ impl Emitter {
fn collect_fn_default_captured( fn collect_fn_default_captured(
&self, params: &oxide_parser::FormalParameters, own: &HashSet<String>, out: &mut HashSet<String>, &self, params: &oxide_parser::FormalParameters, own: &HashSet<String>, out: &mut HashSet<String>,
) { ) {
let empty_shadow = HashSet::new();
for p in &params.items { for p in &params.items {
if let Some(init) = &p.initializer { if let Some(init) = &p.initializer {
self.collect_captured_expr(init, own, out); self.collect_capture_names_expr(init, own, &empty_shadow, out);
}
// 形参模式的运行时求值表达式(计算键/内嵌默认值)在子作用域求值,
// 其标识符引用须纳入父层 MAKE_CELL 判定。
self.collect_captured_pattern_runtime_exprs(&p.pattern, own, out);
}
}
/// 遍历绑定 pattern 内全部运行时求值表达式(计算键与 AssignmentPattern 默认值):
/// 这些表达式在子作用域(嵌套函数形参绑定时)求值,引用的父层绑定须建 cell 供
/// 子函数 upvalue 读取——与 `collect_capture_names_expr` 同口径(标识符比对)。
fn collect_captured_pattern_runtime_exprs(
&self, pattern: &oxide_parser::BindingPattern, own: &HashSet<String>, out: &mut HashSet<String>,
) {
let empty_shadow = HashSet::new();
match pattern {
oxide_parser::BindingPattern::BindingIdentifier(_) => {}
oxide_parser::BindingPattern::ArrayPattern(ap) => {
for p in ap.elements.iter().flatten() {
self.collect_captured_pattern_runtime_exprs(p, own, out);
}
if let Some(rest) = &ap.rest {
self.collect_captured_pattern_runtime_exprs(&rest.argument, own, out);
}
}
oxide_parser::BindingPattern::ObjectPattern(op) => {
for prop in &op.properties {
if prop.computed {
self.collect_capture_names_expr(prop.key.to_expression(), own, &empty_shadow, out);
}
self.collect_captured_pattern_runtime_exprs(&prop.value, own, out);
}
if let Some(rest) = &op.rest {
self.collect_captured_pattern_runtime_exprs(&rest.argument, own, out);
}
}
oxide_parser::BindingPattern::AssignmentPattern(ap) => {
self.collect_capture_names_expr(&ap.right, own, &empty_shadow, out);
self.collect_captured_pattern_runtime_exprs(&ap.left, own, out);
} }
self.collect_captured_binding_pattern(&p.pattern, own, out);
} }
} }
@@ -904,6 +994,8 @@ impl Emitter {
} }
Statement::VariableDeclaration(vd) => { Statement::VariableDeclaration(vd) => {
for d in &vd.declarations { for d in &vd.declarations {
// 绑定 pattern 的计算键是运行时求值表达式,其引用须纳入捕获判定。
self.collect_captured_binding_keys(&d.id, own, out);
if let Some(init) = &d.init { if let Some(init) = &d.init {
self.collect_captured_expr(init, own, out); self.collect_captured_expr(init, own, out);
} }
@@ -921,6 +1013,14 @@ impl Emitter {
if let Some(e) = init.as_expression() { if let Some(e) = init.as_expression() {
self.collect_captured_expr(e, own, out); self.collect_captured_expr(e, own, out);
} }
if let oxide_parser::ForStatementInit::VariableDeclaration(vd) = init {
for d in &vd.declarations {
self.collect_captured_binding_keys(&d.id, own, out);
if let Some(i) = &d.init {
self.collect_captured_expr(i, own, out);
}
}
}
} }
if let Some(t) = &fs.test { if let Some(t) = &fs.test {
self.collect_captured_expr(t, own, out); self.collect_captured_expr(t, own, out);
@@ -940,10 +1040,20 @@ impl Emitter {
} }
Statement::ForInStatement(fi) => { Statement::ForInStatement(fi) => {
self.collect_captured_expr(&fi.right, own, out); self.collect_captured_expr(&fi.right, own, out);
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
for d in &vd.declarations {
self.collect_captured_binding_keys(&d.id, own, out);
}
}
self.collect_captured_stmt(&fi.body, own, out); self.collect_captured_stmt(&fi.body, own, out);
} }
Statement::ForOfStatement(fo) => { Statement::ForOfStatement(fo) => {
self.collect_captured_expr(&fo.right, own, out); self.collect_captured_expr(&fo.right, own, out);
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
for d in &vd.declarations {
self.collect_captured_binding_keys(&d.id, own, out);
}
}
self.collect_captured_stmt(&fo.body, own, out); self.collect_captured_stmt(&fo.body, own, out);
} }
Statement::BlockStatement(b) => { Statement::BlockStatement(b) => {
@@ -1003,6 +1113,11 @@ impl Emitter {
} }
oxide_parser::BindingPattern::ObjectPattern(op) => { oxide_parser::BindingPattern::ObjectPattern(op) => {
for prop in &op.properties { for prop in &op.properties {
// 计算键表达式(本作用域 catch 参数求值,含 IIFE 等嵌套函数)引用
// 本函数绑定走寄存器,只须扫描键内嵌套函数表达式。
if prop.computed {
self.collect_captured_expr(prop.key.to_expression(), own, out);
}
self.collect_captured_binding_pattern(&prop.value, own, out); self.collect_captured_binding_pattern(&prop.value, own, out);
} }
if let Some(rest) = &op.rest { if let Some(rest) = &op.rest {
@@ -1016,6 +1131,38 @@ impl Emitter {
} }
} }
/// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)中的引用,供父层捕获判定
/// (声明语句/for 头的模式键与 catch/默认值路径同口径)。
fn collect_captured_binding_keys(
&self, pattern: &oxide_parser::BindingPattern, own: &HashSet<String>, out: &mut HashSet<String>,
) {
match pattern {
oxide_parser::BindingPattern::BindingIdentifier(_) => {}
oxide_parser::BindingPattern::ArrayPattern(ap) => {
for p in ap.elements.iter().flatten() {
self.collect_captured_binding_keys(p, own, out);
}
if let Some(rest) = &ap.rest {
self.collect_captured_binding_keys(&rest.argument, own, out);
}
}
oxide_parser::BindingPattern::ObjectPattern(op) => {
for prop in &op.properties {
if prop.computed {
self.collect_captured_expr(prop.key.to_expression(), own, out);
}
self.collect_captured_binding_keys(&prop.value, own, out);
}
if let Some(rest) = &op.rest {
self.collect_captured_binding_keys(&rest.argument, own, out);
}
}
oxide_parser::BindingPattern::AssignmentPattern(ap) => {
self.collect_captured_binding_keys(&ap.left, own, out);
}
}
}
fn collect_captured_expr(&self, expr: &Expression, own: &HashSet<String>, out: &mut HashSet<String>) { fn collect_captured_expr(&self, expr: &Expression, own: &HashSet<String>, out: &mut HashSet<String>) {
match expr { match expr {
Expression::FunctionExpression(fe) => { Expression::FunctionExpression(fe) => {
+6 -1
View File
@@ -788,7 +788,9 @@ impl Emitter {
Ok(()) Ok(())
} }
/// 遍历解构 pattern 收集内嵌默认值表达式(AssignmentPattern.right)。 /// 遍历解构 pattern 收集内嵌默认值表达式(AssignmentPattern.right)与
/// 计算键表达式(`{[k]: a}` 的键,运行时求值)——二者都会在参数绑定期被
/// 内部闭包引用,须一并纳入捕获分析。
fn collect_pattern_default_exprs<'a>( fn collect_pattern_default_exprs<'a>(
&self, pattern: &'a oxide_parser::BindingPattern<'a>, out: &mut Vec<&'a oxide_parser::Expression<'a>>, &self, pattern: &'a oxide_parser::BindingPattern<'a>, out: &mut Vec<&'a oxide_parser::Expression<'a>>,
) { ) {
@@ -808,6 +810,9 @@ impl Emitter {
} }
BindingPattern::ObjectPattern(op) => { BindingPattern::ObjectPattern(op) => {
for prop in &op.properties { for prop in &op.properties {
if prop.computed {
out.push(prop.key.to_expression());
}
self.collect_pattern_default_exprs(&prop.value, out); self.collect_pattern_default_exprs(&prop.value, out);
} }
if let Some(rest) = &op.rest { if let Some(rest) = &op.rest {
+47
View File
@@ -22,6 +22,7 @@ impl Emitter {
Statement::ExpressionStatement(es) => self.pre_scan_builtin_expr(&es.expression, ctx), Statement::ExpressionStatement(es) => self.pre_scan_builtin_expr(&es.expression, ctx),
Statement::VariableDeclaration(vd) => { Statement::VariableDeclaration(vd) => {
for d in &vd.declarations { for d in &vd.declarations {
self.pre_scan_builtin_pattern(&d.id, ctx);
if let Some(init) = &d.init { if let Some(init) = &d.init {
self.pre_scan_builtin_expr(init, ctx); self.pre_scan_builtin_expr(init, ctx);
} }
@@ -53,6 +54,7 @@ impl Emitter {
self.pre_scan_builtin_expr(e, ctx); self.pre_scan_builtin_expr(e, ctx);
} else if let oxide_parser::ForStatementInit::VariableDeclaration(decl) = init { } else if let oxide_parser::ForStatementInit::VariableDeclaration(decl) = init {
for d in &decl.declarations { for d in &decl.declarations {
self.pre_scan_builtin_pattern(&d.id, ctx);
if let Some(init_expr) = &d.init { if let Some(init_expr) = &d.init {
self.pre_scan_builtin_expr(init_expr, ctx); self.pre_scan_builtin_expr(init_expr, ctx);
} }
@@ -68,10 +70,20 @@ impl Emitter {
self.pre_scan_builtin_stmt(&fs.body, ctx); self.pre_scan_builtin_stmt(&fs.body, ctx);
} }
Statement::ForInStatement(fi) => { Statement::ForInStatement(fi) => {
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
for d in &vd.declarations {
self.pre_scan_builtin_pattern(&d.id, ctx);
}
}
self.pre_scan_builtin_expr(&fi.right, ctx); self.pre_scan_builtin_expr(&fi.right, ctx);
self.pre_scan_builtin_stmt(&fi.body, ctx); self.pre_scan_builtin_stmt(&fi.body, ctx);
} }
Statement::ForOfStatement(fo) => { Statement::ForOfStatement(fo) => {
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
for d in &vd.declarations {
self.pre_scan_builtin_pattern(&d.id, ctx);
}
}
self.pre_scan_builtin_expr(&fo.right, ctx); self.pre_scan_builtin_expr(&fo.right, ctx);
self.pre_scan_builtin_stmt(&fo.body, ctx); self.pre_scan_builtin_stmt(&fo.body, ctx);
} }
@@ -94,6 +106,9 @@ impl Emitter {
self.pre_scan_builtin_stmt(s, ctx); self.pre_scan_builtin_stmt(s, ctx);
} }
if let Some(handler) = &ts.handler { if let Some(handler) = &ts.handler {
if let Some(param) = &handler.param {
self.pre_scan_builtin_pattern(&param.pattern, ctx);
}
for s in &handler.body.body { for s in &handler.body.body {
self.pre_scan_builtin_stmt(s, ctx); self.pre_scan_builtin_stmt(s, ctx);
} }
@@ -122,6 +137,7 @@ impl Emitter {
Statement::ExportNamedDeclaration(exp) => { Statement::ExportNamedDeclaration(exp) => {
if let Some(Declaration::VariableDeclaration(vd)) = &exp.declaration { if let Some(Declaration::VariableDeclaration(vd)) = &exp.declaration {
for d in &vd.declarations { for d in &vd.declarations {
self.pre_scan_builtin_pattern(&d.id, ctx);
if let Some(init) = &d.init { if let Some(init) = &d.init {
self.pre_scan_builtin_expr(init, ctx); self.pre_scan_builtin_expr(init, ctx);
} }
@@ -297,6 +313,37 @@ impl Emitter {
} }
} }
/// 遍历绑定 pattern 的计算键表达式(`{[k]: a}` 的键)里的 builtin 引用:
/// 模式键是运行时求值表达式,键内 builtin 标识符须预先登记固定槽位
/// (与对象字面量计算键预扫描同口径)。
fn pre_scan_builtin_pattern(&self, pattern: &BindingPattern, ctx: &mut CompileCtx) {
match pattern {
BindingPattern::BindingIdentifier(_) => {}
BindingPattern::ArrayPattern(ap) => {
for e in ap.elements.iter().flatten() {
self.pre_scan_builtin_pattern(e, ctx);
}
if let Some(rest) = &ap.rest {
self.pre_scan_builtin_pattern(&rest.argument, ctx);
}
}
BindingPattern::ObjectPattern(op) => {
for prop in &op.properties {
if prop.computed {
self.pre_scan_builtin_expr(prop.key.to_expression(), ctx);
}
self.pre_scan_builtin_pattern(&prop.value, ctx);
}
if let Some(rest) = &op.rest {
self.pre_scan_builtin_pattern(&rest.argument, ctx);
}
}
BindingPattern::AssignmentPattern(ap) => {
self.pre_scan_builtin_pattern(&ap.left, ctx);
}
}
}
pub(crate) fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) { pub(crate) fn predeclare_function_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) {
for statement in statements { for statement in statements {
let function = match statement { let function = match statement {
+5 -7
View File
@@ -261,13 +261,11 @@ impl Emitter {
fn emit_property_key_expression(&self, key: &PropertyKey, ctx: &mut CompileCtx) -> Result<u32, String> { fn emit_property_key_expression(&self, key: &PropertyKey, ctx: &mut CompileCtx) -> Result<u32, String> {
match key { match key {
PropertyKey::Identifier(ident) => { // 标识符计算键(`{[k]: a}`)走通用表达式分发:经 emit_static_identifier_read
let name = ident.name.as_str(); // 正确解析 upvalue/被捕获 cell/with 动态与未声明标识符读(LOAD_GLOBAL 抛
let var_reg = ctx.lookup_or_builtin(name)?; // ReferenceError),与对象字面量/类字段计算键同口径;直接 lookup_or_builtin
let key_reg = ctx.alloc_reg(); // 只读局部符号表,嵌套函数引用外层绑定会静默回退读全局。
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(key_reg), Operand::Reg(var_reg), Operand::None)); PropertyKey::Identifier(_) => self.emit_expression(key.to_expression(), ctx),
Ok(key_reg)
}
PropertyKey::StringLiteral(s) => { PropertyKey::StringLiteral(s) => {
let key_idx = ctx.add_constant(Constant::String(s.value.to_string())); let key_idx = ctx.add_constant(Constant::String(s.value.to_string()));
let key_reg = ctx.alloc_reg(); let key_reg = ctx.alloc_reg();
@@ -250,3 +250,102 @@ fn computed_key_evaluated_before_default_and_getter() {
let s = unsafe { &*result.as_string_ptr() }.as_str().to_string(); let s = unsafe { &*result.as_string_ptr() }.as_str().to_string();
assert_eq!(s, "5:key,get"); assert_eq!(s, "5:key,get");
} }
// 跨作用域:绑定侧计算键引用外层绑定须走闭包捕获(修复前键引用落 LOAD_GLOBAL
// 兜底——读全局错值或 ReferenceError)。
#[test]
fn computed_key_binding_captures_outer_identifier_key() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function f(){ let k='x'; return function(){ const {[k]: a}={x:1}; return a; }; } f()()",
)
.unwrap();
assert_num(result, 1.0);
}
#[test]
fn computed_key_binding_captures_outer_const_key() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function f(){ const k='x'; return function(){ const {[k]: a}={x:2}; return a; }; } f()()",
)
.unwrap();
assert_num(result, 2.0);
}
#[test]
fn computed_key_binding_captures_outer_call_key() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function f(){ function k(){return 'x'} return function(){ const {[k()]: a}={x:1}; return a; }; } f()()",
)
.unwrap();
assert_num(result, 1.0);
}
#[test]
fn computed_key_binding_captures_outer_template_key() {
// 模板插值键引用外层绑定同样须捕获。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function f(){ let k='x'; return function(){ const {[`${k}`]: a}={x:3}; return a; }; } f()()",
)
.unwrap();
assert_num(result, 3.0);
}
#[test]
fn computed_key_binding_captures_outer_nested_pattern_key() {
// 嵌套解构的内层计算键引用外层绑定。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function f(){ function k(){return 'x'} return function(){ const {a: {[k()]: b}}={a:{x:4}}; return b; }; } f()()",
)
.unwrap();
assert_num(result, 4.0);
}
#[test]
fn computed_key_binding_captures_outer_in_catch_pattern() {
// catch 参数解构模式的计算键引用外层绑定。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function f(){ function k(){return 'x'} return function(){ try { throw {x:5}; } catch ({[k()]: a}) { return a; } }; } f()()",
)
.unwrap();
assert_num(result, 5.0);
}
#[test]
fn computed_key_binding_captures_outer_in_fn_default_pattern() {
// 函数形参解构默认值的计算键引用外层绑定。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function f(){ function k(){return 'x'} return function g({[k()]: a} = {x:6}) { return a; }; } f()()",
)
.unwrap();
assert_num(result, 6.0);
}
#[test]
fn computed_key_binding_same_scope_identifier_key() {
// 同作用域标识符键(无外层绑定)不回归,仍走局部符号表。
let mut vm = Vm::new();
let result = eval(&mut vm, "const key='x'; const {[key]: a}={x:9}; a").unwrap();
assert_num(result, 9.0);
}
#[test]
fn computed_key_binding_undeclared_key_throws_reference_error() {
// 未声明标识符计算键:读取抛 ReferenceError,而非静默读全局槽错值。
let mut vm = Vm::new();
let err = eval(&mut vm, "const {[undeclaredKey]: a} = {x:1};").unwrap_err();
assert!(err.contains("ReferenceError"), "expected ReferenceError, got {err:?}");
}