From c5c4916a8f3746e4e06351793e710fcfd46a862b Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 16 Aug 2026 14:30:20 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BB=91=E5=AE=9A=E4=BE=A7=E8=A7=A3?= =?UTF-8?q?=E6=9E=84=E8=AE=A1=E7=AE=97=E9=94=AE=E5=8F=82=E4=B8=8E=E9=97=AD?= =?UTF-8?q?=E5=8C=85=E6=8D=95=E8=8E=B7=E9=81=8D=E5=8E=86=E9=98=B2=E9=9D=99?= =?UTF-8?q?=E9=BB=98=E9=94=99=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/closure.rs | 151 ++++++++++++++++++- crates/oxide_emit/src/emit.rs | 7 +- crates/oxide_emit/src/prepass.rs | 47 ++++++ crates/oxide_emit/src/shared/chain.rs | 12 +- crates/oxide_vm/tests/destructuring_tests.rs | 99 ++++++++++++ 5 files changed, 306 insertions(+), 10 deletions(-) diff --git a/crates/oxide_emit/src/closure.rs b/crates/oxide_emit/src/closure.rs index bce1a456..9ec7792c 100644 --- a/crates/oxide_emit/src/closure.rs +++ b/crates/oxide_emit/src/closure.rs @@ -306,6 +306,8 @@ impl Emitter { } Statement::VariableDeclaration(vd) => { for d in &vd.declarations { + // 绑定 pattern 的计算键是运行时求值表达式,其引用须纳入捕获。 + self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out); if let Some(init) = &d.init { self.collect_capture_names_expr(init, ref_set, shadow, out); } @@ -342,6 +344,7 @@ impl Emitter { } if let oxide_parser::ForStatementInit::VariableDeclaration(vd) = init { for d in &vd.declarations { + self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out); if let Some(i) = &d.init { self.collect_capture_names_expr(i, ref_set, shadow, out); } @@ -369,12 +372,22 @@ impl Emitter { // left 的 var 声明遮蔽外层同名绑定,body 内引用不视为捕获外层。 let mut for_shadow = shadow.clone(); 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); } Statement::ForOfStatement(fo) => { self.collect_capture_names_expr(&fo.right, ref_set, shadow, out); let mut for_shadow = shadow.clone(); 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); } Statement::BlockStatement(b) => self.collect_capture_names_shadowed(&b.body, ref_set, shadow, out), @@ -410,6 +423,7 @@ impl Emitter { match decl { oxide_parser::Declaration::VariableDeclaration(vd) => { for d in &vd.declarations { + self.collect_capture_names_binding_keys(&d.id, ref_set, shadow, out); if let Some(init) = &d.init { self.collect_capture_names_expr(init, ref_set, shadow, out); } @@ -526,6 +540,10 @@ impl Emitter { } 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_pattern(&prop.value, ref_set, shadow, out, catch_shadow); } 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, shadow: &HashSet, + out: &mut HashSet, + ) { + 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( &self, target: &oxide_parser::AssignmentTarget, ref_set: &HashSet, shadow: &HashSet, @@ -868,11 +920,49 @@ impl Emitter { fn collect_fn_default_captured( &self, params: &oxide_parser::FormalParameters, own: &HashSet, out: &mut HashSet, ) { + let empty_shadow = HashSet::new(); for p in ¶ms.items { 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, out: &mut HashSet, + ) { + 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) => { for d in &vd.declarations { + // 绑定 pattern 的计算键是运行时求值表达式,其引用须纳入捕获判定。 + self.collect_captured_binding_keys(&d.id, own, out); if let Some(init) = &d.init { self.collect_captured_expr(init, own, out); } @@ -921,6 +1013,14 @@ impl Emitter { if let Some(e) = init.as_expression() { 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 { self.collect_captured_expr(t, own, out); @@ -940,10 +1040,20 @@ impl Emitter { } Statement::ForInStatement(fi) => { 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); } Statement::ForOfStatement(fo) => { 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); } Statement::BlockStatement(b) => { @@ -1003,6 +1113,11 @@ impl Emitter { } oxide_parser::BindingPattern::ObjectPattern(op) => { 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); } 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, out: &mut HashSet, + ) { + 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, out: &mut HashSet) { match expr { Expression::FunctionExpression(fe) => { diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs index b872fb42..d11d1c20 100644 --- a/crates/oxide_emit/src/emit.rs +++ b/crates/oxide_emit/src/emit.rs @@ -788,7 +788,9 @@ impl Emitter { Ok(()) } - /// 遍历解构 pattern 收集内嵌默认值表达式(AssignmentPattern.right)。 + /// 遍历解构 pattern 收集内嵌默认值表达式(AssignmentPattern.right)与 + /// 计算键表达式(`{[k]: a}` 的键,运行时求值)——二者都会在参数绑定期被 + /// 内部闭包引用,须一并纳入捕获分析。 fn collect_pattern_default_exprs<'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) => { for prop in &op.properties { + if prop.computed { + out.push(prop.key.to_expression()); + } self.collect_pattern_default_exprs(&prop.value, out); } if let Some(rest) = &op.rest { diff --git a/crates/oxide_emit/src/prepass.rs b/crates/oxide_emit/src/prepass.rs index fb65cb70..945ce405 100644 --- a/crates/oxide_emit/src/prepass.rs +++ b/crates/oxide_emit/src/prepass.rs @@ -22,6 +22,7 @@ impl Emitter { Statement::ExpressionStatement(es) => self.pre_scan_builtin_expr(&es.expression, ctx), Statement::VariableDeclaration(vd) => { for d in &vd.declarations { + self.pre_scan_builtin_pattern(&d.id, ctx); if let Some(init) = &d.init { self.pre_scan_builtin_expr(init, ctx); } @@ -53,6 +54,7 @@ impl Emitter { self.pre_scan_builtin_expr(e, ctx); } else if let oxide_parser::ForStatementInit::VariableDeclaration(decl) = init { for d in &decl.declarations { + self.pre_scan_builtin_pattern(&d.id, ctx); if let Some(init_expr) = &d.init { self.pre_scan_builtin_expr(init_expr, ctx); } @@ -68,10 +70,20 @@ impl Emitter { self.pre_scan_builtin_stmt(&fs.body, ctx); } 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_stmt(&fi.body, ctx); } 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_stmt(&fo.body, ctx); } @@ -94,6 +106,9 @@ impl Emitter { self.pre_scan_builtin_stmt(s, ctx); } if let Some(handler) = &ts.handler { + if let Some(param) = &handler.param { + self.pre_scan_builtin_pattern(¶m.pattern, ctx); + } for s in &handler.body.body { self.pre_scan_builtin_stmt(s, ctx); } @@ -122,6 +137,7 @@ impl Emitter { Statement::ExportNamedDeclaration(exp) => { if let Some(Declaration::VariableDeclaration(vd)) = &exp.declaration { for d in &vd.declarations { + self.pre_scan_builtin_pattern(&d.id, ctx); if let Some(init) = &d.init { 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) { for statement in statements { let function = match statement { diff --git a/crates/oxide_emit/src/shared/chain.rs b/crates/oxide_emit/src/shared/chain.rs index fd33b57b..2eb4e786 100644 --- a/crates/oxide_emit/src/shared/chain.rs +++ b/crates/oxide_emit/src/shared/chain.rs @@ -261,13 +261,11 @@ impl Emitter { fn emit_property_key_expression(&self, key: &PropertyKey, ctx: &mut CompileCtx) -> Result { match key { - PropertyKey::Identifier(ident) => { - let name = ident.name.as_str(); - let var_reg = ctx.lookup_or_builtin(name)?; - let key_reg = ctx.alloc_reg(); - ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(key_reg), Operand::Reg(var_reg), Operand::None)); - Ok(key_reg) - } + // 标识符计算键(`{[k]: a}`)走通用表达式分发:经 emit_static_identifier_read + // 正确解析 upvalue/被捕获 cell/with 动态与未声明标识符读(LOAD_GLOBAL 抛 + // ReferenceError),与对象字面量/类字段计算键同口径;直接 lookup_or_builtin + // 只读局部符号表,嵌套函数引用外层绑定会静默回退读全局。 + PropertyKey::Identifier(_) => self.emit_expression(key.to_expression(), ctx), PropertyKey::StringLiteral(s) => { let key_idx = ctx.add_constant(Constant::String(s.value.to_string())); let key_reg = ctx.alloc_reg(); diff --git a/crates/oxide_vm/tests/destructuring_tests.rs b/crates/oxide_vm/tests/destructuring_tests.rs index d70c880a..aa5652f5 100644 --- a/crates/oxide_vm/tests/destructuring_tests.rs +++ b/crates/oxide_vm/tests/destructuring_tests.rs @@ -250,3 +250,102 @@ fn computed_key_evaluated_before_default_and_getter() { let s = unsafe { &*result.as_string_ptr() }.as_str().to_string(); 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:?}"); +}