feat: 捕获 cell 换键地基——绑定增 cell_idx 并解析点按绑定身份取索引

This commit is contained in:
2026-10-05 12:01:49 +00:00
parent a1cbb82fb6
commit 5239ff74b8
11 changed files with 214 additions and 137 deletions
+61 -3
View File
@@ -94,6 +94,10 @@ pub struct CompileCtx {
/// 捕获判断(MAKE_CELL / CELL_GET / CELL_SET)与子函数 upvalue cell_idx 统一查此映射,
/// 消除符号表时序依赖与 cell 索引错位。
pub(crate) captured_bindings: BTreeMap<String, u8>,
/// 下一可用 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<String, u8>) {
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,
+5 -15
View File
@@ -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,
+28 -43
View File
@@ -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) {
+7 -17
View File
@@ -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));
+9 -1
View File
@@ -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,
+3 -1
View File
@@ -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", &[])?;
+3 -1
View File
@@ -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
+36 -37
View File
@@ -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) {
+43
View File
@@ -30,6 +30,10 @@ pub(crate) struct Binding {
/// 由块/函数级预声明(TDZ 占位)创建,声明点据此复用槽位。
/// 非预声明的同名绑定(如同 scope 的参数/var)不计,避免误复用。
pub(crate) predeclared: bool,
/// 该绑定实例的闭包捕获 cell 索引:`None` 表示未被捕获(无 cell)。
/// 函数级绑定由捕获分析的名字排序分配回填;块级遮蔽绑定在预声明点
/// 追加新索引。解析点按绑定实例取索引,同名多绑定各持各的 cell。
pub(crate) cell_idx: Option<u8>,
}
/// 作用域符号表:名字 → 寄存器号/初始化状态/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());
}
}