From 1144b07534ab194132765c6bbdd21f2fbddbcb23 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Mon, 5 Oct 2026 16:21:42 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8D=95=E8=8E=B7=20cell=20=E6=8D=A2?= =?UTF-8?q?=E9=94=AE=20for=20=E5=A4=B4=E6=8C=89=E7=BB=91=E5=AE=9A=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E5=8F=96=E7=B4=A2=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 36 +++++++-------- Cargo.toml | 2 +- crates/oxide_emit/src/compile_ctx.rs | 7 +++ crates/oxide_emit/src/shared/binding.rs | 9 ++++ crates/oxide_emit/src/stmt/iteration/for_.rs | 30 ++++++++++--- .../oxide_emit/src/stmt/iteration/for_in.rs | 45 ++++++++++--------- .../oxide_emit/src/stmt/iteration/for_of.rs | 35 +++++++++------ 7 files changed, 102 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1578153f..7070048f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1095,7 +1095,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.8" +version = "0.8.9" dependencies = [ "chrono", "fast-float", @@ -1120,7 +1120,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.8" +version = "0.8.9" dependencies = [ "num-bigint", "oxide_log", @@ -1128,7 +1128,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.8" +version = "0.8.9" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.8" +version = "0.8.9" dependencies = [ "ansi_term", "bumpalo", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.8" +version = "0.8.9" dependencies = [ "lru", "oxide_bytecode", @@ -1168,7 +1168,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.8" +version = "0.8.9" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1187,7 +1187,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.8" +version = "0.8.9" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1201,7 +1201,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.8" +version = "0.8.9" dependencies = [ "num-bigint", "oxc_allocator", @@ -1216,7 +1216,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.8" +version = "0.8.9" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1225,7 +1225,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.8" +version = "0.8.9" dependencies = [ "bumpalo", "dashmap", @@ -1240,7 +1240,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.8" +version = "0.8.9" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1252,7 +1252,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.8" +version = "0.8.9" dependencies = [ "tracing", "tracing-appender", @@ -1261,7 +1261,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.8" +version = "0.8.9" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1276,7 +1276,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.8" +version = "0.8.9" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1292,7 +1292,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.8" +version = "0.8.9" dependencies = [ "num-bigint", "num-traits", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.8" +version = "0.8.9" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.8" +version = "0.8.9" dependencies = [ "bumpalo", "hashbrown 0.14.5", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.8" +version = "0.8.9" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 4005df02..5ffc69c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ ] [workspace.package] -version = "0.8.8" +version = "0.8.9" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_emit/src/compile_ctx.rs b/crates/oxide_emit/src/compile_ctx.rs index c61c7282..c87ed937 100644 --- a/crates/oxide_emit/src/compile_ctx.rs +++ b/crates/oxide_emit/src/compile_ctx.rs @@ -153,6 +153,12 @@ pub struct CompileCtx { /// 拷贝的源也是寄存器,故 `emit_bind_target` 对集合内名字在 cell 写之外补写 /// 绑定寄存器。init 发射完毕后清空。 pub(crate) for_head_store_registers: HashSet, + /// C 风格 for 头保留集:被闭包引用或与顶层 var/函数同名的头名。这些头名保留 + /// 独立 cell 贯穿循环(体/test/update 走 cell),其余头名撤出覆盖回退寄存器循环。 + /// 仅 C-for 填充(for-in/for-of 全头名保留,本集为空);`emit_bind_target` 头名 + /// 回填据此门控,非保留头名不置位绑定字段(否则体区读命中陈旧 cell 致死循环)。 + /// begin 前按捕获集与顶层 tier 名计算,init 发射完毕后清空。 + pub(crate) for_head_keep: HashSet, /// 模块编译上下文:当前模块命名空间对象寄存器(`__moduleObject` 返回值)。 pub(crate) module_ns_reg: Option, /// 写穿反演表:本模块源绑定槽寄存器 → 引用它的导出名集合。仅自导入 ns 的 @@ -271,6 +277,7 @@ impl CompileCtx { completion_frames: Vec::new(), register_update_names: Vec::new(), for_head_store_registers: HashSet::new(), + for_head_keep: HashSet::new(), module_ns_reg: None, module_local_export_regs: HashMap::new(), module_dep_ns_regs: HashMap::new(), diff --git a/crates/oxide_emit/src/shared/binding.rs b/crates/oxide_emit/src/shared/binding.rs index a4f004e0..cbbfc073 100644 --- a/crates/oxide_emit/src/shared/binding.rs +++ b/crates/oxide_emit/src/shared/binding.rs @@ -78,6 +78,15 @@ impl Emitter { var_reg } }; + // 头名回填:for 头绑定(字段 None)把覆盖 cell 索引写进绑定字段,解析点 + // 按绑定身份取索引。保留集门控:C-for 仅保留头名置位(非保留头名走寄存器 + // 循环,置位会使体区读命中陈旧 cell 致死循环);for-in/for-of 全头名保留。 + // 非 for 头绑定(函数级 / 块级 / catch 参数)已由各自路径置位,不在此覆盖。 + if ctx.for_head_keep.contains(name) { + if let Some(&idx) = ctx.captured_bindings.get(name) { + ctx.set_binding_cell_idx(name, idx); + } + } if ctx.targets_readonly_builtin(name, target_reg) { // 声明撞全局不可写内置:声明不更新既有全局绑定——sloppy 静默跳过 // (槽保留入口预载原值),strict 在初始化求值后抛 TypeError。 diff --git a/crates/oxide_emit/src/stmt/iteration/for_.rs b/crates/oxide_emit/src/stmt/iteration/for_.rs index 99da90e9..cf0bd07c 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_.rs @@ -68,6 +68,7 @@ impl Emitter { for name in &names { if ctx.captured_bindings.contains_key(name) || self.is_global_tier_name(ctx, name) { head_keep.insert(name.clone()); + ctx.for_head_keep.insert(name.clone()); } } head_env = Some(self.begin_for_head_env(names.clone(), ctx)?); @@ -111,6 +112,15 @@ impl Emitter { } } else { ctx.declare(bi.name.as_str(), var_reg, decl.kind, is_const)?; + // 头名回填:保留头名在 init 声明点建绑定后把覆盖 cell 索引 + // 写进绑定字段,解析点按绑定身份取索引;非保留头名走寄存器 + // 循环,不置位绑定字段(否则体区读经可见判定命中陈旧 cell, + // update 段写寄存器不覆写 cell,循环条件恒真致死循环)。 + if head_keep.contains(bi.name.as_str()) { + if let Some(&idx) = ctx.captured_bindings.get(bi.name.as_str()) { + ctx.set_binding_cell_idx(bi.name.as_str(), idx); + } + } (var_reg, false) }; if !already_bound { @@ -121,10 +131,12 @@ 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()) { + // 词法头无初始化声明:保留头名的覆盖 cell 须置为已初始化 + // (undefined),否则后续 init 表达式内创建、捕获该头名的 + // 闭包读时误报 TDZ;非保留头名走寄存器循环,不建 cell。 + // 索引按绑定实例取(`visible_cell` 已含可见性判定)。 + if !matches!(decl.kind, VariableDeclarationKind::Var) && head_keep.contains(bi.name.as_str()) { + if let Some((cell_idx, _)) = ctx.visible_cell(bi.name.as_str()) { ctx.inst(Inst::new( OpCode::MAKE_CELL, Operand::Reg(tmp), @@ -149,6 +161,7 @@ impl Emitter { } } ctx.for_head_store_registers.clear(); + ctx.for_head_keep.clear(); // 撤出非保留头名的覆盖:普通 for-let 回退寄存器循环,只有被闭包引用或与顶层 // var/函数同名的头名保留独立 cell 贯穿循环。 for name in head_names.iter().flatten() { @@ -157,11 +170,14 @@ impl Emitter { } } // 每迭代 fresh cell 的绑定:保留覆盖者(仍含 cell 下标);已撤出覆盖的头名 - // 走寄存器,不含在内。 + // 走寄存器,不含在内。门控用保留集而非可见性判定:头名字段对保留与非保留 + // 头名都置位,可见性对全部头名返 Some,按可见性门控会误纳非保留头名。 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)); + if head_keep.contains(name) { + if let Some((cell_idx, _)) = ctx.visible_cell(name) { + fresh_bindings.push((name.clone(), cell_idx)); + } } } ctx.labels.set_label_pos(start_label, ctx.insts.len()); diff --git a/crates/oxide_emit/src/stmt/iteration/for_in.rs b/crates/oxide_emit/src/stmt/iteration/for_in.rs index dd90946c..a387deb1 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_in.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_in.rs @@ -67,7 +67,7 @@ impl Emitter { /// /// # 步骤 /// 1. 逐头名:移除旧条目并记入恢复清单。 - /// 2. 按现有最大 cell 索引顺序分配 TDZ cell(与类 brand 合成 cell 同口径)。 + /// 2. 按共享计数器顺序分配 TDZ cell(与块级追加 / 合成 cell 同口径)。 /// 3. 发射未初始化 MAKE_CELL 并覆盖映射。 /// /// # 副作用 @@ -76,11 +76,9 @@ impl Emitter { /// 防头名泄漏进兄弟语句的捕获判定。 pub(crate) fn begin_for_head_env(&self, names: Vec, ctx: &mut CompileCtx) -> Result { let mut entries = Vec::with_capacity(names.len()); - let mut next = ctx.captured_bindings.values().copied().max().map_or(0, |m| m.saturating_add(1)); for name in names { let saved = ctx.captured_bindings.remove(&name); - let tdz_idx = next; - next = next.saturating_add(1); + let tdz_idx = ctx.alloc_cell_idx(); ctx.captured_bindings.insert(name.clone(), tdz_idx); // 头名登记:声明点索引解析据此走映射回退,不就地追加新索引。 ctx.for_head_env_names.insert(name.clone()); @@ -96,7 +94,6 @@ impl Emitter { )); entries.push((name, saved, tdz_idx, 0)); } - ctx.next_cell_idx = next; Ok(ForHeadEnv { entries }) } @@ -117,12 +114,10 @@ impl Emitter { if env.entries.is_empty() { return; } - let mut next = ctx.captured_bindings.values().copied().max().map_or(0, |m| m.saturating_add(1)); // 默认值窗口的 TDZ 占位:未初始化标志折入 MAKE_CELL 立即数高字节。 let undef_reg = self.emit_undefined(ctx); for (name, _, _, body_idx) in &mut env.entries { - let idx = next; - next = next.saturating_add(1); + let idx = ctx.alloc_cell_idx(); *body_idx = idx; ctx.captured_bindings.insert(name.clone(), idx); ctx.inst(Inst::new( @@ -132,7 +127,6 @@ impl Emitter { Operand::None, )); } - ctx.next_cell_idx = next; } /// 语句收尾恢复捕获映射:旧条目放回(无旧条目则移除),头名不进入后续 @@ -148,6 +142,7 @@ impl Emitter { } } ctx.for_head_env_names.remove(&name); + ctx.for_head_keep.remove(&name); } } @@ -162,7 +157,11 @@ impl Emitter { // 每迭代 fresh cell,体发射后恢复捕获映射。 let mut head_env = self .collect_for_head_lexical_names(&fi.left) - .map(|names| self.begin_for_head_env(names, ctx)) + .map(|names| { + // for-in 全头名保留(无撤出覆盖),保留集填全头名供头名回填门控。 + ctx.for_head_keep.extend(names.iter().cloned()); + self.begin_for_head_env(names, ctx) + }) .transpose()?; let obj_reg = self.emit_expression(&fi.right, ctx)?; if let Some(env) = &mut head_env { @@ -201,6 +200,12 @@ impl Emitter { } } else { ctx.declare(name, var_reg, decl.kind, is_const)?; + // 头名回填:体区绑定点建绑定后把覆盖 cell 索引写进绑定 + // 字段,解析点按绑定身份取索引(var 头绑定已回填, + // 同值覆盖幂等)。 + if let Some(&idx) = ctx.captured_bindings.get(name) { + ctx.set_binding_cell_idx(name, idx); + } var_reg }; if ctx.targets_readonly_builtin(name, target_reg) { @@ -209,7 +214,7 @@ impl Emitter { if ctx.is_strict { self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx)?; } - } else if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + } else if let Some((cell_idx, _)) = ctx.visible_cell(name) { let op = if fresh_cell { OpCode::MAKE_CELL_FRESH } else { OpCode::MAKE_CELL }; ctx.inst(Inst::new( op, @@ -253,17 +258,13 @@ impl Emitter { Operand::Reg(key_reg), Operand::Imm(uv_idx as u16), )); - } else if let Some(&cell_idx) = ctx.captured_bindings.get(name) { - if ctx.visible_binding_reg(name).is_some() { - ctx.inst(Inst::new( - OpCode::CELL_SET, - Operand::None, - Operand::Reg(key_reg), - Operand::Imm(cell_idx as u16), - )); - } else { - self.emit_for_in_assignment_target_write(name, key_reg, ctx)?; - } + } else if let Some((cell_idx, _)) = ctx.visible_cell(name) { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(key_reg), + Operand::Imm(cell_idx as u16), + )); } else { self.emit_for_in_assignment_target_write(name, key_reg, ctx)?; } diff --git a/crates/oxide_emit/src/stmt/iteration/for_of.rs b/crates/oxide_emit/src/stmt/iteration/for_of.rs index 3ed6d345..2aba4d70 100644 --- a/crates/oxide_emit/src/stmt/iteration/for_of.rs +++ b/crates/oxide_emit/src/stmt/iteration/for_of.rs @@ -30,7 +30,11 @@ impl Emitter { // 每迭代 fresh cell,体发射后恢复捕获映射(同 for-in 覆盖)。 let mut head_env = self .collect_for_head_lexical_names(&fo.left) - .map(|names| self.begin_for_head_env(names, ctx)) + .map(|names| { + // for-of 全头名保留(无撤出覆盖),保留集填全头名供头名回填门控。 + ctx.for_head_keep.extend(names.iter().cloned()); + self.begin_for_head_env(names, ctx) + }) .transpose()?; let iter_src_reg = self.emit_expression(&fo.right, ctx)?; if let Some(env) = &mut head_env { @@ -77,7 +81,11 @@ impl Emitter { // 词法头 TDZ 环境:同同步 for-of(右值区未初始化 cell → 体区 fresh cell)。 let mut head_env = self .collect_for_head_lexical_names(&fo.left) - .map(|names| self.begin_for_head_env(names, ctx)) + .map(|names| { + // for-await-of 全头名保留(无撤出覆盖),保留集填全头名供头名回填门控。 + ctx.for_head_keep.extend(names.iter().cloned()); + self.begin_for_head_env(names, ctx) + }) .transpose()?; let iter_src_reg = self.emit_expression(&fo.right, ctx)?; if let Some(env) = &mut head_env { @@ -153,18 +161,17 @@ impl Emitter { )); return Ok(()); } - // 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写 - // cell,否则落下方全局解析(未声明名不建隐式全局登记)。 - if let Some(&cell_idx) = ctx.captured_bindings.get(name) { - if ctx.visible_binding_reg(name).is_some() { - ctx.inst(Inst::new( - OpCode::CELL_SET, - Operand::None, - Operand::Reg(val_reg), - Operand::Imm(cell_idx as u16), - )); - return Ok(()); - } + // 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法 + // 绑定时才写 cell(`visible_cell` 已含可见性判定),否则落下方 + // 全局解析(未声明名不建隐式全局登记)。 + if let Some((cell_idx, _)) = ctx.visible_cell(name) { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(val_reg), + Operand::Imm(cell_idx as u16), + )); + return Ok(()); } let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) {