mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 模块自导入绑定改为源绑定活别名
This commit is contained in:
@@ -142,7 +142,10 @@ pub struct CompileCtx {
|
||||
/// 自导入(import from 自身)的 source 字符串集合:绑定走别名语义,不能链接期快照。
|
||||
pub(crate) module_self_import_specs: HashSet<String>,
|
||||
/// 自导入别名:导出名 → 本地绑定槽寄存器(export 语句执行时回写绑定值)。
|
||||
/// 仅承载无法静态解析源绑定的退化占位路径(star 转发的自导入名)。
|
||||
pub(crate) module_self_aliases: HashMap<String, u32>,
|
||||
/// 自导入别名(本地名,基源绑定名):供别名捕获后处理合并源/别名 cell。
|
||||
pub(crate) module_alias_pairs: Vec<(String, String)>,
|
||||
/// 标签模板 site 计数器:本编译树内全局唯一(子 ctx 继承父值继续递增)。
|
||||
/// 运行时与模块 flat_id 组成模板对象缓存键,保证同一编译树同 site 恒返回
|
||||
/// 同一对象、不同编译树(eval 每次编译)互不共享。
|
||||
@@ -226,6 +229,7 @@ impl CompileCtx {
|
||||
module_dep_ns_regs: HashMap::new(),
|
||||
module_self_import_specs: HashSet::new(),
|
||||
module_self_aliases: HashMap::new(),
|
||||
module_alias_pairs: Vec::new(),
|
||||
next_template_site: 0,
|
||||
}
|
||||
}
|
||||
@@ -379,6 +383,12 @@ impl CompileCtx {
|
||||
self.scopes.symbols.init_var(name);
|
||||
}
|
||||
|
||||
/// 声明本地名为源绑定活引用的别名(模块自导入):槽位复用源寄存器,
|
||||
/// TDZ/提升/活值/不可变全部委托源绑定。
|
||||
pub(crate) fn add_alias(&mut self, local: &str, source: &str) -> Result<(), String> {
|
||||
self.scopes.symbols.add_alias(local, source)
|
||||
}
|
||||
|
||||
pub(crate) fn next_label_id(&mut self) -> u32 {
|
||||
let id = self.labels.label_counter;
|
||||
self.labels.label_counter += 1;
|
||||
|
||||
+256
-54
@@ -1,11 +1,12 @@
|
||||
//! 模块编译:import/export 解析、依赖加载(递归)、快照式链接与命名空间。
|
||||
//!
|
||||
//! 快照式链接:依赖模块先整体求值(`__moduleEval`)并返回命名空间对象,
|
||||
//! 导入绑定在 prelude 一次性初始化;`export` 语句就地注册导出值。
|
||||
//! 未支持:live binding(导入绑定活态跟随源模块值变化)、defer(延迟求值)、
|
||||
//! source-phase——导入绑定为链接期快照,不跟随源模块值变化;循环导入在编译期跳过。
|
||||
//! 普通依赖的导入绑定在 prelude 一次性链接为快照;自导入(`import ... from
|
||||
//! 自身`)的局部名解析为本模块源绑定的活别名,TDZ/提升/活值/不可变四语义全部
|
||||
//! 委托源绑定。`export` 语句就地注册导出值。
|
||||
//! 未支持:跨模块 live binding(导入绑定不跟随依赖模块值变化)、defer(延迟
|
||||
//! 求值)、source-phase;star 转发的自导入名退化为链接期快照;循环导入在编译期跳过。
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::capture::{collect_captured_bindings, collect_own_binding_names};
|
||||
@@ -158,10 +159,95 @@ fn is_hoisted_function_decl(stmt: &Statement) -> bool {
|
||||
Statement::ExportNamedDeclaration(exp) => {
|
||||
matches!(&exp.declaration, Some(Declaration::FunctionDeclaration(_)))
|
||||
}
|
||||
// export default function 声明形态在实例化期绑定并预初始化(提升至模块求值前),
|
||||
// 与普通函数声明同一发射次序。
|
||||
Statement::ExportDefaultDeclaration(exp) => {
|
||||
matches!(&exp.declaration, ExportDefaultDeclarationKind::FunctionDeclaration(_))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 合成默认导出的源绑定名(匿名 default 函数/类与 default 表达式共用)。
|
||||
/// 含 `*`,与任何用户标识符都不可能重名。
|
||||
pub(crate) const SYNTHETIC_DEFAULT_BINDING: &str = "*default*";
|
||||
|
||||
/// 收集模块自身导出名 → 源绑定名映射:把 self-import 的局部名别名到该映射给出的
|
||||
/// 源绑定,使别名读/写/TDZ/const 全部经源绑定通道自然成立。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 带声明的导出:var/fn/class 的每个绑定名 → 自身名。
|
||||
/// - 无 source 再导出 `export { x as y }`:`y → x`。
|
||||
/// - self source 再导出 `export { local as indirect } from './self'`:`indirect → local`。
|
||||
/// - 非 self source 再导出与 star 转发不入表(跨模块面由运行期刷新承载),
|
||||
/// 映射缺失时调用方回退非别名路径。
|
||||
/// - `export default`:具名 fn/class → 具名 id;匿名 fn/class 与表达式 → 合成 `*default*`。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 须在依赖收集循环之后构建(self 再导出判定读 `module_self_import_specs`)。
|
||||
fn module_export_name_map(body: &[Statement], self_specs: &HashSet<String>) -> HashMap<String, String> {
|
||||
let mut map = HashMap::new();
|
||||
for stmt in body {
|
||||
match stmt {
|
||||
Statement::ExportNamedDeclaration(exp) => {
|
||||
if let Some(decl) = &exp.declaration {
|
||||
match decl {
|
||||
Declaration::VariableDeclaration(vd) => {
|
||||
for d in &vd.declarations {
|
||||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
let name = bi.name.to_string();
|
||||
map.insert(name.clone(), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
Declaration::FunctionDeclaration(fd) => {
|
||||
if let Some(id) = &fd.id {
|
||||
let name = id.name.to_string();
|
||||
map.insert(name.clone(), name);
|
||||
}
|
||||
}
|
||||
Declaration::ClassDeclaration(cl) => {
|
||||
if let Some(id) = &cl.id {
|
||||
let name = id.name.to_string();
|
||||
map.insert(name.clone(), name);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if let Some(src) = &exp.source {
|
||||
// self 再导出:导出名解析到本模块的本地名,本模块体执行中
|
||||
// 该本地名(可能是 import 名或本地声明)对别名可见。
|
||||
if self_specs.contains(src.value.as_str()) {
|
||||
for spec in &exp.specifiers {
|
||||
map.insert(module_export_name_str(&spec.exported), module_export_name_str(&spec.local));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 无 source 再导出:导出名直接映射到本地声明名。
|
||||
for spec in &exp.specifiers {
|
||||
map.insert(module_export_name_str(&spec.exported), module_export_name_str(&spec.local));
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::ExportDefaultDeclaration(exp) => {
|
||||
let source = match &exp.declaration {
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(fd) => {
|
||||
fd.id.as_ref().map(|id| id.name.to_string())
|
||||
}
|
||||
ExportDefaultDeclarationKind::ClassDeclaration(cl) => cl.id.as_ref().map(|id| id.name.to_string()),
|
||||
_ => None,
|
||||
};
|
||||
map.insert(
|
||||
DEFAULT_EXPORT_NAME.to_string(),
|
||||
source.unwrap_or_else(|| SYNTHETIC_DEFAULT_BINDING.to_string()),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
impl Emitter {
|
||||
/// 编译 ES module:顶层 body + 递归依赖。
|
||||
/// `module_path` 为模块文件路径;入口即归一为规范绝对路径(依赖解析基准 =
|
||||
@@ -274,16 +360,17 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
// —— 导入绑定初始化(const 语义;命名空间绑定直接引用 ns 对象)——
|
||||
// —— 导入绑定初始化(普通依赖为链接期快照;命名空间绑定直接引用 ns 对象;
|
||||
// 自导入绑定解析为本模块源绑定的活别名)——
|
||||
let own_export_names = module_own_export_names(body);
|
||||
// 无命名 `export * from 'x'` 会把依赖的全部导出转发给自身;自导入名可能来自
|
||||
// star 再导出,编译期无法静态判定该名是否存在于自身导出,故链接校验放宽。
|
||||
let has_unnamed_star = body
|
||||
.iter()
|
||||
.any(|s| matches!(s, Statement::ExportAllDeclaration(e) if e.exported.is_none()));
|
||||
// 自导入(import from 自身)的绑定在 body 执行中由 export 语句回写:
|
||||
// prelude 先初始化为 undefined(规范:实例化后未求值前 var 源绑定为
|
||||
// undefined),并登记 导出名 → 本地绑定槽 供回写。
|
||||
// 导出名 → 本模块源绑定名:self-import 的局部名别名到该源绑定。star 转发的
|
||||
// 自导入名不在表内(退化为下方非别名占位路径)。
|
||||
let export_name_map = module_export_name_map(body, &ctx.module_self_import_specs);
|
||||
for stmt in body {
|
||||
if let Statement::ImportDeclaration(imp) = stmt {
|
||||
let dep_spec = imp.source.value.to_string();
|
||||
@@ -297,24 +384,21 @@ impl Emitter {
|
||||
match sp {
|
||||
ImportDeclarationSpecifier::ImportSpecifier(s) => {
|
||||
let imported = module_export_name_str(&s.imported);
|
||||
let alias_source = is_self.then(|| export_name_map.get(&imported)).flatten();
|
||||
if is_self && !own_export_names.contains(&imported) && !has_unnamed_star {
|
||||
return Err(format!("requested module export is not exported: {imported}"));
|
||||
}
|
||||
if let Some(source) = alias_source {
|
||||
// 导出名可静态解析到本模块源绑定:局部名注册为
|
||||
// 源绑定的活别名(源不存在时回退占位路径)。
|
||||
if ctx.add_alias(s.local.name.as_str(), source).is_ok() {
|
||||
ctx.module_alias_pairs.push((s.local.name.to_string(), source.to_string()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if is_self {
|
||||
if !own_export_names.contains(&imported) && !has_unnamed_star {
|
||||
return Err(format!("requested module export is not exported: {imported}"));
|
||||
}
|
||||
let undef_idx = ctx.add_constant(Constant::Undefined);
|
||||
let undef_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
|
||||
self.emit_bind_target(
|
||||
s.local.name.as_str(),
|
||||
undef_reg,
|
||||
VariableDeclarationKind::Const,
|
||||
true,
|
||||
false,
|
||||
ctx,
|
||||
)?;
|
||||
if let Ok(reg) = ctx.lookup(s.local.name.as_str()) {
|
||||
ctx.module_self_aliases.insert(imported, reg);
|
||||
}
|
||||
let alias_reg = self.emit_self_alias_placeholder(s.local.name.as_str(), ctx)?;
|
||||
ctx.module_self_aliases.insert(imported, alias_reg);
|
||||
} else {
|
||||
let name_reg = self.load_string_const(&imported, ctx);
|
||||
let val_reg =
|
||||
@@ -330,24 +414,31 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => {
|
||||
let alias_source =
|
||||
is_self.then(|| export_name_map.get(DEFAULT_EXPORT_NAME)).flatten().cloned();
|
||||
if is_self && !own_export_names.contains(DEFAULT_EXPORT_NAME) {
|
||||
return Err("requested module export is not exported: default".into());
|
||||
}
|
||||
if let Some(source) = alias_source {
|
||||
// 匿名 default 函数/类与 default 表达式无本地声明名,
|
||||
// 源绑定须惰性建:只在此处存在 self-import default 时
|
||||
// 建立,保证无 self-import 的模块零 IR 变化。
|
||||
if source == SYNTHETIC_DEFAULT_BINDING
|
||||
&& ctx.scopes.symbols.lookup_any(&source).is_none()
|
||||
{
|
||||
let reg = ctx.alloc_reg();
|
||||
ctx.declare_predeclared(&source, reg, VariableDeclarationKind::Let, false)?;
|
||||
// 表达式形态 TDZ 保持;函数声明形态由提升臂在求值
|
||||
// 前初始化,此处不做初始化。
|
||||
}
|
||||
if ctx.add_alias(s.local.name.as_str(), &source).is_ok() {
|
||||
ctx.module_alias_pairs.push((s.local.name.to_string(), source.clone()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if is_self {
|
||||
if !own_export_names.contains(DEFAULT_EXPORT_NAME) {
|
||||
return Err("requested module export is not exported: default".into());
|
||||
}
|
||||
let undef_idx = ctx.add_constant(Constant::Undefined);
|
||||
let undef_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
|
||||
self.emit_bind_target(
|
||||
s.local.name.as_str(),
|
||||
undef_reg,
|
||||
VariableDeclarationKind::Const,
|
||||
true,
|
||||
false,
|
||||
ctx,
|
||||
)?;
|
||||
if let Ok(reg) = ctx.lookup(s.local.name.as_str()) {
|
||||
ctx.module_self_aliases.insert(DEFAULT_EXPORT_NAME.to_string(), reg);
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
let name_reg = self.load_string_const(DEFAULT_EXPORT_NAME, ctx);
|
||||
let val_reg =
|
||||
@@ -380,6 +471,32 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
// —— 别名捕获后处理:源绑定被嵌套函数捕获时,别名必须落到同一 cell,
|
||||
// 否则两处各持一份值。无捕获时别名读经源寄存器天然活。此后源被捕获的
|
||||
// var 名在模块入口统一 MAKE_CELL(undefined),与脚本顶层实例化同语义。——
|
||||
self.reconcile_alias_captures(ctx);
|
||||
{
|
||||
let mut var_names: Vec<String> = crate::capture::collect_var_binding_names(body)
|
||||
.into_iter()
|
||||
.filter(|n| !ctx.param_names.contains(n.as_str()))
|
||||
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||||
.collect();
|
||||
var_names.sort();
|
||||
if !var_names.is_empty() {
|
||||
let undef_reg = self.emit_undefined(ctx);
|
||||
for name in var_names {
|
||||
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::MAKE_CELL,
|
||||
Operand::Reg(undef_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// —— re-export(export { x } from 'mod')链接检查:实例化期解析,须先于
|
||||
// body 求值。$DONOTEVALUATE 是 test262 测试框架的断言工具,断言链接错误先于
|
||||
// body 求值抛出,故链接检查必须先于 body 发射。——
|
||||
@@ -511,15 +628,72 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 自导入绑定的非别名占位:源绑定无法静态解析(star 转发的自导入名)时,
|
||||
/// prelude 把局部名绑成已初始化的 undefined 常量槽并登记回写,由 export
|
||||
/// 语句执行时就地刷新。返回该本地绑定槽寄存器。
|
||||
fn emit_self_alias_placeholder(&self, local: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
let undef_idx = ctx.add_constant(Constant::Undefined);
|
||||
let undef_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
|
||||
self.emit_bind_target(local, undef_reg, VariableDeclarationKind::Const, true, false, ctx)?;
|
||||
ctx.lookup(local)
|
||||
}
|
||||
|
||||
fn emit_module_set(&self, ctx: &mut CompileCtx, ns_reg: u32, name: &str, value_reg: u32) -> Result<(), String> {
|
||||
let name_reg = self.load_string_const(name, ctx);
|
||||
self.emit_module_call(ctx, "__moduleSet", &[ns_reg, name_reg, value_reg])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 别名捕获后处理:把源/别名对合并到同一 cell。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `captured_bindings` 是名字级索引;源与别名共用寄存器槽,但捕获分析按
|
||||
/// 名字给出各自(或仅一方)的 entry。两侧都有时以源 cell 为准并把别名指向
|
||||
/// 同一 cell;仅别名有 cell 时把源也指向该 cell(别名会被 MAKE_CELL 建 cell)。
|
||||
/// - 两者都未被捕获时读路径直接读源寄存器,天然活值,无需处理。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 修改 `captured_bindings` 映射;使 `cells_needed` 与实际使用的 cell
|
||||
/// 上限一致(多余 entry 无害,缺失 entry 会导致子函数 upvalue 索引错位)。
|
||||
fn reconcile_alias_captures(&self, ctx: &mut CompileCtx) {
|
||||
let pairs = std::mem::take(&mut ctx.module_alias_pairs);
|
||||
for (local, source) in pairs {
|
||||
let local_cell = ctx.captured_bindings.get(&local).copied();
|
||||
let source_cell = ctx.captured_bindings.get(&source).copied();
|
||||
match (local_cell, source_cell) {
|
||||
(Some(idx), _) => {
|
||||
// 别名被捕获:源须共用同一 cell(源尚未捕获时补映射)。
|
||||
if source_cell.is_none() {
|
||||
ctx.captured_bindings.insert(source.clone(), idx);
|
||||
}
|
||||
}
|
||||
(None, Some(idx)) => {
|
||||
// 仅源被捕获:别名读经源 cell,补别名映射使子函数引用别名时
|
||||
// upvalue 解析到同一 cell。
|
||||
ctx.captured_bindings.insert(local.clone(), idx);
|
||||
}
|
||||
(None, None) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_var_reg(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
let var_reg = ctx.lookup(name)?;
|
||||
let val_reg = ctx.alloc_reg();
|
||||
// 被捕获绑定只更新 cell,寄存器停留在捕获前的旧值;导出注册须经 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_GET,
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
));
|
||||
return Ok(val_reg);
|
||||
}
|
||||
}
|
||||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(val_reg), Operand::Reg(var_reg), Operand::None));
|
||||
Ok(val_reg)
|
||||
}
|
||||
@@ -593,16 +767,20 @@ impl Emitter {
|
||||
let val_reg = match &exp.declaration {
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(fd) => {
|
||||
let reg = self.emit_function_expression(fd, ctx)?;
|
||||
if let Some(id) = &fd.id {
|
||||
self.emit_bind_target(
|
||||
id.name.as_str(),
|
||||
reg,
|
||||
VariableDeclarationKind::Const,
|
||||
true,
|
||||
false,
|
||||
ctx,
|
||||
)?;
|
||||
} else {
|
||||
// 具名 default 函数绑定自身 id 名;匿名 default 函数绑定合成
|
||||
// `*default*` 源名(仅在该绑定已由 self-import 惰性建立时)。
|
||||
// 声明形态的函数绑定可变(CreateMutableBinding),提升臂已在
|
||||
// 预声明期预初始化,此处是声明点的赋值。
|
||||
let binding = fd.id.as_ref().map(|id| id.name.to_string()).or_else(|| {
|
||||
ctx.scopes
|
||||
.symbols
|
||||
.lookup_any(SYNTHETIC_DEFAULT_BINDING)
|
||||
.map(|_| SYNTHETIC_DEFAULT_BINDING.to_string())
|
||||
});
|
||||
if let Some(name) = binding {
|
||||
self.emit_bind_target(&name, reg, VariableDeclarationKind::Var, false, false, ctx)?;
|
||||
}
|
||||
if fd.id.is_none() {
|
||||
set_implicit_name_of_last_nested(ctx, DEFAULT_EXPORT_NAME);
|
||||
}
|
||||
reg
|
||||
@@ -623,6 +801,17 @@ impl Emitter {
|
||||
false,
|
||||
ctx,
|
||||
)?;
|
||||
} else if ctx.scopes.symbols.lookup_any(SYNTHETIC_DEFAULT_BINDING).is_some() {
|
||||
// 匿名 default 类按规范在求值点初始化合成 `*default*`
|
||||
// 绑定(仅在存在 self-import default 时惰性建立)。
|
||||
self.emit_bind_target(
|
||||
SYNTHETIC_DEFAULT_BINDING,
|
||||
reg,
|
||||
VariableDeclarationKind::Let,
|
||||
false,
|
||||
false,
|
||||
ctx,
|
||||
)?;
|
||||
}
|
||||
reg
|
||||
}
|
||||
@@ -630,7 +819,7 @@ impl Emitter {
|
||||
let expr = other
|
||||
.as_expression()
|
||||
.ok_or_else(|| "unsupported export default declaration".to_string())?;
|
||||
if crate::is_anonymous_function_definition(expr) {
|
||||
let reg = if crate::is_anonymous_function_definition(expr) {
|
||||
match strip_parens(expr) {
|
||||
// 匿名类:构造器落名后还会 push 方法子模块,必须经
|
||||
// implicit_name 写构造器;具名类由 ctor_name 优先。
|
||||
@@ -647,7 +836,20 @@ impl Emitter {
|
||||
}
|
||||
} else {
|
||||
self.emit_expression(expr, ctx)?
|
||||
};
|
||||
// default 表达式/匿名类的合成源绑定只在存在 self-import default
|
||||
// 时惰性建立;无 self-import 时保持零 IR 变化(不建绑定、不赋值)。
|
||||
if ctx.scopes.symbols.lookup_any(SYNTHETIC_DEFAULT_BINDING).is_some() {
|
||||
self.emit_bind_target(
|
||||
SYNTHETIC_DEFAULT_BINDING,
|
||||
reg,
|
||||
VariableDeclarationKind::Let,
|
||||
false,
|
||||
false,
|
||||
ctx,
|
||||
)?;
|
||||
}
|
||||
reg
|
||||
}
|
||||
};
|
||||
self.emit_module_set(ctx, ns_reg, DEFAULT_EXPORT_NAME, val_reg)?;
|
||||
|
||||
@@ -15,15 +15,20 @@ impl Emitter {
|
||||
Some(Declaration::FunctionDeclaration(f)) => f,
|
||||
_ => continue,
|
||||
},
|
||||
// export default function foo(){}:foo 绑定由 lexical predeclare 以
|
||||
// Const 预声明(emit 侧 emit_bind_target(Const) 消费预登记槽)。
|
||||
// export default function:具名声明名 / 匿名的合成 `*default*` 名
|
||||
// 按函数声明提升并预初始化(模块实例化期即绑定函数对象)。
|
||||
Statement::ExportDefaultDeclaration(exp) => match &exp.declaration {
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(f) => f,
|
||||
_ => continue,
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
let Some(identifier) = &function.id else {
|
||||
continue;
|
||||
let name = match &function.id {
|
||||
Some(identifier) => identifier.name.to_string(),
|
||||
None => "*default*".to_string(),
|
||||
};
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ = ctx.declare_initialized(identifier.name.as_str(), reg, VariableDeclarationKind::Var, false);
|
||||
let _ = ctx.declare_initialized(name.as_str(), reg, VariableDeclarationKind::Var, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,23 +347,17 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::ExportDefaultDeclaration(exp) => match &exp.declaration {
|
||||
ExportDefaultDeclarationKind::ClassDeclaration(cd) => {
|
||||
Statement::ExportDefaultDeclaration(exp) => {
|
||||
// default 函数声明(具名与匿名)由函数预声明臂按提升 var 承载,
|
||||
// 此处只为具名 default 类建 lexical Let TDZ 占位。
|
||||
if let ExportDefaultDeclarationKind::ClassDeclaration(cd) = &exp.declaration {
|
||||
if let Some(id) = &cd.id {
|
||||
check_restricted_global_lexical(id.name.as_str(), global_lexical)?;
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ = ctx.declare_predeclared(id.name.as_str(), reg, VariableDeclarationKind::Let, false);
|
||||
}
|
||||
}
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(fd) => {
|
||||
if let Some(id) = &fd.id {
|
||||
let reg = ctx.alloc_reg();
|
||||
let _ =
|
||||
ctx.declare_predeclared(id.name.as_str(), reg, VariableDeclarationKind::Const, true);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,14 @@ pub(crate) struct Binding {
|
||||
|
||||
/// 作用域符号表:名字 → 寄存器号/初始化状态/const 标志。
|
||||
/// 首层为全局函数作用域,后续 push 的为块作用域。
|
||||
///
|
||||
/// 别名(`aliases`)承载模块自导入:自导入的局部名是源导出的活引用,其绑定
|
||||
/// 槽位与源绑定相同,TDZ/提升/活值/不可变四语义全部委托源绑定;表内只负责在
|
||||
/// 源绑定初始化时同步解除别名 TDZ。
|
||||
pub struct SymbolTable {
|
||||
pub(crate) scopes: Vec<Scope>,
|
||||
/// 别名本地名 → 基源绑定名(沿 re-export 链解析到最终源名)。
|
||||
aliases: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Default for SymbolTable {
|
||||
@@ -48,6 +54,7 @@ impl SymbolTable {
|
||||
bindings: HashMap::new(),
|
||||
kind: ScopeKind::FunctionScope,
|
||||
}],
|
||||
aliases: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +255,75 @@ impl SymbolTable {
|
||||
}
|
||||
|
||||
/// 从内到外将同名绑定标记为已初始化(var 提升后初始化阶段使用)。
|
||||
/// 指向该源的别名绑定同步置为已初始化,使别名读取的 TDZ 状态跟随源声明点。
|
||||
pub fn init_var(&mut self, name: &str) {
|
||||
for scope in self.scopes.iter_mut().rev() {
|
||||
if let Some(b) = scope.bindings.get_mut(name) {
|
||||
b.initialized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 别名侧表与绑定分居两处,直接按基源名命中全部别名槽位,不走作用域查找。
|
||||
let alias_bases: Vec<String> = self
|
||||
.aliases
|
||||
.iter()
|
||||
.filter(|(_, base)| base.as_str() == name)
|
||||
.map(|(local, _)| local.clone())
|
||||
.collect();
|
||||
for local in alias_bases {
|
||||
self.mark_initialized(&local);
|
||||
}
|
||||
}
|
||||
|
||||
/// 声明本地名为 `source` 源绑定的别名:别名槽位复用源寄存器,`is_const`
|
||||
/// 恒为真(import 绑定是不可变的间接引用),初始化状态镜像源绑定当前值。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `source` 经 `aliases` 表解析到基源名(支持别名链);基源不存在于符号表
|
||||
/// 时返回 `Err`,调用方回退非别名路径。
|
||||
/// - 别名名在当前最内层作用域已有绑定时返回 `Err`(重复声明)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 在当前最内层作用域插入别名 Binding,并登记 `aliases` 侧表。
|
||||
pub(crate) fn add_alias(&mut self, local: &str, source: &str) -> Result<(), String> {
|
||||
let base = self.resolve_alias_base(source).to_string();
|
||||
let Some((reg, initialized)) = self
|
||||
.scopes
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|scope| scope.bindings.get(&base).map(|binding| (binding.reg, binding.initialized)))
|
||||
else {
|
||||
return Err(format!("alias source '{base}' is not declared"));
|
||||
};
|
||||
|
||||
let target = self.scopes.last_mut().expect("symbol table always has a scope");
|
||||
if target.bindings.contains_key(local) {
|
||||
return Err(format!("Identifier '{local}' has already been declared"));
|
||||
}
|
||||
target.bindings.insert(
|
||||
local.to_string(),
|
||||
Binding {
|
||||
reg,
|
||||
initialized,
|
||||
is_const: true,
|
||||
predeclared: false,
|
||||
},
|
||||
);
|
||||
self.aliases.insert(local.to_string(), base);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 沿别名侧表把名字解析到最终基源绑定名;非别名名原样返回。
|
||||
pub(crate) fn resolve_alias_base<'a>(&'a self, name: &'a str) -> &'a str {
|
||||
let mut base = name;
|
||||
while let Some(next) = self.aliases.get(base) {
|
||||
base = next.as_str();
|
||||
}
|
||||
base
|
||||
}
|
||||
|
||||
/// 把名对应绑定置为已初始化(只查绑定、不问状态)。
|
||||
fn mark_initialized(&mut self, name: &str) {
|
||||
for scope in self.scopes.iter_mut().rev() {
|
||||
if let Some(b) = scope.bindings.get_mut(name) {
|
||||
b.initialized = true;
|
||||
@@ -380,4 +455,42 @@ mod tests {
|
||||
assert!(st.declare("y", 2, v(), false).is_err());
|
||||
st.pop_scope();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_shares_source_slot_and_const() {
|
||||
let mut st = SymbolTable::new();
|
||||
st.declare("x", 3, l(), false).unwrap();
|
||||
st.add_alias("y", "x").unwrap();
|
||||
// 别名复用源寄存器,且是不可变的间接绑定。
|
||||
assert_eq!(st.lookup_any("y"), Some(3));
|
||||
assert!(st.lookup_any_binding("y").unwrap().0.is_const);
|
||||
// 源未初始化时别名读同样报 TDZ。
|
||||
assert!(st.lookup("y").unwrap_err().contains("before initialization"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_tdz_follows_source_init() {
|
||||
let mut st = SymbolTable::new();
|
||||
st.declare("x", 3, l(), false).unwrap();
|
||||
st.add_alias("y", "x").unwrap();
|
||||
st.init_var("x");
|
||||
// 源声明点初始化同步解除别名 TDZ。
|
||||
assert_eq!(st.lookup("y").unwrap(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_chain_resolves_to_base() {
|
||||
let mut st = SymbolTable::new();
|
||||
st.declare("x", 3, l(), false).unwrap();
|
||||
st.add_alias("y", "x").unwrap();
|
||||
st.add_alias("z", "y").unwrap();
|
||||
assert_eq!(st.resolve_alias_base("z"), "x");
|
||||
assert_eq!(st.lookup_any("z"), Some(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_of_missing_source_is_error() {
|
||||
let mut st = SymbolTable::new();
|
||||
assert!(st.add_alias("y", "missing").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,3 +214,53 @@ fn export_default_implicit_name_named_unchanged() {
|
||||
let (ir, _) = emit_module("export default 42", "./entry.js", &[]).expect("字面量应编译成功");
|
||||
assert!(named_module(&ir, "default").is_none(), "非函数值不应注入隐式名");
|
||||
}
|
||||
|
||||
/// STORE_VAR 指令计数(合成绑定惰性化守卫用)。
|
||||
fn store_var_count(ir: &IRFunction) -> usize {
|
||||
ir.insts.iter().filter(|i| i.op == OpCode::STORE_VAR).count()
|
||||
}
|
||||
|
||||
/// 面 8:无 self-import 的 default 表达式模块零 diff。
|
||||
/// `export default 42` 不建合成 `*default*` 绑定,故除 export 注册外无任何
|
||||
/// STORE_VAR(字面量经 LOAD_CONST 直接注册)。若合成绑定被无条件建立,会多出
|
||||
/// 一条 STORE_VAR,本守卫即失败。
|
||||
#[test]
|
||||
fn default_expression_module_has_no_synthetic_binding() {
|
||||
let (ir, _) = emit_module("export default 42", "./entry.js", &[]).expect("字面量应编译成功");
|
||||
assert_eq!(store_var_count(&ir), 0, "default 表达式模块不应建合成绑定槽");
|
||||
assert_eq!(native_calls_to(&ir, "__moduleSet"), 1, "default 只注册一次导出");
|
||||
}
|
||||
|
||||
/// 面 8b:无 self-import 的 `export let x = 1` 不因 default 面多出槽位/指令。
|
||||
/// 该模块唯一 STORE_VAR 是 x 自身的声明初始化;零 diff 守卫要求不出现第二个槽。
|
||||
#[test]
|
||||
fn named_export_without_self_import_keeps_single_store() {
|
||||
let (ir, _) = emit_module("export let x = 1;", "./entry.js", &[]).expect("导出声明应编译成功");
|
||||
assert_eq!(store_var_count(&ir), 1, "x 声明只应有一条 STORE_VAR");
|
||||
assert_eq!(native_calls_to(&ir, "__moduleSet"), 1, "x 只注册一次导出");
|
||||
}
|
||||
|
||||
/// 面 8c:无 source 再导出 `export { x }` 不引入合成绑定。
|
||||
/// 局部 x 由普通声明承载,再导出只读值注册;无 self-import 时零额外 STORE_VAR。
|
||||
#[test]
|
||||
fn local_reexport_without_self_import_keeps_single_store() {
|
||||
let (ir, _) = emit_module("let x = 1; export { x };", "./entry.js", &[]).expect("本地再导出应编译成功");
|
||||
assert_eq!(store_var_count(&ir), 1, "x 声明只应有一条 STORE_VAR");
|
||||
}
|
||||
|
||||
/// 面 9:self-import 别名以源绑定槽位承载,导出注册不再回写占位槽。
|
||||
/// `export default 42` 有 self-import default 时合成绑定存在,但别名与源共享
|
||||
/// 槽位:export 语句只发一次 LOAD_CONST + `__moduleSet`,无额外的回写 STORE_VAR。
|
||||
#[test]
|
||||
fn self_import_default_alias_has_no_placeholder_writeback() {
|
||||
let (ir, _) = emit_module(
|
||||
"import d from './self.js'; export default 42;",
|
||||
"./self.js",
|
||||
&[("./self.js", ModuleKind::Js, "export default 42;")],
|
||||
)
|
||||
.expect("self-import default 应编译成功");
|
||||
assert_eq!(native_calls_to(&ir, "__moduleSet"), 1, "default 只注册一次导出");
|
||||
// 别名与合成源绑定共享槽位:仅 default 表达式的绑定写入一条 STORE_VAR,
|
||||
// 不出现旧占位路径的额外回写。
|
||||
assert_eq!(store_var_count(&ir), 1, "不应出现占位回写带来的第二条 STORE_VAR");
|
||||
}
|
||||
|
||||
@@ -75,3 +75,39 @@ fn self_import_module_evaluated_once() {
|
||||
let r = probe(&mut vm, "globalThis.__evals");
|
||||
assert!(r.is_int() && r.as_int() == 1, "自导入模块应只求值一次,实际 __evals={r:?}");
|
||||
}
|
||||
|
||||
/// 自导入绑定是源绑定的活引用:闭包经别名读到的必须是源绑定的当前值;
|
||||
/// 源在导出声明后重赋时,别名读点同步可见新值(共享 cell)。
|
||||
#[test]
|
||||
fn self_import_alias_reads_live_source_value() {
|
||||
let cwd = std::env::current_dir().expect("cwd");
|
||||
let dir = cwd.join("__selfimport_live__");
|
||||
let file = dir.join("live.mjs");
|
||||
std::fs::create_dir_all(&dir).expect("mkdir");
|
||||
std::fs::write(
|
||||
&file,
|
||||
"let before;\n \
|
||||
function readAlias() { return y; }\n \
|
||||
try { before = readAlias(); } catch (e) { before = e instanceof ReferenceError; }\n \
|
||||
import { x as y } from './live.mjs';\n \
|
||||
export let x = 1;\n \
|
||||
x = 2;\n \
|
||||
globalThis.__live = [before, readAlias()].join('|');",
|
||||
)
|
||||
.expect("write module");
|
||||
let _cleanup = Cleanup(dir.clone());
|
||||
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let source = std::fs::read_to_string(&file).expect("read module");
|
||||
let program = oxide_parser::parse_module(&allocator, &source).expect("parse module");
|
||||
let module = Compiler::new()
|
||||
.compile_module(&program, dir.join("live.mjs").to_string_lossy().as_ref(), &mut AbsLoader)
|
||||
.expect("compile module");
|
||||
|
||||
let mut vm = Vm::new();
|
||||
vm.run(&Arc::new(module)).expect("module run");
|
||||
// 声明前闭包读别名按规范抛 ReferenceError;声明后重赋读到新值 2。
|
||||
let r = probe(&mut vm, "globalThis.__live");
|
||||
let text = vm.lookup_str(r).unwrap_or_default();
|
||||
assert_eq!(text, "true|2", "自导入别名应共享源绑定活值");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user