From 33dae0a8c21e0bc90a319b8d0736cf528fb88e44 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 11 Aug 2026 14:17:45 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=97=AD=E5=8C=85=E6=8D=95=E8=8E=B7?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E9=94=AE=E4=B8=8E=E5=BE=AA=E7=8E=AF=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/compile_stmt_tests.rs | 8 +++++++ crates/oxide_emit/src/closure.rs | 24 +++++++++++++++---- crates/oxide_emit/src/prepass.rs | 13 ++++++---- crates/oxide_emit/src/stmt/iteration/for_.rs | 2 ++ .../oxide_emit/src/stmt/iteration/for_in.rs | 9 ++++++- .../oxide_emit/src/stmt/iteration/for_of.rs | 11 ++++++++- crates/oxide_vm/tests/control_flow_tests.rs | 8 +++++++ crates/oxide_vm/tests/scoping_tests.rs | 12 ++++++++++ 8 files changed, 77 insertions(+), 10 deletions(-) diff --git a/crates/oxide_compiler/tests/compile_stmt_tests.rs b/crates/oxide_compiler/tests/compile_stmt_tests.rs index c8748247..8c0ed02d 100644 --- a/crates/oxide_compiler/tests/compile_stmt_tests.rs +++ b/crates/oxide_compiler/tests/compile_stmt_tests.rs @@ -405,6 +405,14 @@ fn compile_closure_capture_arrow_emits_make_cell() { ); } +#[test] +fn compile_adjacent_for_of_lexical_bindings_in_distinct_scopes() { + let module = compile_source( + "for (const [arg, descr] of [[1, 'a']]) { descr; } for (const [arg, descr] of [[2, 'b']]) { descr; }", + ); + assert!(!module.bytecode.is_empty()); +} + #[test] fn compile_compound_assignment_complement_ops() { let module = compile_source("let x = 10; x -= 1; x *= 2; x /= 3; x %= 4;"); diff --git a/crates/oxide_emit/src/closure.rs b/crates/oxide_emit/src/closure.rs index ab0a6e5c..b632b016 100644 --- a/crates/oxide_emit/src/closure.rs +++ b/crates/oxide_emit/src/closure.rs @@ -589,8 +589,16 @@ impl Emitter { } Expression::ObjectExpression(o) => { for prop in &o.properties { - if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop { - self.collect_capture_names_expr(&p.value, ref_set, shadow, out); + match prop { + oxide_parser::ObjectPropertyKind::ObjectProperty(p) => { + if p.computed { + self.collect_capture_names_expr(p.key.to_expression(), ref_set, shadow, out); + } + self.collect_capture_names_expr(&p.value, ref_set, shadow, out); + } + oxide_parser::ObjectPropertyKind::SpreadProperty(spread) => { + self.collect_capture_names_expr(&spread.argument, ref_set, shadow, out); + } } } } @@ -884,8 +892,16 @@ impl Emitter { } Expression::ObjectExpression(o) => { for prop in &o.properties { - if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop { - self.collect_captured_expr(&p.value, own, out); + match prop { + oxide_parser::ObjectPropertyKind::ObjectProperty(p) => { + if p.computed { + self.collect_captured_expr(p.key.to_expression(), own, out); + } + self.collect_captured_expr(&p.value, own, out); + } + oxide_parser::ObjectPropertyKind::SpreadProperty(spread) => { + self.collect_captured_expr(&spread.argument, own, out); + } } } } diff --git a/crates/oxide_emit/src/prepass.rs b/crates/oxide_emit/src/prepass.rs index c79a3840..6d1c696a 100644 --- a/crates/oxide_emit/src/prepass.rs +++ b/crates/oxide_emit/src/prepass.rs @@ -187,11 +187,16 @@ impl Emitter { } Expression::ObjectExpression(obj) => { for prop in &obj.properties { - if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop { - if p.computed { - self.pre_scan_builtin_expr(p.key.to_expression(), ctx); + match prop { + oxide_parser::ObjectPropertyKind::ObjectProperty(p) => { + if p.computed { + self.pre_scan_builtin_expr(p.key.to_expression(), ctx); + } + self.pre_scan_builtin_expr(&p.value, ctx); + } + oxide_parser::ObjectPropertyKind::SpreadProperty(spread) => { + self.pre_scan_builtin_expr(&spread.argument, ctx); } - self.pre_scan_builtin_expr(&p.value, ctx); } } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_.rs b/crates/oxide_emit/src/stmt/iteration/for_.rs index 1508718b..f10a6533 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_.rs @@ -36,6 +36,7 @@ impl Emitter { let Statement::ForStatement(fr) = stmt else { return Ok(None); }; + ctx.push_scope(); let start_label = ctx.next_label_id(); let update_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); @@ -135,6 +136,7 @@ impl Emitter { ctx.labels.set_label_pos(end_label, ctx.insts.len()); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); + ctx.pop_scope(); Ok(None) } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_in.rs b/crates/oxide_emit/src/stmt/iteration/for_in.rs index 51a7749b..d692ed3f 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_in.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_in.rs @@ -11,6 +11,7 @@ impl Emitter { let Statement::ForInStatement(fi) = stmt else { return Ok(None); }; + ctx.push_scope(); let start_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); let obj_reg = self.emit_expression(&fi.right, ctx)?; @@ -40,7 +41,12 @@ impl Emitter { ctx.declare(name, var_reg, decl.kind, is_const)?; if let Some(&cell_idx) = ctx.captured_bindings.get(name) { let op = if fresh_cell { OpCode::MAKE_CELL_FRESH } else { OpCode::MAKE_CELL }; - ctx.inst(Inst::new(op, Operand::Reg(key_reg), Operand::Imm(cell_idx as u16), Operand::None)); + ctx.inst(Inst::new( + op, + Operand::Reg(key_reg), + Operand::Imm(cell_idx as u16), + Operand::None, + )); } else { ctx.inst(Inst::new( OpCode::STORE_VAR, @@ -68,6 +74,7 @@ impl Emitter { ctx.inst(Inst::new(OpCode::FOR_IN_CLEANUP, Operand::None, Operand::None, Operand::None)); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); + ctx.pop_scope(); Ok(None) } } diff --git a/crates/oxide_emit/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs index 1801dbdb..5ec366cd 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_of.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs @@ -23,6 +23,7 @@ impl Emitter { } fn emit_sync_for_of_statement(&self, fo: &ForOfStatement, ctx: &mut CompileCtx) -> Result, String> { + ctx.push_scope(); let start_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); let iter_src_reg = self.emit_expression(&fo.right, ctx)?; @@ -42,6 +43,7 @@ impl Emitter { ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None)); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); + ctx.pop_scope(); Ok(None) } @@ -51,10 +53,16 @@ impl Emitter { /// 挂起(恢复值经 reg 0 交付)→ LOAD_VAR 拷贝为结果对象 → DONE 检查 done → /// FOR_OF_NEXT 读 value → 绑定左侧 → 循环体 → CLOSE 异步收尾。 fn emit_for_await_of_statement(&self, fo: &ForOfStatement, ctx: &mut CompileCtx) -> Result, String> { + ctx.push_scope(); let start_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); let iter_src_reg = self.emit_expression(&fo.right, ctx)?; - ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_INIT, Operand::None, Operand::Reg(iter_src_reg), Operand::None)); + ctx.inst(Inst::new( + OpCode::FOR_AWAIT_OF_INIT, + Operand::None, + Operand::Reg(iter_src_reg), + Operand::None, + )); ctx.labels.set_label_pos(start_label, ctx.insts.len()); ctx.push_loop(end_label, start_label); let n_labeled = ctx.take_pending_loop_labels(end_label, start_label); @@ -80,6 +88,7 @@ impl Emitter { ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_CLOSE, Operand::None, Operand::None, Operand::None)); ctx.pop_label_scopes(n_labeled); ctx.pop_loop(); + ctx.pop_scope(); Ok(None) } diff --git a/crates/oxide_vm/tests/control_flow_tests.rs b/crates/oxide_vm/tests/control_flow_tests.rs index 0761000e..f9c098b2 100644 --- a/crates/oxide_vm/tests/control_flow_tests.rs +++ b/crates/oxide_vm/tests/control_flow_tests.rs @@ -194,6 +194,14 @@ fn eval_for_in_let_scoping() { assert_eq!(result, "1"); } +#[test] +fn eval_adjacent_for_of_lexical_bindings_do_not_conflict() { + assert_eq!( + eval("let out=0; for (const [arg, descr] of [[1, 'a']]) out += arg; for (const [arg, descr] of [[2, 'b']]) out += arg; out"), + "3" + ); +} + #[test] fn eval_switch_basic_match() { assert_eq!(eval("var x=0;switch(2){case 1:x=10;case 2:x=20;break;default:x=30;}x"), "20"); diff --git a/crates/oxide_vm/tests/scoping_tests.rs b/crates/oxide_vm/tests/scoping_tests.rs index 7fc963c9..6b5d506a 100644 --- a/crates/oxide_vm/tests/scoping_tests.rs +++ b/crates/oxide_vm/tests/scoping_tests.rs @@ -92,6 +92,18 @@ fn closure_nested_expr_capture() { assert_eq!(result.as_double(), 2.0); } +#[test] +fn closure_captures_computed_object_key_across_caller_frame() { + let result = eval("let key='answer'; const call=fn=>fn(); const make=()=>({[key]:42}); call(make).answer").unwrap(); + assert_eq!(result.as_int(), 42); +} + +#[test] +fn closure_captures_object_spread_source_across_caller_frame() { + let result = eval("let source={x:7}; const call=fn=>fn(); const make=()=>({...source}); call(make).x").unwrap(); + assert_eq!(result.as_int(), 7); +} + #[test] fn for_let_per_iteration_independent() { let _ = eval("var fns=[]; for(let i=0;i<3;i++){ fns.push(function(){return i}); } fns[0]()+fns[1]()+fns[2]()");