mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 03:01:12 +08:00
fix: 修复块级闭包 TDZ 占位与调用窗口踩踏及 for-of 迭代器展开
This commit is contained in:
@@ -115,6 +115,11 @@ pub struct CompileCtx {
|
||||
/// 0..n-1 占满),块级遮蔽绑定预声明追加、catch 参数就地声明与合成 cell 经
|
||||
/// `alloc_cell_idx` 递增取号,保证与函数级名字排序索引及历次追加互不碰撞。
|
||||
pub(crate) next_cell_idx: u8,
|
||||
/// 块入口已发每迭代 TDZ 占位 MAKE_CELL 的 cell 索引集:被捕获的块级
|
||||
/// let/const 在块入口每迭代新建未初始化 cell(循环体重执行即新 cell),
|
||||
/// 声明点据此改发原位 MAKE_CELL(闭包捕获占位 cell 后跟随初始化),
|
||||
/// 不发 MAKE_CELL_FRESH(替换会孤儿化声明前创建的闭包)。
|
||||
pub(crate) per_iter_predeclared_cells: HashSet<u8>,
|
||||
/// 词法循环头(C-for / for-in / for-of)名集:头名索引由 ForHeadEnv 覆盖
|
||||
/// 捕获映射分配(TDZ 占位与体区 fresh cell),声明点据此走映射回退而非
|
||||
/// 就地追加新索引。begin 时并入、restore 时移除,嵌套循环成对。
|
||||
@@ -304,6 +309,7 @@ impl CompileCtx {
|
||||
block_fn_entry_mats: Vec::new(),
|
||||
captured_bindings: BTreeMap::new(),
|
||||
next_cell_idx: 0,
|
||||
per_iter_predeclared_cells: HashSet::new(),
|
||||
for_head_env_names: HashSet::new(),
|
||||
pending_for_head_names: HashSet::new(),
|
||||
global_tier_names: HashSet::new(),
|
||||
|
||||
@@ -425,6 +425,11 @@ impl Emitter {
|
||||
{
|
||||
let idx = ctx.alloc_cell_idx();
|
||||
ctx.set_binding_cell_idx(bi.name.as_str(), idx);
|
||||
// 登记每迭代 TDZ 占位:循环体内块重执行时该 MAKE_CELL 每迭代
|
||||
// 新建未初始化 cell,声明点据此改发原位 MAKE_CELL(闭包捕获
|
||||
// 占位 cell 后跟随初始化),替换式 fresh 会孤儿化声明前创建的
|
||||
// 闭包(提升函数声明)致 TDZ 误报。
|
||||
ctx.per_iter_predeclared_cells.insert(idx);
|
||||
let undef_reg = self.emit_undefined(ctx);
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
|
||||
@@ -101,7 +101,15 @@ impl Emitter {
|
||||
// 绑定走回填索引,块级遮蔽绑定走预声明追加索引,catch 参数等块作用域
|
||||
// 就地声明现场追加;词法循环头名走捕获映射回退。
|
||||
if let Some(cell_idx) = ctx.resolve_bind_cell_idx(name) {
|
||||
let op = if fresh_cell { OpCode::MAKE_CELL_FRESH } else { OpCode::MAKE_CELL };
|
||||
// 每迭代 TDZ 占位已建(块入口预声明)时改发原位 MAKE_CELL:占位
|
||||
// cell 是本迭代新 cell,声明前创建的闭包(提升函数声明)已捕获它,
|
||||
// 原位更新使其跟随初始化;仅体区 fresh cell 场景(循环头绑定,占位
|
||||
// 一次性)发替换式 MAKE_CELL_FRESH。
|
||||
let op = if fresh_cell && !ctx.per_iter_predeclared_cells.contains(&cell_idx) {
|
||||
OpCode::MAKE_CELL_FRESH
|
||||
} else {
|
||||
OpCode::MAKE_CELL
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(src_reg), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
// C 风格 for 头绑定:cell 之外补写绑定寄存器——循环体/test/update 读寄存器,
|
||||
// 每迭代 fresh 拷贝的源也是寄存器(见 `CompileCtx::for_head_store_registers`)。
|
||||
|
||||
@@ -79,8 +79,13 @@ impl Emitter {
|
||||
// 被捕获绑定的 cell 在函数入口已实例化(undefined);声明语句
|
||||
// 不赋值,仅首次声明刷新初值,已绑定的 cell 保留现值。
|
||||
if !already_bound {
|
||||
// 重执行迭代取新 cell,与 with-init 臂同一门控。
|
||||
let op = if reexec { OpCode::MAKE_CELL_FRESH } else { OpCode::MAKE_CELL };
|
||||
// 重执行迭代取新 cell,与 with-init 臂同一门控:块入口每迭代
|
||||
// TDZ 占位已建时原位更新,防孤儿化声明前创建的闭包。
|
||||
let op = if reexec && !ctx.per_iter_predeclared_cells.contains(&cell_idx) {
|
||||
OpCode::MAKE_CELL_FRESH
|
||||
} else {
|
||||
OpCode::MAKE_CELL
|
||||
};
|
||||
ctx.inst(Inst::new(op, Operand::Reg(tmp), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
}
|
||||
} else if !already_bound {
|
||||
|
||||
Reference in New Issue
Block a user