feat: 补全 star 与再导出路径的自导入别名回写

This commit is contained in:
2026-10-04 12:57:25 +00:00
parent 50482a09f1
commit a0fe780a6e
4 changed files with 86 additions and 7 deletions
+34
View File
@@ -561,6 +561,40 @@ pub fn module_set_reexport<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::undefined()) NativeResult::Ok(JsValue::undefined())
} }
/// `__moduleAlias(ns, name, fallback)`:自别名收尾刷新用。命名空间存在该导出时
/// 返回其值,否则返回传入的 `fallback`(不抛错):star 未提供该名时占位值原样
/// 保留,与现行「星号未提供该名时静默 undefined」行为一致。
///
/// # 边界与前提
/// - 命名空间无该导出时不抛错(区别于 `__moduleGet` 的 TypeError 与
/// `__moduleLinkGet` 的 SyntaxError),缺失臂返回 `fallback` 原值。
///
/// # 副作用
/// - 无:只读命名空间属性,不修改条目表。
pub fn module_alias<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 4 {
return type_error(vm, "__moduleAlias: 3 arguments required");
}
let ns_val = vm.reg(args[1]);
let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleAlias: target is not an object") {
Ok(Some(p)) => p,
Ok(None) => return type_error(vm, "__moduleAlias: target is not an object"),
Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)),
};
let name_val = vm.reg(args[2]);
let name_si = vm.property_key_si(name_val);
let fallback = vm.reg(args[3]);
let obj = unsafe { &*ns_ptr };
if vm.get_own_property_slot(obj, name_si).is_some() {
match vm.ordinary_get(obj, name_si, ns_val) {
Ok(v) => NativeResult::Ok(v),
Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)),
}
} else {
NativeResult::Ok(fallback)
}
}
/// `__moduleGet(ns, name)`:读取命名空间导出属性。 /// `__moduleGet(ns, name)`:读取命名空间导出属性。
pub fn module_get<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn module_get<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 3 { if args.len() < 3 {
+3 -2
View File
@@ -175,8 +175,9 @@ pub struct CompileCtx {
/// 自导入(import from 自身)的 source 字符串集合:绑定走别名语义,不能链接期快照。 /// 自导入(import from 自身)的 source 字符串集合:绑定走别名语义,不能链接期快照。
pub(crate) module_self_import_specs: HashSet<String>, pub(crate) module_self_import_specs: HashSet<String>,
/// 自导入别名:导出名 → 本地绑定槽寄存器(export 语句执行时回写绑定值)。 /// 自导入别名:导出名 → 本地绑定槽寄存器(export 语句执行时回写绑定值)。
/// 仅承载无法静态解析源绑定的退化占位路径(star 转发的自导入名)。 /// 仅承载无法静态解析源绑定的退化占位路径(star 转发的自导入名);同名多绑定
pub(crate) module_self_aliases: HashMap<String, u32>, /// (如 default 双绑定)经 Vec 并列回写。
pub(crate) module_self_aliases: HashMap<String, Vec<u32>>,
/// 自导入别名(本地名,基源绑定名):供别名捕获后处理合并源/别名 cell。 /// 自导入别名(本地名,基源绑定名):供别名捕获后处理合并源/别名 cell。
pub(crate) module_alias_pairs: Vec<(String, String)>, pub(crate) module_alias_pairs: Vec<(String, String)>,
/// 标签模板 site 计数器:本编译树内全局唯一(子 ctx 继承父值继续递增)。 /// 标签模板 site 计数器:本编译树内全局唯一(子 ctx 继承父值继续递增)。
+48 -5
View File
@@ -567,7 +567,7 @@ impl Emitter {
} }
if is_self { if is_self {
let alias_reg = self.emit_self_alias_placeholder(s.local.name.as_str(), ctx)?; let alias_reg = self.emit_self_alias_placeholder(s.local.name.as_str(), ctx)?;
ctx.module_self_aliases.insert(imported, alias_reg); ctx.module_self_aliases.entry(imported).or_default().push(alias_reg);
} else { } else {
let name_reg = self.load_string_const(&imported, ctx); let name_reg = self.load_string_const(&imported, ctx);
let val_reg = let val_reg =
@@ -619,7 +619,10 @@ impl Emitter {
} }
if is_self { if is_self {
let alias_reg = self.emit_self_alias_placeholder(s.local.name.as_str(), ctx)?; let alias_reg = self.emit_self_alias_placeholder(s.local.name.as_str(), ctx)?;
ctx.module_self_aliases.insert(DEFAULT_EXPORT_NAME.to_string(), alias_reg); ctx.module_self_aliases
.entry(DEFAULT_EXPORT_NAME.to_string())
.or_default()
.push(alias_reg);
} else { } else {
let name_reg = self.load_string_const(DEFAULT_EXPORT_NAME, ctx); let name_reg = self.load_string_const(DEFAULT_EXPORT_NAME, ctx);
let val_reg = let val_reg =
@@ -799,6 +802,10 @@ impl Emitter {
self.emit_statement(stmt, ctx)?; self.emit_statement(stmt, ctx)?;
} }
// —— 自别名收尾刷新:star 复制等跨模块注册路径后占位槽可能仍为旧值,
// 封冻命名空间前统一从命名空间非抛出回读兜底。 ——
self.emit_self_alias_refresh(ctx, ns_reg)?;
// —— 收尾:封冻命名空间并返回(顶层 RETURN 亦终止 run)—— // —— 收尾:封冻命名空间并返回(顶层 RETURN 亦终止 run)——
self.emit_module_call(ctx, "__moduleSeal", &[ns_reg])?; self.emit_module_call(ctx, "__moduleSeal", &[ns_reg])?;
if top_level { if top_level {
@@ -888,9 +895,11 @@ impl Emitter {
} }
/// 自导入别名回写:导出名若被本模块 self-import 绑定别名引用, /// 自导入别名回写:导出名若被本模块 self-import 绑定别名引用,
/// export 语句执行时同步把值写回本地绑定槽。 /// export 语句执行时同步把值写回本地绑定槽。同名多槽(default 双绑定)
/// 逐槽并列回写。
fn emit_self_alias_write(&self, ctx: &mut CompileCtx, export_name: &str, value_reg: u32) { fn emit_self_alias_write(&self, ctx: &mut CompileCtx, export_name: &str, value_reg: u32) {
if let Some(&alias_reg) = ctx.module_self_aliases.get(export_name) { let regs = ctx.module_self_aliases.get(export_name).cloned().unwrap_or_default();
for &alias_reg in &regs {
ctx.inst(Inst::new( ctx.inst(Inst::new(
OpCode::STORE_VAR, OpCode::STORE_VAR,
Operand::Reg(alias_reg), Operand::Reg(alias_reg),
@@ -900,9 +909,33 @@ impl Emitter {
} }
} }
/// 自导入别名统一刷新:对每个登记的自导入占位名,从命名空间非抛出读回写
/// 占位槽(命名空间无该名时保留占位值,不抛错)。star 转发的自导入名在
/// 导出语句处静态不可知,故每次 star 复制后与 body 收尾各刷新一次;
/// 名集按字典序排序后发射,保证发射序确定性。
fn emit_self_alias_refresh(&self, ctx: &mut CompileCtx, ns_reg: u32) -> Result<(), String> {
if ctx.module_self_aliases.is_empty() {
return Ok(());
}
let mut names: Vec<String> = ctx.module_self_aliases.keys().cloned().collect();
names.sort();
for name in names {
let regs = ctx.module_self_aliases.get(&name).cloned().unwrap_or_default();
for &alias_reg in &regs {
let name_reg = self.load_string_const(&name, ctx);
let cur_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(cur_reg), Operand::Reg(alias_reg), Operand::None));
let r = self.emit_module_call(ctx, "__moduleAlias", &[ns_reg, name_reg, cur_reg])?;
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(alias_reg), Operand::Reg(r), Operand::None));
}
}
Ok(())
}
/// 自导入绑定的非别名占位:源绑定无法静态解析(star 转发的自导入名)时, /// 自导入绑定的非别名占位:源绑定无法静态解析(star 转发的自导入名)时,
/// prelude 把局部名绑成已初始化的 undefined 常量槽并登记回写,由 export /// prelude 把局部名绑成已初始化的 undefined 常量槽并登记回写,由 export
/// 语句执行时就地刷新。返回该本地绑定槽寄存器。 /// 语句执行时就地刷新,body 收尾再统一从命名空间回读兜底。
/// 返回该本地绑定槽寄存器。
fn emit_self_alias_placeholder(&self, local: &str, ctx: &mut CompileCtx) -> Result<u32, String> { fn emit_self_alias_placeholder(&self, local: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
let undef_idx = ctx.add_constant(Constant::Undefined); let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg(); let undef_reg = ctx.alloc_reg();
@@ -1132,6 +1165,9 @@ impl Emitter {
&dep_path, &dep_path,
&local_name, &local_name,
)?; )?;
// 自导入名可能来自 star 转发(占位路径):导出语句执行时
// 就地刷新本地槽,覆盖同模块 body 内导出后读取的形态。
self.emit_self_alias_write(ctx, &exported_name, val_reg);
continue; continue;
} }
// 无 source 但局部名是导入绑定:按规范重分类为间接导出,来源为 // 无 source 但局部名是导入绑定:按规范重分类为间接导出,来源为
@@ -1139,6 +1175,8 @@ impl Emitter {
if let Some((dep_path, imported)) = ctx.module_import_origins.get(&local_name).cloned() { if let Some((dep_path, imported)) = ctx.module_import_origins.get(&local_name).cloned() {
let val_reg = self.load_var_reg(&local_name, ctx)?; let val_reg = self.load_var_reg(&local_name, ctx)?;
self.emit_module_set_reexport(ctx, ns_reg, &exported_name, val_reg, &dep_path, &imported)?; self.emit_module_set_reexport(ctx, ns_reg, &exported_name, val_reg, &dep_path, &imported)?;
// 自导入名重分类为间接导出时同样就地刷新占位槽。
self.emit_self_alias_write(ctx, &exported_name, val_reg);
continue; continue;
} }
// 本地导出:来源恒为 Local。 // 本地导出:来源恒为 Local。
@@ -1265,10 +1303,15 @@ impl Emitter {
&dep_path, &dep_path,
MODULE_NAMESPACE_BINDING, MODULE_NAMESPACE_BINDING,
)?; )?;
// 自导入的 star-as 名走占位路径:就地刷新为命名空间对象。
self.emit_self_alias_write(ctx, &exported_name, dep_ns_reg);
} else { } else {
// 来源传播:star 复制按 (dep_path, 绑定) 判同名冲突。 // 来源传播:star 复制按 (dep_path, 绑定) 判同名冲突。
let path_reg = self.load_string_const(&dep_path, ctx); let path_reg = self.load_string_const(&dep_path, ctx);
self.emit_module_call(ctx, "__moduleStar", &[ns_reg, dep_ns_reg, path_reg])?; self.emit_module_call(ctx, "__moduleStar", &[ns_reg, dep_ns_reg, path_reg])?;
// star 复制后 star 转发的自导入名已就位:就地刷新占位槽,
// 覆盖 body 内 star 语句之后立即读取的形态。
self.emit_self_alias_refresh(ctx, ns_reg)?;
} }
} }
_ => return Err("unsupported module export statement".into()), _ => return Err("unsupported module export statement".into()),
@@ -44,6 +44,7 @@ pub fn bind_global(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
oxide_builtins::module::module_set_reexport::<crate::vm::Vm> as *const (), oxide_builtins::module::module_set_reexport::<crate::vm::Vm> as *const (),
5, 5,
), ),
("__moduleAlias", oxide_builtins::module::module_alias::<crate::vm::Vm> as *const (), 3),
("__moduleGet", oxide_builtins::module::module_get::<crate::vm::Vm> as *const (), 2), ("__moduleGet", oxide_builtins::module::module_get::<crate::vm::Vm> as *const (), 2),
( (
"__moduleLinkGet", "__moduleLinkGet",