mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: C 风格 for 词法头建立独立每迭代环境
This commit is contained in:
@@ -130,6 +130,11 @@ pub struct CompileCtx {
|
||||
/// let/const 循环变量每迭代新分配一个 cell,update 写寄存器(不污染本迭代
|
||||
/// 闭包捕获的 cell),下一迭代把这个寄存器值拷入新分配的 cell。
|
||||
pub(crate) register_update_names: Vec<String>,
|
||||
/// 正在发射的 C 风格 for 头词法绑定名(含解构叶):这些头名在 init 中经覆盖
|
||||
/// cell 建 `MAKE_CELL`,但循环体/test/update 读的是绑定寄存器、每迭代 fresh
|
||||
/// 拷贝的源也是寄存器,故 `emit_bind_target` 对集合内名字在 cell 写之外补写
|
||||
/// 绑定寄存器。init 发射完毕后清空。
|
||||
pub(crate) for_head_store_registers: HashSet<String>,
|
||||
/// 模块编译上下文:当前模块命名空间对象寄存器(`__moduleObject` 返回值)。
|
||||
pub(crate) module_ns_reg: Option<u32>,
|
||||
/// 已求值依赖模块的命名空间对象寄存器(按 import/export source 字符串索引)。
|
||||
@@ -216,6 +221,7 @@ impl CompileCtx {
|
||||
with_stack: Vec::new(),
|
||||
open_try_handlers: Vec::new(),
|
||||
register_update_names: Vec::new(),
|
||||
for_head_store_registers: HashSet::new(),
|
||||
module_ns_reg: None,
|
||||
module_dep_ns_regs: HashMap::new(),
|
||||
module_self_import_specs: HashSet::new(),
|
||||
|
||||
@@ -89,6 +89,16 @@ impl Emitter {
|
||||
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(src_reg), Operand::Imm(cell_idx as u16), Operand::None));
|
||||
// C 风格 for 头绑定:cell 之外补写绑定寄存器——循环体/test/update 读寄存器,
|
||||
// 每迭代 fresh 拷贝的源也是寄存器(见 `CompileCtx::for_head_store_registers`)。
|
||||
if ctx.for_head_store_registers.contains(name) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_VAR,
|
||||
Operand::Reg(target_reg),
|
||||
Operand::Reg(src_reg),
|
||||
Operand::Imm(0),
|
||||
));
|
||||
}
|
||||
} else if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) {
|
||||
// with 内 var 初始化:对象有该属性则写对象,否则写提升槽(动态解析)。
|
||||
// ponytail: with 内顶层 var 不写全局对象属性,with 语句本身已是稀见用例。
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
//! for 语句 emit:`emit_for_statement` 含 init/test/update 三段与循环跳转。
|
||||
//!
|
||||
//! C 风格 for 的词法头按规范建模为独立环境:init 求值前头名绑定未初始化 cell
|
||||
//! (直读与捕获均抛 ReferenceError),被闭包引用或与顶层 var/函数同名的头名保留
|
||||
//! 独立 cell 贯穿循环,其余 init 后撤出覆盖回退寄存器循环。
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::for_in::ForHeadEnv;
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::module::Constant;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
@@ -42,13 +49,32 @@ impl Emitter {
|
||||
let end_label = ctx.next_label_id();
|
||||
ctx.push_loop(end_label, update_label, crate::emit_ctx::LoopKind::Plain);
|
||||
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();
|
||||
// 循环头 let/const 声明名:update 段是 per-iteration 可变绑定(规范
|
||||
// §14.7.4.4 CreatePerIterationEnvironment 用 CreateMutableBinding)——
|
||||
// 规范允许 update 段写 let/const 循环变量,每迭代新建一个可变绑定;
|
||||
// 编译期 const 写检查对 update 段豁免,故记录全部声明名(含未被捕获者)。
|
||||
let mut update_names: Vec<String> = Vec::new();
|
||||
// 词法头独立环境:init 求值前把头名全部覆盖为未初始化 cell,使 init 内的
|
||||
// 直读与该区创建闭包的捕获命中 TDZ cell(不穿透外层同名已初始化绑定)。
|
||||
// init 后仅「被闭包引用」或「与顶层 var/函数同名」的头名保留覆盖贯穿循环
|
||||
// (体/测试/update 走 cell),其余撤出覆盖回退寄存器循环——普通 for-let
|
||||
// 不付每迭代 cell 代价。循环收尾恢复捕获映射,兄弟语句仍见外层同名绑定的
|
||||
// 原 cell。
|
||||
let mut head_keep: HashSet<String> = HashSet::new();
|
||||
let mut head_names: Option<Vec<String>> = None;
|
||||
let mut head_env: Option<ForHeadEnv> = None;
|
||||
if let Some(init) = &fr.init {
|
||||
if let Some(names) = self.collect_for_init_lexical_names(init) {
|
||||
for name in &names {
|
||||
if ctx.captured_bindings.contains_key(name) || self.is_global_tier_name(ctx, name) {
|
||||
head_keep.insert(name.clone());
|
||||
}
|
||||
}
|
||||
head_env = Some(self.begin_for_head_env(names.clone(), ctx)?);
|
||||
ctx.for_head_store_registers = names.iter().cloned().collect();
|
||||
head_names = Some(names);
|
||||
}
|
||||
}
|
||||
if let Some(init) = &fr.init {
|
||||
if let Some(expr) = init.as_expression() {
|
||||
self.emit_expression(expr, ctx)?;
|
||||
@@ -68,26 +94,9 @@ 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)?;
|
||||
// 捕获头名的寄存器补写在 `emit_bind_target` 经
|
||||
// `for_head_store_registers` 完成(解构叶用各自的叶值,不用整头 RHS)。
|
||||
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) {
|
||||
// 全局不可写内置的捕获写被声明路径拦截,同步写一并跳过。
|
||||
if !ctx.targets_readonly_builtin(name, reg) {
|
||||
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();
|
||||
@@ -112,24 +121,49 @@ impl Emitter {
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
// 词法头无初始化声明:覆盖 cell 须置为已初始化(undefined),
|
||||
// 否则后续 init 表达式内创建、捕获该头名的闭包读时误报 TDZ。
|
||||
if !matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(bi.name.as_str()) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(tmp),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
ctx.init_var(bi.name.as_str());
|
||||
}
|
||||
// 记录 let/const 循环头声明名(update 段写豁免所需)与被捕获的绑定
|
||||
// (解构 pattern 递归收集绑定名,被捕获者每迭代 fresh cell)。
|
||||
// 记录 let/const 循环头声明名(update 段写豁免所需);fresh cell 判定
|
||||
// 在 init 后按保留集统一重算(见下方)。
|
||||
if !matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||
let mut names = Vec::new();
|
||||
self.collect_pattern_binding_names(&d.id, &mut names);
|
||||
for name in &names {
|
||||
update_names.push(name.clone());
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
|
||||
fresh_bindings.push((name.clone(), cell_idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.pending_for_head_names = saved_pending;
|
||||
}
|
||||
}
|
||||
ctx.for_head_store_registers.clear();
|
||||
// 撤出非保留头名的覆盖:普通 for-let 回退寄存器循环,只有被闭包引用或与顶层
|
||||
// var/函数同名的头名保留独立 cell 贯穿循环。
|
||||
for name in head_names.iter().flatten() {
|
||||
if !head_keep.contains(name) {
|
||||
ctx.captured_bindings.remove(name);
|
||||
}
|
||||
}
|
||||
// 每迭代 fresh cell 的绑定:保留覆盖者(仍含 cell 下标);已撤出覆盖的头名
|
||||
// 走寄存器,不含在内。
|
||||
let mut fresh_bindings: Vec<(String, u8)> = Vec::new();
|
||||
for name in &update_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)。
|
||||
@@ -159,6 +193,11 @@ impl Emitter {
|
||||
self.emit_expression(update, ctx)?;
|
||||
ctx.register_update_names = prev;
|
||||
}
|
||||
// 恢复捕获映射:循环后的兄弟语句仍见外层同名绑定的原 cell(词法头环境
|
||||
// 作用是语句局部的)。
|
||||
if let Some(env) = head_env {
|
||||
self.restore_for_head_env(env, ctx);
|
||||
}
|
||||
ctx.inst(Inst::jmp(start_label));
|
||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||
ctx.pop_label_scopes(n_labeled);
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{ForStatementLeft, Statement, VariableDeclarationKind};
|
||||
use oxide_parser::{ForStatementInit, ForStatementLeft, Statement, VariableDeclarationKind};
|
||||
|
||||
/// for-in/for-of 词法头的捕获映射覆盖:每头名一条记录,依次为名字、旧条目
|
||||
/// (无旧条目为 None)、右值区 TDZ cell、体区 fresh cell(切换后填入)。
|
||||
@@ -22,6 +22,8 @@ pub(crate) struct ForHeadEnv {
|
||||
impl Emitter {
|
||||
/// 收集 for-in/for-of 词法声明头(let/const/using,含解构 pattern 叶)的
|
||||
/// 头名(排序保证稳定);var 头与赋值头无 TDZ 环境,返回 None。
|
||||
/// C 风格 for 的 init 布尔头同用 `ForHeadEnv`,其头名收集见
|
||||
/// [`Emitter::collect_for_init_lexical_names`]。
|
||||
pub(crate) fn collect_for_head_lexical_names(&self, left: &ForStatementLeft) -> Option<Vec<String>> {
|
||||
let ForStatementLeft::VariableDeclaration(decl) = left else {
|
||||
return None;
|
||||
@@ -38,6 +40,27 @@ impl Emitter {
|
||||
Some(v)
|
||||
}
|
||||
|
||||
/// 收集 C 风格 for 头词法声明(let/const,含解构 pattern 叶)的头名(排序
|
||||
/// 保证稳定);var 头与表达式头无 TDZ 环境,返回 None。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 无;纯 AST 收集。
|
||||
pub(crate) fn collect_for_init_lexical_names(&self, init: &ForStatementInit) -> Option<Vec<String>> {
|
||||
let ForStatementInit::VariableDeclaration(decl) = init else {
|
||||
return None;
|
||||
};
|
||||
if matches!(decl.kind, VariableDeclarationKind::Var) {
|
||||
return None;
|
||||
}
|
||||
let mut names = HashSet::new();
|
||||
for d in &decl.declarations {
|
||||
collect_binding_pattern_names(&d.id, &mut names);
|
||||
}
|
||||
let mut v: Vec<String> = names.into_iter().collect();
|
||||
v.sort();
|
||||
Some(v)
|
||||
}
|
||||
|
||||
/// 把词法头名覆盖到捕获映射(右值区阶段):保存旧条目、分配 TDZ cell 并
|
||||
/// 发射未初始化 MAKE_CELL、头名映射 TDZ cell。右值区内对头名的直读与
|
||||
/// 该区内创建闭包的捕获均命中未初始化 cell(读抛 ReferenceError)。
|
||||
|
||||
Reference in New Issue
Block a user