feat: 循环 let/const 每迭代绑定(MAKE_CELL_FRESH + 解构捕获修复)
This commit is contained in:
@@ -217,6 +217,9 @@ define_opcodes! {
|
||||
FOR_AWAIT_OF_NEXT = 0x7B => "FOR_AWAIT_OF_NEXT",
|
||||
FOR_AWAIT_OF_DONE = 0x7C => "FOR_AWAIT_OF_DONE",
|
||||
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) ──
|
||||
BIT_AND = 0x80 => "BIT_AND",
|
||||
|
||||
@@ -28,13 +28,39 @@ impl Emitter {
|
||||
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)绑定的名字。
|
||||
fn collect_for_left_decl_names(&self, left: &oxide_parser::ForStatementLeft, out: &mut HashSet<String>) {
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = left {
|
||||
for d in &vd.declarations {
|
||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
out.insert(bi.name.to_string());
|
||||
}
|
||||
self.collect_binding_pattern_names(&d.id, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,9 +80,7 @@ impl Emitter {
|
||||
match stmt {
|
||||
Statement::VariableDeclaration(vd) => {
|
||||
for d in &vd.declarations {
|
||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
out.insert(bi.name.to_string());
|
||||
}
|
||||
self.collect_binding_pattern_names(&d.id, out);
|
||||
}
|
||||
}
|
||||
Statement::FunctionDeclaration(fd) => {
|
||||
@@ -86,22 +110,14 @@ impl Emitter {
|
||||
// for-in / for-of 头部的 var 声明是本函数局部绑定(遮蔽外层同名),
|
||||
// 须计入 own_bindings,否则闭包捕获分析会误判为捕获外层变量。
|
||||
Statement::ForInStatement(fi) => {
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
|
||||
for d in &vd.declarations {
|
||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
out.insert(bi.name.to_string());
|
||||
}
|
||||
}
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(_) = &fi.left {
|
||||
self.collect_for_left_decl_names(&fi.left, out);
|
||||
}
|
||||
self.collect_decl_names_stmt(std::slice::from_ref(&fi.body), out);
|
||||
}
|
||||
Statement::ForOfStatement(fo) => {
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
|
||||
for d in &vd.declarations {
|
||||
if let oxide_parser::BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
out.insert(bi.name.to_string());
|
||||
}
|
||||
}
|
||||
if let oxide_parser::ForStatementLeft::VariableDeclaration(_) = &fo.left {
|
||||
self.collect_for_left_decl_names(&fo.left, out);
|
||||
}
|
||||
self.collect_decl_names_stmt(std::slice::from_ref(&fo.body), out);
|
||||
}
|
||||
|
||||
@@ -194,6 +194,10 @@ pub struct CompileCtx {
|
||||
/// try_stack 组成。每项标记是否为纯 catch handler(TRY_BEGIN,由 TRY_END
|
||||
/// 弹出):return 逃出 try 域时据此弹出栈顶连续纯 catch,防 handler 泄漏。
|
||||
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)形态。
|
||||
@@ -289,6 +293,7 @@ impl CompileCtx {
|
||||
const_overflow: false,
|
||||
with_stack: Vec::new(),
|
||||
open_try_handlers: Vec::new(),
|
||||
register_update_names: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1117,7 +1122,7 @@ impl Emitter {
|
||||
} else {
|
||||
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 } => {
|
||||
if let Some(init) = initializer {
|
||||
|
||||
@@ -18,6 +18,14 @@ impl Emitter {
|
||||
fn emit_compound_identifier_static(
|
||||
&self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx,
|
||||
) -> 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 captured_cell = ctx.captured_bindings.get(name).copied();
|
||||
if let Some(uv) = uv_idx {
|
||||
|
||||
@@ -112,6 +112,19 @@ impl Emitter {
|
||||
/// - `const_flag` 为 1 时走 STORE_VAR 运行时 const guard(对 const 再赋值报错)。
|
||||
/// - upvalue 与 cell 写穿共享单元,不设 guard。
|
||||
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
|
||||
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||
ctx.inst(Inst::new(
|
||||
|
||||
@@ -418,6 +418,21 @@ impl Emitter {
|
||||
) -> Result<u32, String> {
|
||||
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
||||
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 {
|
||||
// upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE;
|
||||
// 后缀形式返回旧值(前缀返回新值)。
|
||||
|
||||
@@ -48,8 +48,11 @@ impl Emitter {
|
||||
Ok(val_reg)
|
||||
}
|
||||
|
||||
/// 绑定单个标识符到槽位。`fresh_cell` 为 true 时对被捕获绑定用 MAKE_CELL_FRESH
|
||||
/// (循环每迭代绑定:无条件新建 cell,本迭代闭包捕获新 cell)。
|
||||
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> {
|
||||
let target_reg = if matches!(kind, VariableDeclarationKind::Var) {
|
||||
// `var` 名已预声明(hoisting),复用预登记槽位而非新分配,使 n_registers 与 let/const 一致。
|
||||
@@ -66,8 +69,9 @@ impl Emitter {
|
||||
var_reg
|
||||
};
|
||||
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(
|
||||
OpCode::MAKE_CELL,
|
||||
op,
|
||||
Operand::Reg(src_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
@@ -119,14 +123,14 @@ impl Emitter {
|
||||
|
||||
pub(crate) fn emit_binding_pattern(
|
||||
&self, pattern: &BindingPattern, src_reg: u32, kind: VariableDeclarationKind, is_const: bool,
|
||||
ctx: &mut CompileCtx,
|
||||
fresh_cell: bool, ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
match pattern {
|
||||
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::ObjectPattern(op) => self.emit_object_binding(op, 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, fresh_cell, ctx),
|
||||
BindingPattern::AssignmentPattern(ap) => {
|
||||
// `[x = fn]`:默认值函数按绑定名推断 name。
|
||||
let name = match &ap.left {
|
||||
@@ -134,13 +138,14 @@ impl Emitter {
|
||||
_ => None,
|
||||
};
|
||||
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(
|
||||
&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> {
|
||||
ctx.inst(Inst::new(OpCode::FOR_OF_INIT, Operand::None, Operand::Reg(src_reg), Operand::None));
|
||||
for elem in &ap.elements {
|
||||
@@ -149,12 +154,12 @@ impl Emitter {
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
|
||||
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 {
|
||||
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));
|
||||
Ok(())
|
||||
@@ -193,7 +198,8 @@ impl Emitter {
|
||||
}
|
||||
|
||||
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> {
|
||||
// 对象解构先 ToObject(rhs):null/undefined 抛 TypeError(含空 pattern),
|
||||
// 原始值包装为对应对象,保证属性读取与 rest 都以对象为源。
|
||||
@@ -221,7 +227,7 @@ impl Emitter {
|
||||
self.emit_push_excluded_key(arr, Some(key_reg), "", excl_push, ctx);
|
||||
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 {
|
||||
let rest_reg = ctx.alloc_reg();
|
||||
@@ -232,7 +238,7 @@ impl Emitter {
|
||||
excluded_idx as u32,
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -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(¶m.pattern, src_reg, VariableDeclarationKind::Let, false, ctx)?;
|
||||
self.emit_binding_pattern(¶m.pattern, src_reg, VariableDeclarationKind::Let, false, false, ctx)?;
|
||||
}
|
||||
let mut last_catch_result: Option<u32> = None;
|
||||
for s in &catch.body.body {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -54,6 +54,7 @@ impl Inst {
|
||||
| OpCode::TRY_FINALLY_BEGIN
|
||||
| OpCode::TRY_FINALLY_END
|
||||
| OpCode::MAKE_CELL
|
||||
| OpCode::MAKE_CELL_FRESH
|
||||
| OpCode::CELL_SET
|
||||
| OpCode::STORE_UPVALUE
|
||||
| OpCode::SET_PROP
|
||||
@@ -254,7 +255,7 @@ impl Inst {
|
||||
OpCode::SPILL => push_operand(&mut uses, &self.rd),
|
||||
OpCode::UNSPILL => {}
|
||||
// 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),
|
||||
// 写对象属性:rd/a/b 全 use
|
||||
OpCode::SET_PROP
|
||||
|
||||
@@ -264,7 +264,7 @@ fn rewrite_inst(
|
||||
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!(
|
||||
matches!(b, Operand::Imm(_) | Operand::None),
|
||||
"{}: b 槽是 imm16 编码位,不得为 Reg",
|
||||
|
||||
@@ -204,6 +204,24 @@ impl Vm {
|
||||
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)]
|
||||
pub(crate) fn dispatch_cell_get(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
let cell_idx = b;
|
||||
|
||||
@@ -1101,6 +1101,9 @@ impl Vm {
|
||||
OpCode::MAKE_CELL => {
|
||||
self.dispatch_make_cell(rd, instr)?;
|
||||
}
|
||||
OpCode::MAKE_CELL_FRESH => {
|
||||
self.dispatch_make_cell_fresh(rd, instr)?;
|
||||
}
|
||||
OpCode::CELL_GET => {
|
||||
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");
|
||||
}
|
||||
Reference in New Issue
Block a user