feat: 循环 let/const 每迭代绑定(MAKE_CELL_FRESH + 解构捕获修复)

This commit is contained in:
2026-08-10 10:18:39 +08:00
parent e03ec0f635
commit 80cb1c2feb
17 changed files with 320 additions and 46 deletions
@@ -22,7 +22,7 @@ impl Emitter {
}
if let Some(init) = &d.init {
let val_reg = self.emit_expression(init, ctx)?;
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, ctx)?;
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, false, ctx)?;
if let BindingPattern::BindingIdentifier(bi) = &d.id {
if crate::is_anonymous_function_definition(init) {
if let Some(sub_mod) = ctx.nested.last_mut() {
@@ -56,7 +56,7 @@ impl Emitter {
let src_reg = ctx.alloc_reg();
// 异常值在 VM 物理 regs[0](unwind 展开处写入),STORE_VAR a=None 读回。
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(src_reg), Operand::None, Operand::None));
self.emit_binding_pattern(&param.pattern, src_reg, VariableDeclarationKind::Let, false, ctx)?;
self.emit_binding_pattern(&param.pattern, src_reg, VariableDeclarationKind::Let, false, false, ctx)?;
}
let mut last_catch_result: Option<u32> = None;
for s in &catch.body.body {
+71 -1
View File
@@ -8,6 +8,30 @@ use oxide_ir::operand::Operand;
use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind};
impl Emitter {
/// 递归收集解构 pattern 内全部绑定标识符名(用于循环头声明被捕获时的 fresh 判定)。
fn collect_pattern_binding_names(&self, pattern: &BindingPattern, out: &mut Vec<String>) {
match pattern {
BindingPattern::BindingIdentifier(bi) => out.push(bi.name.as_str().to_string()),
BindingPattern::ArrayPattern(ap) => {
for p in ap.elements.iter().flatten() {
self.collect_pattern_binding_names(p, out);
}
if let Some(rest) = &ap.rest {
self.collect_pattern_binding_names(&rest.argument, out);
}
}
BindingPattern::ObjectPattern(op) => {
for prop in &op.properties {
self.collect_pattern_binding_names(&prop.value, out);
}
if let Some(rest) = &op.rest {
self.collect_pattern_binding_names(&rest.argument, out);
}
}
BindingPattern::AssignmentPattern(ap) => self.collect_pattern_binding_names(&ap.left, out),
}
}
pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
let Statement::ForStatement(fr) = stmt else {
return Ok(None);
@@ -17,6 +41,8 @@ impl Emitter {
let end_label = ctx.next_label_id();
ctx.push_loop(end_label, update_label);
let n_labeled = ctx.take_pending_loop_labels(end_label, update_label);
// 循环头声明中被嵌套函数捕获的 let/const 绑定:每迭代 fresh cell。
let mut fresh_bindings: Vec<(String, u8)> = Vec::new();
if let Some(init) = &fr.init {
if let Some(expr) = init.as_expression() {
self.emit_expression(expr, ctx)?;
@@ -25,7 +51,23 @@ impl Emitter {
let is_const = matches!(decl.kind, VariableDeclarationKind::Const);
if let Some(init_expr) = &d.init {
let val_reg = self.emit_expression(init_expr, ctx)?;
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, ctx)?;
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, false, ctx)?;
// 被捕获绑定:fresh 从寄存器读当前值,须把 init 值写入寄存器槽。
// emit_binding_pattern 对捕获绑定只建 cell,不写寄存器。
let mut names = Vec::new();
self.collect_pattern_binding_names(&d.id, &mut names);
for name in &names {
if ctx.captured_bindings.contains_key(name) {
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
ctx.inst(Inst::new(
OpCode::STORE_VAR,
Operand::Reg(reg),
Operand::Reg(val_reg),
Operand::None,
));
}
}
}
} else if let BindingPattern::BindingIdentifier(bi) = &d.id {
let idx = ctx.add_constant(Constant::Undefined);
let tmp = ctx.alloc_reg();
@@ -48,10 +90,32 @@ impl Emitter {
));
ctx.init_var(bi.name.as_str());
}
// 记录被捕获的 let/const 声明(解构 pattern 递归收集绑定名)。
if !matches!(decl.kind, VariableDeclarationKind::Var) {
let mut names = Vec::new();
self.collect_pattern_binding_names(&d.id, &mut names);
for name in &names {
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
fresh_bindings.push((name.clone(), cell_idx));
}
}
}
}
}
}
ctx.labels.set_label_pos(start_label, ctx.insts.len());
// 每迭代 fresh cell:被捕获的 let/const 循环变量在迭代开始拷贝当前值到新 cell,
// 本迭代闭包捕获新 cell(规范 CreatePerIterationEnvironment)。
// 值源是循环变量的寄存器(update 段写寄存器而非 cell),首迭代即 init 值。
for (name, cell_idx) in &fresh_bindings {
let reg = ctx.scopes.symbols.lookup_any(name).expect("declared loop binding");
ctx.inst(Inst::new(
OpCode::MAKE_CELL_FRESH,
Operand::Reg(reg),
Operand::Imm(*cell_idx as u16),
Operand::None,
));
}
if let Some(test) = &fr.test {
let test_reg = self.emit_expression(test, ctx)?;
ctx.inst(Inst::jmp_if_false(test_reg, end_label));
@@ -59,7 +123,13 @@ impl Emitter {
self.emit_statement(&fr.body, ctx)?;
ctx.labels.set_label_pos(update_label, ctx.insts.len());
if let Some(update) = &fr.update {
// 被捕获的 let/const 循环变量在 update 段写寄存器(而非 cell),
// 供下一迭代 fresh 拷贝——否则 CELL_SET 会污染本迭代闭包捕获的 cell。
let prev = std::mem::take(&mut ctx.register_update_names);
let names: Vec<String> = fresh_bindings.iter().map(|(n, _)| n.clone()).collect();
ctx.register_update_names = names;
self.emit_expression(update, ctx)?;
ctx.register_update_names = prev;
}
ctx.inst(Inst::jmp(start_label));
ctx.labels.set_label_pos(end_label, ctx.insts.len());
+22 -7
View File
@@ -29,15 +29,30 @@ impl Emitter {
ctx.inst(Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(key_reg), Operand::None, Operand::None));
match &fi.left {
ForStatementLeft::VariableDeclaration(decl) => {
// let/const 声明对被捕获绑定用 fresh cell(每迭代新 cell);var 单绑定。
let fresh_cell = !matches!(decl.kind, VariableDeclarationKind::Var);
for d in &decl.declarations {
let name = match &d.id {
oxide_parser::BindingPattern::BindingIdentifier(bi) => bi.name.as_str(),
match &d.id {
oxide_parser::BindingPattern::BindingIdentifier(bi) => {
let is_const = matches!(decl.kind, VariableDeclarationKind::Const);
let name = bi.name.as_str();
let var_reg = ctx.alloc_reg();
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));
} else {
ctx.inst(Inst::new(
OpCode::STORE_VAR,
Operand::Reg(var_reg),
Operand::Reg(key_reg),
Operand::None,
));
}
ctx.init_var(name);
}
_ => return Err("destructuring not supported".into()),
};
let var_reg = ctx.alloc_reg();
ctx.declare(name, var_reg, decl.kind, matches!(decl.kind, VariableDeclarationKind::Const))?;
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(key_reg), Operand::None));
ctx.init_var(name);
}
}
}
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
@@ -9,7 +9,7 @@ use crate::{CompileCtx, Emitter};
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
use oxide_parser::{ForOfStatement, ForStatementLeft, Statement};
use oxide_parser::{ForOfStatement, ForStatementLeft, Statement, VariableDeclarationKind};
impl Emitter {
pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
@@ -84,13 +84,16 @@ impl Emitter {
}
/// for-of/for-await-of 左侧绑定:把当前迭代值 `val_reg` 写入声明/赋值目标。
/// let/const 声明对被捕获绑定用 fresh cell(每迭代新 cell,规范 per-iteration 绑定);
/// var 保持单绑定。
fn emit_for_of_left_assignment(
&self, left: &ForStatementLeft, val_reg: u32, ctx: &mut CompileCtx,
) -> Result<(), String> {
match left {
ForStatementLeft::VariableDeclaration(decl) => {
let fresh_cell = !matches!(decl.kind, VariableDeclarationKind::Var);
for d in &decl.declarations {
self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, ctx)?;
self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, fresh_cell, ctx)?;
}
}
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {