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
+3
View File
@@ -217,6 +217,9 @@ define_opcodes! {
FOR_AWAIT_OF_NEXT = 0x7B => "FOR_AWAIT_OF_NEXT", FOR_AWAIT_OF_NEXT = 0x7B => "FOR_AWAIT_OF_NEXT",
FOR_AWAIT_OF_DONE = 0x7C => "FOR_AWAIT_OF_DONE", FOR_AWAIT_OF_DONE = 0x7C => "FOR_AWAIT_OF_DONE",
FOR_AWAIT_OF_CLOSE = 0x7D => "FOR_AWAIT_OF_CLOSE", FOR_AWAIT_OF_CLOSE = 0x7D => "FOR_AWAIT_OF_CLOSE",
// 无条件新建 cell 并替换 cell_stack[cell_idx],值为 regs[rd]——循环每迭代绑定
// 把当前值拷入新 cell,本迭代闭包捕获新 cell(旧闭包仍指向旧 cell)。
MAKE_CELL_FRESH = 0x7E => "MAKE_CELL_FRESH",
// ── 位运算 (0x80-0x8F) ── // ── 位运算 (0x80-0x8F) ──
BIT_AND = 0x80 => "BIT_AND", BIT_AND = 0x80 => "BIT_AND",
+34 -18
View File
@@ -28,13 +28,39 @@ impl Emitter {
names names
} }
/// 递归收集 binding pattern 内全部绑定标识符名(解构 `[a, b]` / `{x: y}` 嵌套)。
fn collect_binding_pattern_names(&self, pattern: &oxide_parser::BindingPattern, out: &mut HashSet<String>) {
match pattern {
oxide_parser::BindingPattern::BindingIdentifier(bi) => {
out.insert(bi.name.to_string());
}
oxide_parser::BindingPattern::ArrayPattern(ap) => {
for p in ap.elements.iter().flatten() {
self.collect_binding_pattern_names(p, out);
}
if let Some(rest) = &ap.rest {
self.collect_binding_pattern_names(&rest.argument, out);
}
}
oxide_parser::BindingPattern::ObjectPattern(op) => {
for prop in &op.properties {
self.collect_binding_pattern_names(&prop.value, out);
}
if let Some(rest) = &op.rest {
self.collect_binding_pattern_names(&rest.argument, out);
}
}
oxide_parser::BindingPattern::AssignmentPattern(ap) => {
self.collect_binding_pattern_names(&ap.left, out);
}
}
}
/// 收集 for-in/for-of 头部声明(`var x` / 解构 pattern)绑定的名字。 /// 收集 for-in/for-of 头部声明(`var x` / 解构 pattern)绑定的名字。
fn collect_for_left_decl_names(&self, left: &oxide_parser::ForStatementLeft, out: &mut HashSet<String>) { fn collect_for_left_decl_names(&self, left: &oxide_parser::ForStatementLeft, out: &mut HashSet<String>) {
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = left { if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = left {
for d in &vd.declarations { for d in &vd.declarations {
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id { self.collect_binding_pattern_names(&d.id, out);
out.insert(bi.name.to_string());
}
} }
} }
} }
@@ -54,9 +80,7 @@ impl Emitter {
match stmt { match stmt {
Statement::VariableDeclaration(vd) => { Statement::VariableDeclaration(vd) => {
for d in &vd.declarations { for d in &vd.declarations {
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id { self.collect_binding_pattern_names(&d.id, out);
out.insert(bi.name.to_string());
}
} }
} }
Statement::FunctionDeclaration(fd) => { Statement::FunctionDeclaration(fd) => {
@@ -86,22 +110,14 @@ impl Emitter {
// for-in / for-of 头部的 var 声明是本函数局部绑定(遮蔽外层同名), // for-in / for-of 头部的 var 声明是本函数局部绑定(遮蔽外层同名),
// 须计入 own_bindings,否则闭包捕获分析会误判为捕获外层变量。 // 须计入 own_bindings,否则闭包捕获分析会误判为捕获外层变量。
Statement::ForInStatement(fi) => { Statement::ForInStatement(fi) => {
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left { if let oxide_parser::ForStatementLeft::VariableDeclaration(_) = &fi.left {
for d in &vd.declarations { self.collect_for_left_decl_names(&fi.left, out);
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
out.insert(bi.name.to_string());
}
}
} }
self.collect_decl_names_stmt(std::slice::from_ref(&fi.body), out); self.collect_decl_names_stmt(std::slice::from_ref(&fi.body), out);
} }
Statement::ForOfStatement(fo) => { Statement::ForOfStatement(fo) => {
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left { if let oxide_parser::ForStatementLeft::VariableDeclaration(_) = &fo.left {
for d in &vd.declarations { self.collect_for_left_decl_names(&fo.left, out);
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
out.insert(bi.name.to_string());
}
}
} }
self.collect_decl_names_stmt(std::slice::from_ref(&fo.body), out); self.collect_decl_names_stmt(std::slice::from_ref(&fo.body), out);
} }
+6 -1
View File
@@ -194,6 +194,10 @@ pub struct CompileCtx {
/// try_stack 组成。每项标记是否为纯 catch handler(TRY_BEGIN,由 TRY_END /// try_stack 组成。每项标记是否为纯 catch handler(TRY_BEGIN,由 TRY_END
/// 弹出):return 逃出 try 域时据此弹出栈顶连续纯 catch,防 handler 泄漏。 /// 弹出):return 逃出 try 域时据此弹出栈顶连续纯 catch,防 handler 泄漏。
pub(crate) open_try_handlers: Vec<bool>, pub(crate) open_try_handlers: Vec<bool>,
/// 循环 update 段中应走寄存器(而非 cell)的被捕获绑定名:C 风格 for 的
/// let/const 循环变量每迭代 fresh cell,update 写寄存器(不污染本迭代闭包
/// 捕获的 cell),下一迭代 fresh 从寄存器拷入新 cell。
pub(crate) register_update_names: Vec<String>,
} }
/// 函数体编译上下文:决定 `this`/`super` 绑定与参数前导(prologue)形态。 /// 函数体编译上下文:决定 `this`/`super` 绑定与参数前导(prologue)形态。
@@ -289,6 +293,7 @@ impl CompileCtx {
const_overflow: false, const_overflow: false,
with_stack: Vec::new(), with_stack: Vec::new(),
open_try_handlers: Vec::new(), open_try_handlers: Vec::new(),
register_update_names: Vec::new(),
} }
} }
@@ -1117,7 +1122,7 @@ impl Emitter {
} else { } else {
src_reg src_reg
}; };
self.emit_binding_pattern(pattern, src_reg, VariableDeclarationKind::Var, false, ctx)?; self.emit_binding_pattern(pattern, src_reg, VariableDeclarationKind::Var, false, false, ctx)?;
} }
ParamSpec::Identifier { name, initializer } => { ParamSpec::Identifier { name, initializer } => {
if let Some(init) = initializer { if let Some(init) = initializer {
+8
View File
@@ -18,6 +18,14 @@ impl Emitter {
fn emit_compound_identifier_static( fn emit_compound_identifier_static(
&self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx, &self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx,
) -> Result<u32, String> { ) -> Result<u32, String> {
// 循环 update 段:被捕获绑定走寄存器 RMW(C 风格 for 每迭代 fresh,
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
if ctx.register_update_names.iter().any(|n| n == name) {
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(rhs), Operand::None));
return Ok(reg);
}
}
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
let captured_cell = ctx.captured_bindings.get(name).copied(); let captured_cell = ctx.captured_bindings.get(name).copied();
if let Some(uv) = uv_idx { if let Some(uv) = uv_idx {
+13
View File
@@ -112,6 +112,19 @@ impl Emitter {
/// - `const_flag` 为 1 时走 STORE_VAR 运行时 const guard(对 const 再赋值报错)。 /// - `const_flag` 为 1 时走 STORE_VAR 运行时 const guard(对 const 再赋值报错)。
/// - upvalue 与 cell 写穿共享单元,不设 guard。 /// - upvalue 与 cell 写穿共享单元,不设 guard。
pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) { pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
// 循环 update 段:被捕获绑定走寄存器而非 cell(C 风格 for 每迭代 fresh,
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
if ctx.register_update_names.iter().any(|n| n == 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::Imm(const_flag),
));
return;
}
}
// 目标若是 upvalue 引用,走 STORE_UPVALUE // 目标若是 upvalue 引用,走 STORE_UPVALUE
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) { if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
ctx.inst(Inst::new( ctx.inst(Inst::new(
@@ -418,6 +418,21 @@ impl Emitter {
) -> Result<u32, String> { ) -> Result<u32, String> {
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
let captured_cell = ctx.captured_bindings.get(name).copied(); let captured_cell = ctx.captured_bindings.get(name).copied();
// 循环 update 段:被捕获绑定走寄存器 INC/DEC(C 风格 for 每迭代 fresh,
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
if ctx.register_update_names.iter().any(|n| n == name) {
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
let result_reg = ctx.alloc_reg();
let op = match (update.operator, update.prefix) {
(UpdateOperator::Increment, true) => OpCode::INC_PRE,
(UpdateOperator::Increment, false) => OpCode::INC_POST,
(UpdateOperator::Decrement, true) => OpCode::DEC_PRE,
(UpdateOperator::Decrement, false) => OpCode::DEC_POST,
};
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(result_reg), Operand::Reg(result_reg)));
return Ok(result_reg);
}
}
if let Some(uv) = uv_idx { if let Some(uv) = uv_idx {
// upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE; // upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE;
// 后缀形式返回旧值(前缀返回新值)。 // 后缀形式返回旧值(前缀返回新值)。
+19 -13
View File
@@ -48,8 +48,11 @@ impl Emitter {
Ok(val_reg) Ok(val_reg)
} }
/// 绑定单个标识符到槽位。`fresh_cell` 为 true 时对被捕获绑定用 MAKE_CELL_FRESH
/// (循环每迭代绑定:无条件新建 cell,本迭代闭包捕获新 cell)。
fn emit_bind_target( fn emit_bind_target(
&self, name: &str, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, ctx: &mut CompileCtx, &self, name: &str, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, fresh_cell: bool,
ctx: &mut CompileCtx,
) -> Result<(), String> { ) -> Result<(), String> {
let target_reg = if matches!(kind, VariableDeclarationKind::Var) { let target_reg = if matches!(kind, VariableDeclarationKind::Var) {
// `var` 名已预声明(hoisting),复用预登记槽位而非新分配,使 n_registers 与 let/const 一致。 // `var` 名已预声明(hoisting),复用预登记槽位而非新分配,使 n_registers 与 let/const 一致。
@@ -66,8 +69,9 @@ impl Emitter {
var_reg var_reg
}; };
if let Some(&cell_idx) = ctx.captured_bindings.get(name) { 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( ctx.inst(Inst::new(
OpCode::MAKE_CELL, op,
Operand::Reg(src_reg), Operand::Reg(src_reg),
Operand::Imm(cell_idx as u16), Operand::Imm(cell_idx as u16),
Operand::None, Operand::None,
@@ -119,14 +123,14 @@ impl Emitter {
pub(crate) fn emit_binding_pattern( pub(crate) fn emit_binding_pattern(
&self, pattern: &BindingPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, &self, pattern: &BindingPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool,
ctx: &mut CompileCtx, fresh_cell: bool, ctx: &mut CompileCtx,
) -> Result<(), String> { ) -> Result<(), String> {
match pattern { match pattern {
BindingPattern::BindingIdentifier(bi) => { BindingPattern::BindingIdentifier(bi) => {
self.emit_bind_target(bi.name.as_str(), src_reg, kind, is_const, ctx) self.emit_bind_target(bi.name.as_str(), src_reg, kind, is_const, fresh_cell, ctx)
} }
BindingPattern::ArrayPattern(ap) => self.emit_array_binding(ap, src_reg, kind, is_const, ctx), BindingPattern::ArrayPattern(ap) => self.emit_array_binding(ap, src_reg, kind, is_const, fresh_cell, ctx),
BindingPattern::ObjectPattern(op) => self.emit_object_binding(op, src_reg, kind, is_const, ctx), BindingPattern::ObjectPattern(op) => self.emit_object_binding(op, src_reg, kind, is_const, fresh_cell, ctx),
BindingPattern::AssignmentPattern(ap) => { BindingPattern::AssignmentPattern(ap) => {
// `[x = fn]`:默认值函数按绑定名推断 name。 // `[x = fn]`:默认值函数按绑定名推断 name。
let name = match &ap.left { let name = match &ap.left {
@@ -134,13 +138,14 @@ impl Emitter {
_ => None, _ => None,
}; };
let val_reg = self.emit_default_if_undefined(src_reg, &ap.right, name, ctx)?; let val_reg = self.emit_default_if_undefined(src_reg, &ap.right, name, ctx)?;
self.emit_binding_pattern(&ap.left, val_reg, kind, is_const, ctx) self.emit_binding_pattern(&ap.left, val_reg, kind, is_const, fresh_cell, ctx)
} }
} }
} }
fn emit_array_binding( fn emit_array_binding(
&self, ap: &ArrayPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, ctx: &mut CompileCtx, &self, ap: &ArrayPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, fresh_cell: bool,
ctx: &mut CompileCtx,
) -> Result<(), String> { ) -> Result<(), String> {
ctx.inst(Inst::new(OpCode::FOR_OF_INIT, Operand::None, Operand::Reg(src_reg), Operand::None)); ctx.inst(Inst::new(OpCode::FOR_OF_INIT, Operand::None, Operand::Reg(src_reg), Operand::None));
for elem in &ap.elements { for elem in &ap.elements {
@@ -149,12 +154,12 @@ impl Emitter {
let val_reg = ctx.alloc_reg(); let val_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None)); ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
if let Some(pattern) = elem { if let Some(pattern) = elem {
self.emit_binding_pattern(pattern, val_reg, kind, is_const, ctx)?; self.emit_binding_pattern(pattern, val_reg, kind, is_const, fresh_cell, ctx)?;
} }
} }
if let Some(rest) = &ap.rest { if let Some(rest) = &ap.rest {
let rest_reg = self.emit_collect_rest_array(ctx)?; let rest_reg = self.emit_collect_rest_array(ctx)?;
self.emit_binding_pattern(&rest.argument, rest_reg, kind, is_const, ctx)?; self.emit_binding_pattern(&rest.argument, rest_reg, kind, is_const, fresh_cell, ctx)?;
} }
ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None)); ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None));
Ok(()) Ok(())
@@ -193,7 +198,8 @@ impl Emitter {
} }
fn emit_object_binding( fn emit_object_binding(
&self, op: &ObjectPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, ctx: &mut CompileCtx, &self, op: &ObjectPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool, fresh_cell: bool,
ctx: &mut CompileCtx,
) -> Result<(), String> { ) -> Result<(), String> {
// 对象解构先 ToObject(rhs):null/undefined 抛 TypeError(含空 pattern), // 对象解构先 ToObject(rhs):null/undefined 抛 TypeError(含空 pattern),
// 原始值包装为对应对象,保证属性读取与 rest 都以对象为源。 // 原始值包装为对应对象,保证属性读取与 rest 都以对象为源。
@@ -221,7 +227,7 @@ impl Emitter {
self.emit_push_excluded_key(arr, Some(key_reg), "", excl_push, ctx); self.emit_push_excluded_key(arr, Some(key_reg), "", excl_push, ctx);
excl_push += 1; excl_push += 1;
} }
self.emit_binding_pattern(&prop.value, prop_reg, kind, is_const, ctx)?; self.emit_binding_pattern(&prop.value, prop_reg, kind, is_const, fresh_cell, ctx)?;
} }
if let Some(rest) = &op.rest { if let Some(rest) = &op.rest {
let rest_reg = ctx.alloc_reg(); let rest_reg = ctx.alloc_reg();
@@ -232,7 +238,7 @@ impl Emitter {
excluded_idx as u32, excluded_idx as u32,
excl_arr_reg.map(Operand::Reg), excl_arr_reg.map(Operand::Reg),
)); ));
self.emit_binding_pattern(&rest.argument, rest_reg, kind, is_const, ctx)?; self.emit_binding_pattern(&rest.argument, rest_reg, kind, is_const, fresh_cell, ctx)?;
} }
Ok(()) Ok(())
} }
@@ -22,7 +22,7 @@ impl Emitter {
} }
if let Some(init) = &d.init { if let Some(init) = &d.init {
let val_reg = self.emit_expression(init, ctx)?; 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 let BindingPattern::BindingIdentifier(bi) = &d.id {
if crate::is_anonymous_function_definition(init) { if crate::is_anonymous_function_definition(init) {
if let Some(sub_mod) = ctx.nested.last_mut() { if let Some(sub_mod) = ctx.nested.last_mut() {
@@ -56,7 +56,7 @@ impl Emitter {
let src_reg = ctx.alloc_reg(); let src_reg = ctx.alloc_reg();
// 异常值在 VM 物理 regs[0](unwind 展开处写入),STORE_VAR a=None 读回。 // 异常值在 VM 物理 regs[0](unwind 展开处写入),STORE_VAR a=None 读回。
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(src_reg), Operand::None, Operand::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; let mut last_catch_result: Option<u32> = None;
for s in &catch.body.body { 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}; use oxide_parser::{BindingPattern, ForStatementInit, Statement, VariableDeclarationKind};
impl Emitter { 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> { pub(crate) fn emit_for_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> {
let Statement::ForStatement(fr) = stmt else { let Statement::ForStatement(fr) = stmt else {
return Ok(None); return Ok(None);
@@ -17,6 +41,8 @@ impl Emitter {
let end_label = ctx.next_label_id(); let end_label = ctx.next_label_id();
ctx.push_loop(end_label, update_label); ctx.push_loop(end_label, update_label);
let n_labeled = ctx.take_pending_loop_labels(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(init) = &fr.init {
if let Some(expr) = init.as_expression() { if let Some(expr) = init.as_expression() {
self.emit_expression(expr, ctx)?; self.emit_expression(expr, ctx)?;
@@ -25,7 +51,23 @@ impl Emitter {
let is_const = matches!(decl.kind, VariableDeclarationKind::Const); let is_const = matches!(decl.kind, VariableDeclarationKind::Const);
if let Some(init_expr) = &d.init { if let Some(init_expr) = &d.init {
let val_reg = self.emit_expression(init_expr, ctx)?; 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 { } else if let BindingPattern::BindingIdentifier(bi) = &d.id {
let idx = ctx.add_constant(Constant::Undefined); let idx = ctx.add_constant(Constant::Undefined);
let tmp = ctx.alloc_reg(); let tmp = ctx.alloc_reg();
@@ -48,10 +90,32 @@ impl Emitter {
)); ));
ctx.init_var(bi.name.as_str()); 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()); 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 { if let Some(test) = &fr.test {
let test_reg = self.emit_expression(test, ctx)?; let test_reg = self.emit_expression(test, ctx)?;
ctx.inst(Inst::jmp_if_false(test_reg, end_label)); ctx.inst(Inst::jmp_if_false(test_reg, end_label));
@@ -59,7 +123,13 @@ impl Emitter {
self.emit_statement(&fr.body, ctx)?; self.emit_statement(&fr.body, ctx)?;
ctx.labels.set_label_pos(update_label, ctx.insts.len()); ctx.labels.set_label_pos(update_label, ctx.insts.len());
if let Some(update) = &fr.update { 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)?; self.emit_expression(update, ctx)?;
ctx.register_update_names = prev;
} }
ctx.inst(Inst::jmp(start_label)); ctx.inst(Inst::jmp(start_label));
ctx.labels.set_label_pos(end_label, ctx.insts.len()); 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)); ctx.inst(Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(key_reg), Operand::None, Operand::None));
match &fi.left { match &fi.left {
ForStatementLeft::VariableDeclaration(decl) => { ForStatementLeft::VariableDeclaration(decl) => {
// let/const 声明对被捕获绑定用 fresh cell(每迭代新 cell);var 单绑定。
let fresh_cell = !matches!(decl.kind, VariableDeclarationKind::Var);
for d in &decl.declarations { for d in &decl.declarations {
let name = match &d.id { match &d.id {
oxide_parser::BindingPattern::BindingIdentifier(bi) => bi.name.as_str(), 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()), _ => 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) => { ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
@@ -9,7 +9,7 @@ use crate::{CompileCtx, Emitter};
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst; use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
use oxide_parser::{ForOfStatement, ForStatementLeft, Statement}; use oxide_parser::{ForOfStatement, ForStatementLeft, Statement, VariableDeclarationKind};
impl Emitter { impl Emitter {
pub(crate) fn emit_for_of_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result<Option<u32>, String> { 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` 写入声明/赋值目标。 /// for-of/for-await-of 左侧绑定:把当前迭代值 `val_reg` 写入声明/赋值目标。
/// let/const 声明对被捕获绑定用 fresh cell(每迭代新 cell,规范 per-iteration 绑定);
/// var 保持单绑定。
fn emit_for_of_left_assignment( fn emit_for_of_left_assignment(
&self, left: &ForStatementLeft, val_reg: u32, ctx: &mut CompileCtx, &self, left: &ForStatementLeft, val_reg: u32, ctx: &mut CompileCtx,
) -> Result<(), String> { ) -> Result<(), String> {
match left { match left {
ForStatementLeft::VariableDeclaration(decl) => { ForStatementLeft::VariableDeclaration(decl) => {
let fresh_cell = !matches!(decl.kind, VariableDeclarationKind::Var);
for d in &decl.declarations { 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) => { ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
+2 -1
View File
@@ -54,6 +54,7 @@ impl Inst {
| OpCode::TRY_FINALLY_BEGIN | OpCode::TRY_FINALLY_BEGIN
| OpCode::TRY_FINALLY_END | OpCode::TRY_FINALLY_END
| OpCode::MAKE_CELL | OpCode::MAKE_CELL
| OpCode::MAKE_CELL_FRESH
| OpCode::CELL_SET | OpCode::CELL_SET
| OpCode::STORE_UPVALUE | OpCode::STORE_UPVALUE
| OpCode::SET_PROP | OpCode::SET_PROP
@@ -254,7 +255,7 @@ impl Inst {
OpCode::SPILL => push_operand(&mut uses, &self.rd), OpCode::SPILL => push_operand(&mut uses, &self.rd),
OpCode::UNSPILL => {} OpCode::UNSPILL => {}
// MAKE_CELL:cell 初值读 rd;CELL_SET/STORE_UPVALUE 读 a // MAKE_CELL:cell 初值读 rd;CELL_SET/STORE_UPVALUE 读 a
OpCode::MAKE_CELL => push_operand(&mut uses, &self.rd), OpCode::MAKE_CELL | OpCode::MAKE_CELL_FRESH => push_operand(&mut uses, &self.rd),
OpCode::CELL_SET | OpCode::STORE_UPVALUE => push_operand(&mut uses, &self.a), OpCode::CELL_SET | OpCode::STORE_UPVALUE => push_operand(&mut uses, &self.a),
// 写对象属性:rd/a/b 全 use // 写对象属性:rd/a/b 全 use
OpCode::SET_PROP OpCode::SET_PROP
+1 -1
View File
@@ -264,7 +264,7 @@ fn rewrite_inst(
inst.op inst.op
); );
} }
OpCode::MAKE_CELL | OpCode::CELL_GET | OpCode::CELL_SET | OpCode::STORE_UPVALUE => { OpCode::MAKE_CELL | OpCode::MAKE_CELL_FRESH | OpCode::CELL_GET | OpCode::CELL_SET | OpCode::STORE_UPVALUE => {
debug_assert!( debug_assert!(
matches!(b, Operand::Imm(_) | Operand::None), matches!(b, Operand::Imm(_) | Operand::None),
"{}: b 槽是 imm16 编码位,不得为 Reg", "{}: b 槽是 imm16 编码位,不得为 Reg",
+18
View File
@@ -204,6 +204,24 @@ impl Vm {
Ok(()) Ok(())
} }
/// 无条件新建 cell 并替换 cell_stack[cell_idx],值为 `regs[rd]`。
///
/// 循环每迭代绑定用:把当前迭代值拷入新 cell,本迭代创建的闭包经
/// CREATE_CLOSURE 捕获新 cell(旧闭包仍指向旧 cell,值保持)。与
/// `dispatch_make_cell` 的唯一区别是恒新建、不复用占位 cell。
#[allow(dead_code)]
pub(crate) fn dispatch_make_cell_fresh(&mut self, rd: usize, instr: u32) -> Result<(), String> {
let cell_idx = opcode::imm16(instr) as usize;
let value = self.regs[rd];
let current = self.cell_stack.last_mut().unwrap();
while current.len() <= cell_idx {
current.push(std::ptr::null_mut());
}
let cell = self.gc_state.session_epoch.alloc(Cell::new(value, true));
current[cell_idx] = cell as *mut Cell;
Ok(())
}
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
let cell_idx = b; let cell_idx = b;
+3
View File
@@ -1101,6 +1101,9 @@ impl Vm {
OpCode::MAKE_CELL => { OpCode::MAKE_CELL => {
self.dispatch_make_cell(rd, instr)?; self.dispatch_make_cell(rd, instr)?;
} }
OpCode::MAKE_CELL_FRESH => {
self.dispatch_make_cell_fresh(rd, instr)?;
}
OpCode::CELL_GET => { OpCode::CELL_GET => {
self.dispatch_cell_get(rd, a, b)?; self.dispatch_cell_get(rd, a, b)?;
} }
@@ -0,0 +1,98 @@
//! 循环每迭代绑定(CreatePerIterationEnvironment):for/for-of/for-in 的 let/const
//! 闭包捕获各自迭代快照。对应 PLAN 06-01(正确性报告 P1/P2)。
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
let result = vm.run(&module)?;
Ok((vm, result))
}
fn to_str(vm: &Vm, val: JsValue) -> String {
vm.lookup_str(val).unwrap_or_default()
}
#[test]
fn c_style_for_let_captures_per_iteration() {
let (vm, result) =
eval("let fns = []; for (let i = 0; i < 3; i++) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "0,1,2");
}
#[test]
fn c_style_for_let_captures_after_multiple_updates() {
let (vm, result) = eval("let fns = []; for (let i = 0; i < 5; i += 2) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "0,2,4");
}
#[test]
fn c_style_for_var_remains_single_binding() {
// var 循环变量是单绑定(规范:闭包捕获同一绑定,最终值共享)。
let (vm, result) =
eval("let fns = []; for (var i = 0; i < 3; i++) fns.push(() => i); [fns[0](), fns[1](), fns[2]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "3,3,3");
}
#[test]
fn for_of_const_captures_per_iteration() {
let (vm, result) = eval("let fns = []; for (const a of [1, 2]) fns.push(() => a); [fns[0](), fns[1]()].join(',')").unwrap();
assert_eq!(to_str(&vm, result), "1,2");
}
#[test]
fn for_of_let_captures_per_iteration() {
let (vm, result) =
eval("let fns = []; for (let a of [10, 20, 30]) fns.push(() => a); [fns[0](), fns[1](), fns[2]()].join(',')").unwrap();
assert_eq!(to_str(&vm, result), "10,20,30");
}
#[test]
fn for_of_destructuring_captures_per_iteration() {
// P2:解构 + 闭包捕获不再产出垃圾值。
let (vm, result) =
eval("let fns = []; for (const [a, b] of [[1, 2], [3, 4]]) fns.push(() => a + b); [fns[0](), fns[1]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "3,7");
}
#[test]
fn for_of_object_destructuring_captures_per_iteration() {
let (vm, result) =
eval("let fns = []; for (const {x, y} of [{x:1,y:2},{x:3,y:4}]) fns.push(() => x * y); [fns[0](), fns[1]()].join(',')")
.unwrap();
assert_eq!(to_str(&vm, result), "2,12");
}
#[test]
fn for_in_const_captures_per_iteration() {
let (vm, result) = eval("let fns = []; for (const k in {a:1,b:2}) fns.push(() => k); [fns[0](), fns[1]()].join(',')").unwrap();
assert_eq!(to_str(&vm, result), "a,b");
}
#[test]
fn nested_loop_closure_captures_distinct_bindings() {
let (vm, result) = eval(
"let fns = []; for (let i = 0; i < 2; i++) for (let j = 0; j < 2; j++) fns.push(() => i + j); [fns[0](), fns[1](), fns[2](), fns[3]()].join(',')",
)
.unwrap();
assert_eq!(to_str(&vm, result), "0,1,1,2");
}
#[test]
fn loop_variable_mutation_visible_to_same_iteration() {
// 同迭代内变量修改(非闭包)仍直接可见:fresh 每迭代一次,迭代内读写走同一 cell。
let (vm, result) = eval(
"let fns = []; for (let i = 0; i < 3; i++) { fns.push(() => i); i += 0; } [fns[0](), fns[1](), fns[2]()].join(',')",
)
.unwrap();
assert_eq!(to_str(&vm, result), "0,1,2");
}