fix: 模块写穿按绑定槽过滤并覆盖更新赋值路径

This commit is contained in:
2026-09-18 00:32:12 +00:00
parent f53918f287
commit 39183f449c
6 changed files with 82 additions and 15 deletions
+6 -5
View File
@@ -137,10 +137,11 @@ pub struct CompileCtx {
pub(crate) for_head_store_registers: HashSet<String>,
/// 模块编译上下文:当前模块命名空间对象寄存器(`__moduleObject` 返回值)。
pub(crate) module_ns_reg: Option<u32>,
/// 写穿反演表:本模块源绑定名 → 引用它的导出名集合。仅自导入 ns 的 live 模块
/// 填充;顶层对该源绑定的赋值据此同步命名空间条目。嵌套函数 ctx 为新建,
/// 不继承本字段,写穿天然只作用于模块顶层。
pub(crate) module_local_exports: HashMap<String, Vec<String>>,
/// 写穿反演表:本模块源绑定槽寄存器 → 引用它的导出名集合。仅自导入 ns 的
/// live 模块填充;顶层赋值按解析后的绑定槽命中此表时同步命名空间条目。
/// 以槽位而非名字为键,块级/catch 同名遮蔽解析到不同槽位,天然不误触发。
/// 嵌套函数 ctx 为新建,不继承本字段,写穿只作用于模块顶层。
pub(crate) module_local_export_regs: HashMap<u32, Vec<String>>,
/// 已求值依赖模块的命名空间对象寄存器(按 import/export source 字符串索引)。
pub(crate) module_dep_ns_regs: HashMap<String, u32>,
/// 依赖模块规范路径(按 import/export source 字符串索引):再导出来源身份用。
@@ -235,7 +236,7 @@ impl CompileCtx {
register_update_names: Vec::new(),
for_head_store_registers: HashSet::new(),
module_ns_reg: None,
module_local_exports: HashMap::new(),
module_local_export_regs: HashMap::new(),
module_dep_ns_regs: HashMap::new(),
module_dep_paths: HashMap::new(),
module_import_origins: HashMap::new(),
+2
View File
@@ -474,6 +474,8 @@ impl Emitter {
}
}
}
// 逻辑赋值仅在短路通过后到达此处:写穿同步命名空间条目。
self.emit_module_write_through(name, val_reg, ctx)?;
ctx.inst(Inst::new(
OpCode::LOAD_VAR,
Operand::Reg(result_reg),
@@ -674,6 +674,7 @@ impl Emitter {
Operand::Reg(new_reg),
Operand::Imm(uv as u16),
));
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 {
// 被捕获 cell:CELL_GET 旧值 + 常量 1 + ADD/SUB + CELL_SET。
@@ -710,6 +711,7 @@ impl Emitter {
Operand::Reg(new_reg),
Operand::Imm(cell_idx as u16),
));
self.emit_module_write_through(name, new_reg, ctx)?;
Ok(if update.prefix { new_reg } else { old_reg })
} else {
let var_reg = ctx.lookup_or_global(name);
@@ -766,6 +768,9 @@ impl Emitter {
// 可写内置名:RMW 新值同步落全局对象属性。
self.emit_global_put_write(name, var_reg, ctx);
}
// 槽位寄存器 rd 承载更新后的新值:后缀形式 result_reg 保留旧值,
// 写穿必须取 var_reg,否则命名空间条目被写回旧值。
self.emit_module_write_through(name, var_reg, ctx)?;
Ok(result_reg)
}
}
+16 -10
View File
@@ -496,18 +496,19 @@ impl Emitter {
}
}
// —— 写穿反演:源绑定名 → 导出名集合。仅 live 模块(自导入 ns)填充:
// 非 live 模块不激活写穿,编译产物逐字节零 IR 变化。按名排序保证多导出名
// 映射到同一源绑定时的发射顺序确定。——
// —— 写穿反演:源绑定槽寄存器 → 导出名集合。仅 live 模块(自导入 ns)填充:
// 非 live 模块不激活写穿,编译产物逐字节零 IR 变化。以槽位为键使块级/catch
// 同名遮蔽解析到内层槽时不命中;按名排序保证多导出名映射到同一源绑定的
// 发射顺序确定。——
if has_self_ns_import {
let mut pairs: Vec<(&String, &String)> =
export_name_map.iter().map(|(exported, source)| (source, exported)).collect();
pairs.sort();
for (source, exported) in pairs {
ctx.module_local_exports
.entry(source.clone())
.or_default()
.push(exported.clone());
let Some(reg) = ctx.scopes.symbols.lookup_any(source) else {
continue;
};
ctx.module_local_export_regs.entry(reg).or_default().push(exported.clone());
}
}
@@ -710,8 +711,10 @@ impl Emitter {
/// 顶层对导出源绑定的写入同步到命名空间:按反演表把新值写入每个导出名。
///
/// # 边界与前提
/// - 仅在模块顶层 live 模块激活:`module_local_exports` 只由自导入 ns 模块填充,
/// 嵌套函数 ctx 不继承该表,行为自然为零;`name` 非导出源时直接返回。
/// - 仅在模块顶层 live 模块激活:`module_local_export_regs` 只由自导入 ns 模块
/// 填充,嵌套函数 ctx 不继承该表,行为自然为零。
/// - `name` 按当前作用域解析到绑定槽,槽不在表内(未声明名、块级/catch 同名
/// 遮蔽等)时直接返回,不误写同名导出。
/// - 调用方保证 `value_reg` 是写入后的当前值;const 抛错分支不得调用本函数。
///
/// # 副作用
@@ -722,7 +725,10 @@ impl Emitter {
let Some(ns_reg) = ctx.module_ns_reg else {
return Ok(());
};
let Some(export_names) = ctx.module_local_exports.get(name).cloned() else {
let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) else {
return Ok(());
};
let Some(export_names) = ctx.module_local_export_regs.get(&binding.reg).cloned() else {
return Ok(());
};
for export_name in export_names {
+6
View File
@@ -179,6 +179,8 @@ impl Emitter {
// 可写内置名:值同步落全局对象属性。
self.emit_global_put_write(name, var_reg, ctx);
}
// live 模块顶层解构赋值目标同样写穿命名空间条目。
self.emit_module_write_through(name, src_reg, ctx)?;
Ok(())
}
AssignmentTarget::ArrayAssignmentTarget(ap) => self.emit_array_assignment(ap, src_reg, ctx),
@@ -390,6 +392,8 @@ impl Emitter {
// 可写内置名:值同步落全局对象属性。
self.emit_global_put_write(name, var_reg, ctx);
}
// live 模块顶层解构赋值目标同样写穿命名空间条目。
self.emit_module_write_through(name, src_reg, ctx)?;
Ok(())
}
_ => Err("assignment target not supported".into()),
@@ -483,6 +487,8 @@ impl Emitter {
// 可写内置名:值同步落全局对象属性(其余属性继续赋值)。
self.emit_global_put_write(name, var_reg, ctx);
}
// live 模块顶层解构赋值目标同样写穿命名空间条目。
self.emit_module_write_through(name, prop_reg, ctx)?;
}
AssignmentTargetProperty::AssignmentTargetPropertyProperty(prop) => {
let (prop_reg, static_key, key_reg) =