feat: 捕获 cell 按绑定身份换键

This commit is contained in:
2026-10-05 14:32:42 +00:00
parent 5239ff74b8
commit 34e22c5519
12 changed files with 316 additions and 51 deletions
Generated
+18 -18
View File
@@ -1095,7 +1095,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"chrono",
"fast-float",
@@ -1120,7 +1120,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1128,7 +1128,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1139,7 +1139,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1159,7 +1159,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1168,7 +1168,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1187,7 +1187,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1201,7 +1201,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1216,7 +1216,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1225,7 +1225,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"bumpalo",
"dashmap",
@@ -1240,7 +1240,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1252,7 +1252,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"tracing",
"tracing-appender",
@@ -1261,7 +1261,7 @@ dependencies = [
[[package]]
name = "oxide_parser"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1276,7 +1276,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1292,7 +1292,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"num-bigint",
"num-traits",
@@ -1304,7 +1304,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"bumpalo",
"hashbrown 0.14.5",
@@ -1333,7 +1333,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.8.7"
version = "0.8.8"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -22,7 +22,7 @@ members = [
]
[workspace.package]
version = "0.8.7"
version = "0.8.8"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+1
View File
@@ -16,6 +16,7 @@ mod upvalues;
use std::collections::HashSet;
pub(crate) use captured::collect_captured_bindings;
pub(crate) use names::collect_block_level_decl_names;
pub(crate) use names::collect_direct_lexical_names;
pub(crate) use names::collect_own_binding_names;
pub(crate) use names::collect_top_level_function_names;
+110
View File
@@ -181,6 +181,116 @@ pub(crate) fn collect_direct_lexical_names(stmts: &[Statement]) -> HashSet<Strin
names
}
/// 收集块级声明名(块内 let/const/class、catch 参数、for 头 let/const):
/// 这些名字是块作用域绑定,遮蔽同名顶层 tier 名时,tier 名须保留在捕获集,
/// 块级绑定才能追加新索引供闭包捕获。顶层直接子级 let/const/class 是顶层
/// 词法绑定,不纳;var 声明不纳(var 是函数/全局作用域)。
pub(crate) fn collect_block_level_decl_names(stmts: &[Statement]) -> HashSet<String> {
let mut names = HashSet::new();
collect_block_level_names_stmt(stmts, &mut names, true);
names
}
/// 递归遍历语句树收集块级声明名。`top` 标记当前层是否为顶层(顶层直接子级
/// let/const/class 是顶层词法绑定,非块级)。
fn collect_block_level_names_stmt(stmts: &[Statement], out: &mut HashSet<String>, top: bool) {
for stmt in stmts {
match stmt {
Statement::VariableDeclaration(vd) => {
if matches!(vd.kind, VariableDeclarationKind::Var) || top {
continue;
}
for d in &vd.declarations {
collect_binding_pattern_names(&d.id, out);
}
}
Statement::ClassDeclaration(cd) => {
if !top {
if let Some(id) = &cd.id {
out.insert(id.name.to_string());
}
}
}
Statement::BlockStatement(b) => collect_block_level_names_stmt(&b.body, out, false),
Statement::IfStatement(is) => {
collect_block_level_names_stmt(std::slice::from_ref(&is.consequent), out, false);
if let Some(alt) = &is.alternate {
collect_block_level_names_stmt(std::slice::from_ref(alt), out, false);
}
}
Statement::WhileStatement(w) => collect_block_level_names_stmt(std::slice::from_ref(&w.body), out, false),
Statement::DoWhileStatement(d) => collect_block_level_names_stmt(std::slice::from_ref(&d.body), out, false),
Statement::ForStatement(f) => {
if let Some(oxide_parser::ForStatementInit::VariableDeclaration(vd)) = &f.init {
if !matches!(vd.kind, VariableDeclarationKind::Var) {
for d in &vd.declarations {
collect_binding_pattern_names(&d.id, out);
}
}
}
collect_block_level_names_stmt(std::slice::from_ref(&f.body), out, false);
}
Statement::ForInStatement(fi) => {
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fi.left {
if !matches!(vd.kind, VariableDeclarationKind::Var) {
collect_for_left_decl_names(&fi.left, out);
}
}
collect_block_level_names_stmt(std::slice::from_ref(&fi.body), out, false);
}
Statement::ForOfStatement(fo) => {
if let oxide_parser::ForStatementLeft::VariableDeclaration(vd) = &fo.left {
if !matches!(vd.kind, VariableDeclarationKind::Var) {
collect_for_left_decl_names(&fo.left, out);
}
}
collect_block_level_names_stmt(std::slice::from_ref(&fo.body), out, false);
}
Statement::SwitchStatement(sw) => {
for case in &sw.cases {
collect_block_level_names_stmt(&case.consequent, out, false);
}
}
Statement::TryStatement(ts) => {
collect_block_level_names_stmt(&ts.block.body, out, false);
if let Some(h) = &ts.handler {
if let Some(param) = &h.param {
collect_binding_pattern_names(&param.pattern, out);
}
collect_block_level_names_stmt(&h.body.body, out, false);
}
if let Some(f) = &ts.finalizer {
collect_block_level_names_stmt(&f.body, out, false);
}
}
Statement::LabeledStatement(ls) => {
collect_block_level_names_stmt(std::slice::from_ref(&ls.body), out, false)
}
Statement::WithStatement(ws) => collect_block_level_names_stmt(std::slice::from_ref(&ws.body), out, false),
Statement::ExportNamedDeclaration(exp) => {
if let Some(decl) = &exp.declaration {
match decl {
oxide_parser::Declaration::VariableDeclaration(vd) => {
if !matches!(vd.kind, VariableDeclarationKind::Var) && !top {
for d in &vd.declarations {
collect_binding_pattern_names(&d.id, out);
}
}
}
oxide_parser::Declaration::ClassDeclaration(cd) if !top => {
if let Some(id) = &cd.id {
out.insert(id.name.to_string());
}
}
_ => {}
}
}
}
_ => {}
}
}
}
/// 收集当前函数作用域声明的绑定名(参数 + 变量/函数声明,含嵌套 block,不含嵌套函数体)。
pub(crate) fn collect_own_binding_names(param_names: &[&str], stmts: &[Statement]) -> HashSet<String> {
let mut names = HashSet::new();
+74 -11
View File
@@ -95,9 +95,13 @@ pub struct CompileCtx {
/// 消除符号表时序依赖与 cell 索引错位。
pub(crate) captured_bindings: BTreeMap<String, u8>,
/// 下一可用 cell 索引(计数器):捕获分析后初始化为名字集大小(函数级索引
/// 0..n-1 占满),块级遮蔽绑定预声明追加与合成 cell 经 `alloc_cell_idx`
/// 递增取号,保证与函数级名字排序索引及历次追加互不碰撞。
/// 0..n-1 占满),块级遮蔽绑定预声明追加、catch 参数就地声明与合成 cell 经
/// `alloc_cell_idx` 递增取号,保证与函数级名字排序索引及历次追加互不碰撞。
pub(crate) next_cell_idx: u8,
/// 词法循环头(C-for / for-in / for-of)名集:头名索引由 ForHeadEnv 覆盖
/// 捕获映射分配(TDZ 占位与体区 fresh cell),声明点据此走映射回退而非
/// 就地追加新索引。begin 时并入、restore 时移除,嵌套循环成对。
pub(crate) for_head_env_names: HashSet<String>,
/// 正在发射的 C 风格 for 头 let/const 声明名(含解构叶)。头名不像块级
/// let/const 那样在块入口预声明,init 表达式内创建的嵌套函数引用同头尚未
/// declare 的前向名时,父捕获可见性过滤据此放行;init 发射完毕后移除,
@@ -253,6 +257,7 @@ impl CompileCtx {
block_fn_entry_mats: Vec::new(),
captured_bindings: BTreeMap::new(),
next_cell_idx: 0,
for_head_env_names: HashSet::new(),
pending_for_head_names: HashSet::new(),
global_tier_names: HashSet::new(),
upvalue_const_flags: HashSet::new(),
@@ -426,13 +431,27 @@ impl CompileCtx {
/// 登记)时返回 (cell_idx, binding_reg)。索引优先取绑定自身字段(函数级
/// 回填与块级追加的真源),绑定未分配索引时回退捕获映射(for 头覆盖 /
/// catch 参数等容器面,后续子任务逐面收编)。块级遮蔽绑定在块退出后不可
/// 解析,返回 None(落全局解析),不得按名误读已失效 cell。
/// 解析,返回 None(落全局解析),不得按名误读已失效 cell。tier 绑定
/// (scope 0 顶层 var/函数)的全局对象属性是唯一存储,不建 cell;映射中
/// 被同名块级绑定保留的 entry 不属该 tier 绑定,回退不得命中——for 头
/// 前向名除外(头绑定未 declare,映射值是 ForHeadEnv 的 TDZ 索引)。
pub(crate) fn visible_cell(&self, name: &str) -> Option<(u8, u32)> {
let (binding, _) = self.scopes.symbols.lookup_any_binding(name)?;
let (binding, scope_idx) = 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())?;
// for 头前向名(C 风格 for 头 pending / for-in/for-of env)读 TDZ cell:
// 头绑定未 declare,按名回查命中外层同名 tier 绑定,映射回退须放行以命中
// ForHeadEnv 覆盖的 TDZ 索引。
let is_for_head = self.pending_for_head_names.contains(name) || self.for_head_env_names.contains(name);
let is_tier_binding = scope_idx == 0 && self.global_tier_names.contains(name);
let idx = binding.cell_idx.or_else(|| {
if is_tier_binding && !is_for_head {
None
} else {
self.captured_bindings.get(name).copied()
}
})?;
Some((idx, binding.reg))
}
@@ -443,15 +462,54 @@ impl CompileCtx {
self.captured_bindings = map;
}
/// 分配一个新 cell 索引:取当前计数器值并递增。块级遮蔽绑定预声明追加与
/// 合成 cell 共用,分配序由发射序决定,跨 run 稳定。
#[allow(dead_code)] // 块级遮蔽绑定预声明追加(后续子任务)消费,本件仅立计数器
/// 分配一个新 cell 索引:取当前计数器值并递增。块级遮蔽绑定预声明追加、
/// catch 参数就地声明与合成 cell 共用,分配序由发射序决定,跨 run 稳定。
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 索引解析:索引按绑定实例取(绑定自身字段是真源)。绑定未分配
/// 索引时,词法循环头名(ForHeadEnv 覆盖捕获映射)走映射回退;最内层为块
/// 作用域的就地声明(catch 参数)现场追加新索引并写回绑定,与函数级同名
/// 绑定的名字排序索引区分。名字不可见(隐式全局登记)或不在捕获集时返回
/// None,调用方落 with/全局臂。顶层 tier 名(顶层 var/函数)的全局对象属性
/// 是唯一存储,不建 cell,返回 None。
///
/// # 副作用
/// - 就地追加臂写回绑定 cell 索引并递增计数器;指令流不改动。
pub(crate) fn resolve_bind_cell_idx(&mut self, name: &str) -> Option<u8> {
let (existing, is_implicit, block_scope, for_head, is_tier) = {
let symbols = &self.scopes.symbols;
let (binding, scope_idx) = symbols.lookup_any_binding(name)?;
(
binding.cell_idx,
self.is_implicit_global_reg(binding.reg),
symbols.scopes.last().is_some_and(|s| s.kind == ScopeKind::BlockScope),
self.for_head_env_names.contains(name),
self.is_global_scope && self.global_tier_names.contains(name) && scope_idx == 0,
)
};
if is_implicit {
return None;
}
if let Some(idx) = existing {
return Some(idx);
}
if is_tier {
return None;
}
let base = *self.captured_bindings.get(name)?;
if block_scope && !for_head {
let fresh = self.alloc_cell_idx();
self.set_binding_cell_idx(name, fresh);
Some(fresh)
} else {
Some(base)
}
}
/// 给最内层同名可见绑定写入 cell 索引(委托符号表)。
pub(crate) fn set_binding_cell_idx(&mut self, name: &str, idx: u8) {
self.scopes.symbols.set_binding_cell_idx(name, idx);
@@ -459,10 +517,15 @@ impl CompileCtx {
/// 函数级绑定 cell 索引回填:捕获集(名字排序分配)的索引写入对应绑定
/// 实例,使解析点按绑定身份取索引。预声明完成后调用,函数级绑定(参数 /
/// var / 直接子级词法 / 块级函数外层 var / arguments)此时已齐。
/// var / 直接子级词法 / 块级函数外层 var / arguments)此时已齐。顶层 tier
/// 名(顶层 var/函数)的全局对象属性是唯一存储,不回填 cell 索引。
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();
let entries: Vec<(String, u8)> = self
.captured_bindings
.iter()
.filter(|(name, _)| !(self.is_global_scope && self.global_tier_names.contains(*name)))
.map(|(name, &idx)| (name.clone(), idx))
.collect();
for (name, idx) in entries {
self.set_binding_cell_idx(&name, idx);
}
+43 -1
View File
@@ -655,14 +655,56 @@ impl Emitter {
// upvalue 代表的祖父绑定在父函数体内恒可见,不参与过滤。
// C 风格 for 头 let/const 名在 init 内尚未 declare,但同头声明会为其
// 建 cell,前向捕获须按父捕获集保留(见 `pending_for_head_names`)。
// 索引按父可见绑定实例取(块级遮蔽绑定持追加索引);词法循环头名
// (for-in/for-of 头绑定在右值区编译时未进符号表,按名回查会误命中
// 外层同名绑定)与 for 头前向名无条件取捕获映射值(ForHeadEnv 覆盖
// 索引或名字排序索引);绑定未分配索引的其余名(命名空间导入等)
// 回退映射值。顶层 tier 名(顶层 var/函数)已在捕获集剔除阶段出集,
// 不在此列。
let visible_parent_captured: BTreeMap<String, u8> = parent_ctx
.captured_bindings
.iter()
.filter(|(n, _)| {
// for 头前向名(C 风格 for 头 pending / for-in/for-of env):
// 头绑定未 declare,按名回查命中外层同名 tier 绑定,映射值是
// ForHeadEnv 的 TDZ 索引,须保留为 upvalue。
let for_head = parent_ctx.pending_for_head_names.contains(n.as_str())
|| parent_ctx.for_head_env_names.contains(n.as_str());
// tier 绑定(scope 0 顶层 var/函数):全局对象属性是唯一存储,
// 不建 cell;嵌套函数直读全局,不入 upvalue。映射中为同名块级
// 绑定保留的 entry 不属该 tier 绑定,不得按名捕获。
let tier =
!for_head
&& parent_ctx.scopes.symbols.lookup_any_binding(n.as_str()).is_some_and(
|(_, scope_idx)| scope_idx == 0 && parent_ctx.global_tier_names.contains(n.as_str()),
);
if tier {
return false;
}
parent_ctx.visible_binding_reg(n.as_str()).is_some()
|| parent_ctx.pending_for_head_names.contains(n.as_str())
})
.map(|(n, &i)| (n.clone(), i))
.map(|(n, &i)| {
let idx = if parent_ctx.for_head_env_names.contains(n.as_str())
|| parent_ctx.pending_for_head_names.contains(n.as_str())
{
// 词法循环头名:头绑定在右值区编译时未进符号表(按名回查
// 会误命中外层同名绑定),索引由 ForHeadEnv 覆盖映射分配,
// 无条件取映射值。
i
} else {
// 其余名按绑定实例取索引(块级遮蔽绑定持追加索引);
// 绑定未分配索引(命名空间导入等绑定点走映射值不写绑定
// 字段)回退映射值,与基线按名取索引同口径。
parent_ctx
.scopes
.symbols
.lookup_any_binding(n.as_str())
.and_then(|(b, _)| b.cell_idx)
.unwrap_or(i)
};
(n.clone(), idx)
})
.collect();
ctx.current_upvalue_captures = collect_upvalue_names(
body_stmts,
+27
View File
@@ -1,7 +1,11 @@
//! emit 前置 pass,声明预登记:函数/var 声明提升预声明、块级函数声明按 ES2015
//! 块绑定预声明、let/const/class 建 TDZ 占位。
use crate::symbol_table::ScopeKind;
use crate::{CompileCtx, Emitter};
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
use oxide_parser::{BindingPattern, Declaration, ExportDefaultDeclarationKind, Statement, VariableDeclarationKind};
impl Emitter {
@@ -395,6 +399,29 @@ impl Emitter {
},
is_const,
);
// 块级遮蔽绑定:名字在捕获集且最内层为块作用域时,追加新 cell
// 索引并发未初始化 MAKE_CELL(TDZ 占位)。声明点与闭包捕获据
// 绑定字段走新索引,不原位覆写函数级同名绑定的名字排序索引;
// 函数作用域调用点(函数体/顶层直接子级)名字排序索引归该
// 绑定所有,不追加。
if ctx.captured_bindings.contains_key(bi.name.as_str())
&& ctx
.scopes
.symbols
.scopes
.last()
.is_some_and(|s| s.kind == ScopeKind::BlockScope)
{
let idx = ctx.alloc_cell_idx();
ctx.set_binding_cell_idx(bi.name.as_str(), idx);
let undef_reg = self.emit_undefined(ctx);
ctx.inst(Inst::new(
OpCode::MAKE_CELL,
Operand::Reg(undef_reg),
Operand::Imm(idx as u16 | 0x0100),
Operand::None,
));
}
}
BindingPattern::ArrayPattern(ap) => {
for e in ap.elements.iter().flatten() {
+9 -3
View File
@@ -17,7 +17,7 @@ use oxide_ir::IRFunction;
use oxide_parser::Statement;
use crate::capture::{
collect_captured_bindings, collect_direct_lexical_names, collect_own_binding_names,
collect_block_level_decl_names, collect_captured_bindings, collect_direct_lexical_names, collect_own_binding_names,
collect_top_level_function_names, collect_top_level_function_names_ordered, collect_var_binding_names,
};
use crate::compile_ctx::CompileCtx;
@@ -350,7 +350,12 @@ impl Emitter {
}
ctx.global_tier_names = tier_names;
ctx.set_captured_bindings(collect_captured_bindings(&program.body, &[], &ctx.own_bindings));
ctx.captured_bindings.retain(|n, _| !ctx.global_tier_names.contains(n));
// 顶层 tier 名(顶层 var/函数)的全局对象属性是唯一存储,从捕获集剔除;
// 被同名块级绑定(catch 参数 / 块级 let/const)遮蔽时保留,使块级绑定能
// 追加新索引供闭包捕获(顶层 var 本身不建 cell,回填与解析点均跳过)。
let block_level_names = collect_block_level_decl_names(&program.body);
ctx.captured_bindings
.retain(|n, _| !ctx.global_tier_names.contains(n) || block_level_names.contains(n));
// 顶层函数级绑定 cell 索引回填:预声明已完成,绑定已齐。
ctx.backfill_captured_cell_idxs();
@@ -358,9 +363,10 @@ impl Emitter {
// 声明语句执行前创建的闭包读取到 undefined(脚本 GlobalDeclarationInstantiation
// 语义),而非占位 cell 的 TDZ 误报。声明语句的 MAKE_CELL 覆盖此初值。
// 名集保持 var-only:函数声明名从不入捕获集(tier 剔除),无需入口 cell。
// tier 名(全局对象属性为唯一存储)不建入口 cell。
let mut var_names: Vec<String> = var_names
.into_iter()
.filter(|n| ctx.captured_bindings.contains_key(n))
.filter(|n| ctx.captured_bindings.contains_key(n) && !ctx.global_tier_names.contains(n))
.collect();
// HashSet 迭代序带随机种子,排序后入口 MAKE_CELL 发射序跨进程稳定。
var_names.sort();
+4 -3
View File
@@ -86,9 +86,10 @@ impl Emitter {
}
return Ok(());
}
// 索引按绑定实例取(`visible_cell` 已含可见性判定):函数级绑定走回填
// 索引,绑定未分配索引时回退捕获映射(for 头覆盖等容器面)。
if let Some((cell_idx, _)) = ctx.visible_cell(name) {
// 索引按绑定实例取(`resolve_bind_cell_idx` 已含可见性判定):函数级
// 绑定走回填索引,块级遮蔽绑定走预声明追加索引,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 };
ctx.inst(Inst::new(op, Operand::Reg(src_reg), Operand::Imm(cell_idx as u16), Operand::None));
// C 风格 for 头绑定:cell 之外补写绑定寄存器——循环体/test/update 读寄存器,
@@ -70,7 +70,7 @@ impl Emitter {
};
(target, false)
};
if let Some(&cell_idx) = ctx.captured_bindings.get(bi.name.as_str()) {
if let Some(cell_idx) = ctx.resolve_bind_cell_idx(bi.name.as_str()) {
// 被捕获绑定的 cell 在函数入口已实例化(undefined);声明语句
// 不赋值,仅首次声明刷新初值,已绑定的 cell 保留现值。
if !already_bound {
+10 -4
View File
@@ -71,8 +71,9 @@ impl Emitter {
/// 3. 发射未初始化 MAKE_CELL 并覆盖映射。
///
/// # 副作用
/// - 捕获映射被改动;须经 `switch_for_head_env_to_body` 与
/// `restore_for_head_env` 成对收尾,防头名泄漏进兄弟语句的捕获映射。
/// - 捕获映射与头名集(`for_head_env_names`)被改动;须经
/// `switch_for_head_env_to_body` 与 `restore_for_head_env` 成对收尾,
/// 防头名泄漏进兄弟语句的捕获判定。
pub(crate) fn begin_for_head_env(&self, names: Vec<String>, ctx: &mut CompileCtx) -> Result<ForHeadEnv, String> {
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));
@@ -81,6 +82,8 @@ impl Emitter {
let tdz_idx = next;
next = next.saturating_add(1);
ctx.captured_bindings.insert(name.clone(), tdz_idx);
// 头名登记:声明点索引解析据此走映射回退,不就地追加新索引。
ctx.for_head_env_names.insert(name.clone());
// MAKE_CELL 的 16 位立即数:低字节是 cell 索引,高字节 bit 0 是
// 未初始化标记。标志折入立即数高字节(0x0100),dispatch 侧按字节
// 拆回两字段。
@@ -93,6 +96,7 @@ impl Emitter {
));
entries.push((name, saved, tdz_idx, 0));
}
ctx.next_cell_idx = next;
Ok(ForHeadEnv { entries })
}
@@ -128,20 +132,22 @@ impl Emitter {
Operand::None,
));
}
ctx.next_cell_idx = next;
}
/// 语句收尾恢复捕获映射:旧条目放回(无旧条目则移除),头名不进入后续
/// 兄弟语句的捕获判定。
/// 兄弟语句的捕获判定;头名集同步移除(嵌套循环成对)。
pub(crate) fn restore_for_head_env(&self, env: ForHeadEnv, ctx: &mut CompileCtx) {
for (name, saved, _, _) in env.entries {
match saved {
Some(old) => {
ctx.captured_bindings.insert(name, old);
ctx.captured_bindings.insert(name.clone(), old);
}
None => {
ctx.captured_bindings.remove(&name);
}
}
ctx.for_head_env_names.remove(&name);
}
}
+18 -9
View File
@@ -250,14 +250,15 @@ impl Vm {
/// 1. 取 a 槽为 cell 下标,按需扩容 `cell_stack` 至可容纳该下标。
/// 2. b 槽为 0 表示已初始化,非 0 为 TDZ(读写抛 ReferenceError,用于 for-in/for-of
/// 词法头环境建模)。
/// 3. 槽位为空时新建 cell;已有 `CREATE_CLOSURE` 建的占位 cell 则原位更新值与标志,
/// 使闭包 upvalue 指向的同一 cell 跟随初始化。
/// 3. 槽位为空时新建 cell;TDZ 占位遇已初始化 cell 恒新建替换(循环体每迭代
/// 重执行不得把上一迭代已被闭包捕获的 cell 原位翻回未初始化);其余情形
/// 原位更新值与标志,使闭包 upvalue 指向的同一 cell 跟随初始化。
///
/// # 边界与前提
/// - `cell_stack` 为空时 `last_mut()` unwrap 失败;调用前须已压入当前帧环境层。
///
/// # 副作用
/// - 写入/更新堆上 `Cell`(可能触发分配),改动闭包 upvalue 可见值。
/// - 写入/更新/替换堆上 `Cell`(可能触发分配),改动闭包 upvalue 可见值。
///
/// # 注意事项
/// - 手写 IR 路径保留的指令,常规编译不发射,故标 `#[allow(dead_code)]`。
@@ -276,12 +277,20 @@ impl Vm {
// 无占位 cell:新建。
current[cell_idx] = self.gc_state.alloc_cell(value, initialized);
} else {
// 更新占位 cell(CREATE_CLOSURE 已建),使闭包 upvalue 指向的
// cell 值跟随初始化;初始化标志随指令 b 槽。
unsafe {
let cell = &mut *current[cell_idx];
cell.value = value;
cell.set_initialized(initialized);
let existing_initialized = unsafe { &*current[cell_idx] }.is_initialized();
if !initialized && existing_initialized {
// TDZ 占位而槽内是已初始化 cell:新建替换。循环体每迭代重执行
// 的 TDZ 占位不得原位翻新迭代 cell(已被本迭代前闭包捕获);
// 已初始化 cell 保持身份,闭包 upvalue 保值。
current[cell_idx] = self.gc_state.alloc_cell(value, initialized);
} else {
// 更新占位 cell(CREATE_CLOSURE 已建),使闭包 upvalue 指向的
// cell 值跟随初始化;初始化标志随指令 b 槽。
unsafe {
let cell = &mut *current[cell_idx];
cell.value = value;
cell.set_initialized(initialized);
}
}
}
Ok(())