diff --git a/Cargo.lock b/Cargo.lock index 025f45b4..eacbac76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1095,7 +1095,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.6" +version = "0.8.7" dependencies = [ "chrono", "fast-float", @@ -1120,7 +1120,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.6" +version = "0.8.7" dependencies = [ "num-bigint", "oxide_log", @@ -1128,7 +1128,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.6" +version = "0.8.7" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1139,7 +1139,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.6" +version = "0.8.7" dependencies = [ "ansi_term", "bumpalo", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.6" +version = "0.8.7" dependencies = [ "lru", "oxide_bytecode", @@ -1168,7 +1168,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.6" +version = "0.8.7" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1187,7 +1187,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.6" +version = "0.8.7" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1201,7 +1201,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.6" +version = "0.8.7" dependencies = [ "num-bigint", "oxc_allocator", @@ -1216,7 +1216,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.6" +version = "0.8.7" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1225,7 +1225,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.6" +version = "0.8.7" dependencies = [ "bumpalo", "dashmap", @@ -1240,7 +1240,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.6" +version = "0.8.7" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1252,7 +1252,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.6" +version = "0.8.7" dependencies = [ "tracing", "tracing-appender", @@ -1261,7 +1261,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.6" +version = "0.8.7" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1276,7 +1276,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.6" +version = "0.8.7" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1292,7 +1292,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.6" +version = "0.8.7" dependencies = [ "num-bigint", "num-traits", @@ -1304,7 +1304,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.6" +version = "0.8.7" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.6" +version = "0.8.7" dependencies = [ "bumpalo", "hashbrown 0.14.5", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.6" +version = "0.8.7" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index b5a7cfc3..68887e34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ ] [workspace.package] -version = "0.8.6" +version = "0.8.7" 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 82606112..f470ccee 100644 --- a/crates/oxide_emit/src/compile_ctx.rs +++ b/crates/oxide_emit/src/compile_ctx.rs @@ -94,6 +94,10 @@ pub struct CompileCtx { /// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射, /// 消除符号表时序依赖与 cell 索引错位。 pub(crate) captured_bindings: BTreeMap, + /// 下一可用 cell 索引(计数器):捕获分析后初始化为名字集大小(函数级索引 + /// 0..n-1 占满),块级遮蔽绑定预声明追加与合成 cell 经 `alloc_cell_idx` + /// 递增取号,保证与函数级名字排序索引及历次追加互不碰撞。 + pub(crate) next_cell_idx: u8, /// 正在发射的 C 风格 for 头 let/const 声明名(含解构叶)。头名不像块级 /// let/const 那样在块入口预声明,init 表达式内创建的嵌套函数引用同头尚未 /// declare 的前向名时,父捕获可见性过滤据此放行;init 发射完毕后移除, @@ -248,6 +252,7 @@ impl CompileCtx { block_fn_suppressed: HashSet::new(), block_fn_entry_mats: Vec::new(), captured_bindings: BTreeMap::new(), + next_cell_idx: 0, pending_for_head_names: HashSet::new(), global_tier_names: HashSet::new(), upvalue_const_flags: HashSet::new(), @@ -417,6 +422,52 @@ impl CompileCtx { (!self.is_implicit_global_reg(binding.reg)).then_some(binding.reg) } + /// 解析点 cell 索引与绑定寄存器:名字当前可见(真实词法绑定、非隐式全局 + /// 登记)时返回 (cell_idx, binding_reg)。索引优先取绑定自身字段(函数级 + /// 回填与块级追加的真源),绑定未分配索引时回退捕获映射(for 头覆盖 / + /// catch 参数等容器面,后续子任务逐面收编)。块级遮蔽绑定在块退出后不可 + /// 解析,返回 None(落全局解析),不得按名误读已失效 cell。 + pub(crate) fn visible_cell(&self, name: &str) -> Option<(u8, u32)> { + let (binding, _) = self.scopes.symbols.lookup_any_binding(name)?; + if self.is_implicit_global_reg(binding.reg) { + return None; + } + let idx = binding.cell_idx.or_else(|| self.captured_bindings.get(name).copied())?; + Some((idx, binding.reg)) + } + + /// 设置捕获集并同步 cell 索引计数器:映射值为 0..n-1 的名字排序索引, + /// 计数器自 n 起保证后续追加(块级预声明 / 合成 cell)不与既有索引碰撞。 + pub(crate) fn set_captured_bindings(&mut self, map: BTreeMap) { + self.next_cell_idx = map.len() as u8; + self.captured_bindings = map; + } + + /// 分配一个新 cell 索引:取当前计数器值并递增。块级遮蔽绑定预声明追加与 + /// 合成 cell 共用,分配序由发射序决定,跨 run 稳定。 + #[allow(dead_code)] // 块级遮蔽绑定预声明追加(后续子任务)消费,本件仅立计数器 + pub(crate) fn alloc_cell_idx(&mut self) -> u8 { + let idx = self.next_cell_idx; + self.next_cell_idx = self.next_cell_idx.saturating_add(1); + idx + } + + /// 给最内层同名可见绑定写入 cell 索引(委托符号表)。 + pub(crate) fn set_binding_cell_idx(&mut self, name: &str, idx: u8) { + self.scopes.symbols.set_binding_cell_idx(name, idx); + } + + /// 函数级绑定 cell 索引回填:捕获集(名字排序分配)的索引写入对应绑定 + /// 实例,使解析点按绑定身份取索引。预声明完成后调用,函数级绑定(参数 / + /// var / 直接子级词法 / 块级函数外层 var / arguments)此时已齐。 + pub(crate) fn backfill_captured_cell_idxs(&mut self) { + let entries: Vec<(String, u8)> = + self.captured_bindings.iter().map(|(name, &idx)| (name.clone(), idx)).collect(); + for (name, idx) in entries { + self.set_binding_cell_idx(&name, idx); + } + } + pub(crate) fn lookup_const_flag(&self, name: &str) -> bool { // upvalue 捕获的 const:子 ctx 符号表不含父函数作用域绑定,查快照标志。 self.scopes.symbols.lookup_is_const(name) || self.upvalue_const_flags.contains(name) @@ -810,9 +861,16 @@ impl CompileCtx { param_layout, builtin_reg_map: std::mem::take(&mut self.scopes.builtin_reg_map), upvalue_captures, - // 容量按名字集大小取:别名捕获合并会让多个名字共享同一 cell, - // 下标可重复但不超过名字数,故 len 恒覆盖实际使用的最大下标。 - cells_needed: self.captured_bindings.len() as u8, + // 容量覆盖全部已分配 cell 索引:函数级名字排序索引(映射值 0..n-1) + // 与追加索引(计数器,块级预声明 / 合成 cell)取较大者加一;别名 + // 捕获合并让多个名字共享同一 cell,下标可重复。 + cells_needed: self + .captured_bindings + .values() + .copied() + .max() + .map_or(0, |m| m.saturating_add(1)) + .max(self.next_cell_idx), n_registers: self.max_regs, is_arrow: false, is_class_constructor: false, diff --git a/crates/oxide_emit/src/expr/assignment.rs b/crates/oxide_emit/src/expr/assignment.rs index 7b627a00..086502d2 100644 --- a/crates/oxide_emit/src/expr/assignment.rs +++ b/crates/oxide_emit/src/expr/assignment.rs @@ -49,7 +49,6 @@ impl Emitter { } } let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); - let captured_cell = ctx.captured_bindings.get(name).copied(); let const_flag = if ctx.lookup_const_flag(name) { 1 } else { 0 }; if let Some(uv) = uv_idx { let val_reg = ctx.alloc_reg(); @@ -68,16 +67,12 @@ impl Emitter { )); self.emit_module_write_through(name, val_reg, ctx)?; Ok(val_reg) - } else if let Some(cell_idx) = captured_cell { + } else if let Some((cell_idx, binding_reg)) = ctx.visible_cell(name) { let val_reg = ctx.alloc_reg(); - let a_operand = match ctx.scopes.symbols.lookup_any_binding(name) { - Some((binding, _)) => Operand::Reg(binding.reg), - None => Operand::None, - }; ctx.inst(Inst::new( OpCode::CELL_GET, Operand::Reg(val_reg), - a_operand, + Operand::Reg(binding_reg), Operand::Imm(cell_idx as u16), )); ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None)); @@ -376,7 +371,6 @@ impl Emitter { self.emit_identifier_tdz_guard(name, ctx)?; // 目标判定:upvalue / 被捕获 cell / 普通槽,读与写须穿透共享单元。 let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); - let captured_cell = ctx.captured_bindings.get(name).copied(); let result_reg = ctx.alloc_reg(); if let Some(uv) = uv_idx { ctx.inst(Inst::new( @@ -385,15 +379,11 @@ impl Emitter { Operand::Imm(uv as u16), Operand::None, )); - } else if let Some(cell_idx) = captured_cell { - let a_operand = match ctx.scopes.symbols.lookup_any_binding(name) { - Some((binding, _)) => Operand::Reg(binding.reg), - None => Operand::None, - }; + } else if let Some((cell_idx, binding_reg)) = ctx.visible_cell(name) { ctx.inst(Inst::new( OpCode::CELL_GET, Operand::Reg(result_reg), - a_operand, + Operand::Reg(binding_reg), Operand::Imm(cell_idx as u16), )); } else { @@ -429,7 +419,7 @@ impl Emitter { Operand::Reg(val_reg), Operand::Imm(uv as u16), )); - } else if let Some(cell_idx) = captured_cell { + } else if let Some((cell_idx, _)) = ctx.visible_cell(name) { ctx.inst(Inst::new( OpCode::CELL_SET, Operand::None, diff --git a/crates/oxide_emit/src/expr/identifier.rs b/crates/oxide_emit/src/expr/identifier.rs index 5c338baf..a44da956 100644 --- a/crates/oxide_emit/src/expr/identifier.rs +++ b/crates/oxide_emit/src/expr/identifier.rs @@ -34,18 +34,17 @@ impl Emitter { // 捕获集是函数级名字并集:块级 let/const 的 cell 在块退出后仍存留,但名字 // 已不在作用域链内。仅当名字当前可解析为真实词法绑定时才走 cell(隐式全局 // 登记不算),否则落下方全局解析(未声明读经 LOAD_GLOBAL 抛 ReferenceError), - // 不得按名误读已失效的 cell。 - if let Some(&cell_idx) = ctx.captured_bindings.get(name) { - if let Some(binding_reg) = ctx.visible_binding_reg(name) { - let r = ctx.alloc_reg(); - ctx.inst(Inst::new( - OpCode::CELL_GET, - Operand::Reg(r), - Operand::Reg(binding_reg), - Operand::Imm(cell_idx as u16), - )); - return Ok(r); - } + // 不得按名误读已失效的 cell。索引按绑定实例取(`visible_cell` 已含可见性 + // 判定),同名多绑定各读各的 cell。 + if let Some((cell_idx, binding_reg)) = ctx.visible_cell(name) { + let r = ctx.alloc_reg(); + ctx.inst(Inst::new( + OpCode::CELL_GET, + Operand::Reg(r), + Operand::Reg(binding_reg), + Operand::Imm(cell_idx as u16), + )); + return Ok(r); } let var_reg = match ctx.lookup_or_builtin(name) { @@ -117,29 +116,16 @@ impl Emitter { Operand::Imm(uv_idx as u16), Operand::None, )); - } else if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + } else if let Some((cell_idx, binding_reg)) = ctx.visible_cell(name) { // 回退分支的捕获 cell 同样只在名字当前可解析为真实词法绑定时才有效; // 否则与静态解析一致:顶层 tier 名读全局对象属性,其余读 undefined // (with 回退不抛未解析引用)。 - if let Some(binding_reg) = ctx.visible_binding_reg(name) { - ctx.inst(Inst::new( - OpCode::CELL_GET, - Operand::Reg(result_reg), - Operand::Reg(binding_reg), - Operand::Imm(cell_idx as u16), - )); - } else if self.is_global_tier_name(ctx, name) { - let key_idx = ctx.add_constant(Constant::String(name.to_string())); - ctx.inst(Inst::new( - OpCode::LOAD_GLOBAL, - Operand::Reg(result_reg), - Operand::Const(key_idx), - Operand::None, - )); - } else { - let undef_idx = ctx.add_constant(Constant::Undefined); - ctx.inst(Inst::load_const(Operand::Reg(result_reg), undef_idx)); - } + ctx.inst(Inst::new( + OpCode::CELL_GET, + Operand::Reg(result_reg), + Operand::Reg(binding_reg), + Operand::Imm(cell_idx as u16), + )); } else if self.is_global_tier_name(ctx, name) { // 顶层已声明 var:with 对象无该属性时回退读全局对象属性(顶层 var 的唯一 // 存储,引擎侧不保留镜像副本)。 @@ -229,17 +215,16 @@ impl Emitter { // 目标若是被捕获 cell,走 CELL_SET;仅当名字当前可解析为真实词法绑定时才写 // cell——捕获集按名保留的块级绑定在块退出后不可解析(隐式全局登记不算), // 须落下方全局写(sloppy 物化隐式全局属性,strict 抛 ReferenceError)。 - 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), - )); - self.emit_module_write_through(name, val_reg, ctx)?; - return Ok(()); - } + // 索引按绑定实例取(`visible_cell` 已含可见性判定),同名多绑定各写各的 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), + )); + self.emit_module_write_through(name, val_reg, ctx)?; + return Ok(()); } let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { diff --git a/crates/oxide_emit/src/expr/operator/mod.rs b/crates/oxide_emit/src/expr/operator/mod.rs index 51f05d65..aa798598 100644 --- a/crates/oxide_emit/src/expr/operator/mod.rs +++ b/crates/oxide_emit/src/expr/operator/mod.rs @@ -629,7 +629,6 @@ impl Emitter { self.emit_const_write_guard(name, ctx)?; } 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 的 let/const // 循环变量每迭代新分配一个 cell,update 写寄存器供其拷入,不污染本迭代 // 闭包捕获的 cell(机制见 `compile_ctx.rs` 的 `register_update_names` 字段文档)。 @@ -731,25 +730,16 @@ impl Emitter { )); self.emit_module_write_through(name, new_reg, ctx)?; Ok(if update.prefix { new_reg } else { old_reg }) - } else if let Some(cell_idx) = captured_cell { + } else if let Some((cell_idx, binding_reg)) = ctx.visible_cell(name) { // 被捕获 cell:CELL_GET 旧值 + 常量 1 + ADD/SUB + CELL_SET。 // 后缀形式须保留旧值(结果寄存器),前缀返回新值。 let old_reg = ctx.alloc_reg(); - if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) { - ctx.inst(Inst::new( - OpCode::CELL_GET, - Operand::Reg(old_reg), - Operand::Reg(binding.reg), - Operand::Imm(cell_idx as u16), - )); - } else { - ctx.inst(Inst::new( - OpCode::CELL_GET, - Operand::Reg(old_reg), - Operand::None, - Operand::Imm(cell_idx as u16), - )); - } + ctx.inst(Inst::new( + OpCode::CELL_GET, + Operand::Reg(old_reg), + Operand::Reg(binding_reg), + Operand::Imm(cell_idx as u16), + )); let one_idx = ctx.add_constant(Constant::Int(1)); let one_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); diff --git a/crates/oxide_emit/src/function_body.rs b/crates/oxide_emit/src/function_body.rs index c6dbfaf8..e018254f 100644 --- a/crates/oxide_emit/src/function_body.rs +++ b/crates/oxide_emit/src/function_body.rs @@ -365,6 +365,9 @@ impl Emitter { is_const: binding.is_const, lexical: binding.lexical, predeclared: false, + // 父层 cell 索引在子帧无效:子帧经 upvalue 路径访问父捕获 + // 绑定,不复制索引(复制会让解析点误发子帧 cell 表读)。 + cell_idx: None, }, ); inherited_reg_start = inherited_reg_start.max(binding.reg.saturating_add(1)); @@ -390,6 +393,7 @@ impl Emitter { // 类 this 自绑定非用户词法声明,不置位。 lexical: false, predeclared: false, + cell_idx: None, }, ); inherited_reg_start = inherited_reg_start.max(reg.saturating_add(1)); @@ -444,6 +448,10 @@ impl Emitter { } } + // 函数级绑定 cell 索引回填:预声明完成后函数级绑定已齐,捕获集的名字 + // 排序索引写入各绑定实例,解析点据此按绑定身份取索引。 + ctx.backfill_captured_cell_idxs(); + // `var` 与块级函数外层 var 绑定入口实例化(非捕获名):预声明只登记槽位、 // 不发射定义指令,声明点对已绑定名又跳过 undefined 写,缺失入口写会让首次 // 写入前的读取取到调用方遗留的寄存器值。捕获名已由参数 prologue 的 @@ -635,7 +643,7 @@ impl Emitter { // 字段初始化表达式(值表达式)与参数默认值一并纳入捕获分析。 let mut capture_exprs: Vec<&oxide_parser::Expression> = param_defaults.clone(); capture_exprs.extend_from_slice(extra_capture_exprs); - ctx.captured_bindings = collect_captured_bindings(body_stmts, &capture_exprs, &ctx.own_bindings); + ctx.set_captured_bindings(collect_captured_bindings(body_stmts, &capture_exprs, &ctx.own_bindings)); // 自由变量分析:收集 upvalue 捕获(类方法也是普通函数,可捕获外层变量)。 if matches!( body_context, diff --git a/crates/oxide_emit/src/module.rs b/crates/oxide_emit/src/module.rs index 41e005e5..39db5521 100644 --- a/crates/oxide_emit/src/module.rs +++ b/crates/oxide_emit/src/module.rs @@ -449,7 +449,9 @@ impl Emitter { // 闭包捕获分析(import 绑定名纳入 own_bindings,供嵌套函数 cell 捕获)。 ctx.own_bindings = collect_own_binding_names(&[], body); - ctx.captured_bindings = collect_captured_bindings(body, &[], &ctx.own_bindings); + ctx.set_captured_bindings(collect_captured_bindings(body, &[], &ctx.own_bindings)); + // 模块顶层函数级绑定 cell 索引回填:预声明已完成,绑定已齐。 + ctx.backfill_captured_cell_idxs(); // —— 模块命名空间对象 —— let ns_reg = self.emit_module_call(ctx, "__moduleObject", &[])?; diff --git a/crates/oxide_emit/src/program.rs b/crates/oxide_emit/src/program.rs index 014a54c5..2bf4dfbc 100644 --- a/crates/oxide_emit/src/program.rs +++ b/crates/oxide_emit/src/program.rs @@ -349,8 +349,10 @@ impl Emitter { } } ctx.global_tier_names = tier_names; - ctx.captured_bindings = collect_captured_bindings(&program.body, &[], &ctx.own_bindings); + ctx.set_captured_bindings(collect_captured_bindings(&program.body, &[], &ctx.own_bindings)); ctx.captured_bindings.retain(|n, _| !ctx.global_tier_names.contains(n)); + // 顶层函数级绑定 cell 索引回填:预声明已完成,绑定已齐。 + ctx.backfill_captured_cell_idxs(); // 顶层 var 入口实例化:被捕获的 var 名统一 MAKE_CELL(undefined),使 var // 声明语句执行前创建的闭包读取到 undefined(脚本 GlobalDeclarationInstantiation diff --git a/crates/oxide_emit/src/shared/binding.rs b/crates/oxide_emit/src/shared/binding.rs index de17c526..4ef121da 100644 --- a/crates/oxide_emit/src/shared/binding.rs +++ b/crates/oxide_emit/src/shared/binding.rs @@ -86,7 +86,9 @@ impl Emitter { } return Ok(()); } - if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + // 索引按绑定实例取(`visible_cell` 已含可见性判定):函数级绑定走回填 + // 索引,绑定未分配索引时回退捕获映射(for 头覆盖等容器面)。 + 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, Operand::Reg(src_reg), Operand::Imm(cell_idx as u16), Operand::None)); // C 风格 for 头绑定:cell 之外补写绑定寄存器——循环体/test/update 读寄存器, @@ -158,18 +160,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(src_reg), - Operand::Imm(cell_idx as u16), - )); - self.emit_module_write_through(name, src_reg, ctx)?; - return Ok(()); - } + // cell,否则落下方全局解析(未声明名不建隐式全局登记)。索引按绑定实例 + // 取(`visible_cell` 已含可见性判定),同名多绑定各写各的 cell。 + if let Some((cell_idx, _)) = ctx.visible_cell(name) { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(src_reg), + Operand::Imm(cell_idx as u16), + )); + self.emit_module_write_through(name, src_reg, ctx)?; + return Ok(()); } let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { @@ -391,18 +392,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(src_reg), - Operand::Imm(cell_idx as u16), - )); - self.emit_module_write_through(name, src_reg, ctx)?; - return Ok(()); - } + // cell,否则落下方全局解析(未声明名不建隐式全局登记)。索引按绑定实例 + // 取(`visible_cell` 已含可见性判定),同名多绑定各写各的 cell。 + if let Some((cell_idx, _)) = ctx.visible_cell(name) { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(src_reg), + Operand::Imm(cell_idx as u16), + )); + self.emit_module_write_through(name, src_reg, ctx)?; + return Ok(()); } let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { @@ -511,18 +511,17 @@ impl Emitter { continue; } // 目标若是被捕获 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(prop_reg), - Operand::Imm(cell_idx as u16), - )); - self.emit_module_write_through(name, prop_reg, ctx)?; - continue; - } + // cell,否则落下方全局解析(未声明名不建隐式全局登记)。索引按绑定实例 + // 取(`visible_cell` 已含可见性判定),同名多绑定各写各的 cell。 + if let Some((cell_idx, _)) = ctx.visible_cell(name) { + ctx.inst(Inst::new( + OpCode::CELL_SET, + Operand::None, + Operand::Reg(prop_reg), + Operand::Imm(cell_idx as u16), + )); + self.emit_module_write_through(name, prop_reg, ctx)?; + continue; } let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { diff --git a/crates/oxide_emit/src/symbol_table.rs b/crates/oxide_emit/src/symbol_table.rs index 20a6fc63..7b88c61e 100644 --- a/crates/oxide_emit/src/symbol_table.rs +++ b/crates/oxide_emit/src/symbol_table.rs @@ -30,6 +30,10 @@ pub(crate) struct Binding { /// 由块/函数级预声明(TDZ 占位)创建,声明点据此复用槽位。 /// 非预声明的同名绑定(如同 scope 的参数/var)不计,避免误复用。 pub(crate) predeclared: bool, + /// 该绑定实例的闭包捕获 cell 索引:`None` 表示未被捕获(无 cell)。 + /// 函数级绑定由捕获分析的名字排序分配回填;块级遮蔽绑定在预声明点 + /// 追加新索引。解析点按绑定实例取索引,同名多绑定各持各的 cell。 + pub(crate) cell_idx: Option, } /// 作用域符号表:名字 → 寄存器号/初始化状态/const 标志。 @@ -115,6 +119,7 @@ impl SymbolTable { is_const: matches!(kind, VariableDeclarationKind::Const) || is_const, lexical: !matches!(kind, VariableDeclarationKind::Var), predeclared: false, + cell_idx: None, }, ); Ok(()) @@ -151,6 +156,17 @@ impl SymbolTable { None } + /// 给最内层同名可见绑定写入 cell 索引(函数级回填与块级追加共用)。 + /// 名字未在任何作用域声明时静默跳过。 + pub(crate) fn set_binding_cell_idx(&mut self, name: &str, idx: u8) { + for scope in self.scopes.iter_mut().rev() { + if let Some(b) = scope.bindings.get_mut(name) { + b.cell_idx = Some(idx); + return; + } + } + } + /// 消费当前(最内层)作用域中由预声明创建的绑定槽:存在且 `predeclared` /// 时返回其寄存器并清除标志(声明点复用后不再视作预声明);否则返回 None。 /// 同 scope 的非预声明绑定(参数/var/未推 scope 的 try 内声明)不计,避免误复用。 @@ -179,6 +195,7 @@ impl SymbolTable { is_const: false, lexical: false, predeclared: false, + cell_idx: None, }, ); reg_for_new @@ -220,6 +237,7 @@ impl SymbolTable { is_const: matches!(kind, VariableDeclarationKind::Const) || is_const, lexical: !matches!(kind, VariableDeclarationKind::Var), predeclared: false, + cell_idx: None, }, ); Ok(()) @@ -233,6 +251,7 @@ impl SymbolTable { is_const: false, lexical: false, predeclared: false, + cell_idx: None, }); } @@ -258,6 +277,7 @@ impl SymbolTable { is_const: matches!(kind, VariableDeclarationKind::Const) || is_const, lexical: !matches!(kind, VariableDeclarationKind::Var), predeclared: true, + cell_idx: None, }, ); Ok(()) @@ -318,6 +338,7 @@ impl SymbolTable { // 导入绑定是模块作用域词法(const)绑定,同 let/const 置位。 lexical: true, predeclared: false, + cell_idx: None, }, ); self.aliases.insert(local.to_string(), base); @@ -504,4 +525,26 @@ mod tests { let mut st = SymbolTable::new(); assert!(st.add_alias("y", "missing").is_err()); } + + #[test] + fn set_binding_cell_idx_innermost_first() { + let mut st = SymbolTable::new(); + st.declare("x", 0, l(), false).unwrap(); + st.push_scope(); + st.declare("x", 1, l(), false).unwrap(); + st.set_binding_cell_idx("x", 3); + let (inner, _) = st.lookup_any_binding("x").unwrap(); + assert_eq!(inner.cell_idx, Some(3)); + st.pop_scope(); + // 外层绑定不受内层写入影响:同名多绑定各持各的 cell 索引。 + let (outer, _) = st.lookup_any_binding("x").unwrap(); + assert_eq!(outer.cell_idx, None); + } + + #[test] + fn set_binding_cell_idx_missing_name_noop() { + let mut st = SymbolTable::new(); + st.set_binding_cell_idx("missing", 0); + assert!(st.lookup_any_binding("missing").is_none()); + } }