mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
1324 lines
69 KiB
Rust
1324 lines
69 KiB
Rust
//! 模块编译:import/export 解析、依赖加载(递归)、快照式链接与命名空间。
|
||
//!
|
||
//! 普通依赖的导入绑定在 prelude 链接:依赖含可重赋导出时顶层读点经
|
||
//! `__moduleGet` 活读,其余退化为快照;被闭包捕获的导出挂共享 cell,闭包内写
|
||
//! 随活读可见。自导入(`import ... from 自身`)的局部名解析为本模块源绑定的活
|
||
//! 别名,TDZ/提升/活值/不可变四语义全部委托源绑定。`export` 语句就地注册导出值。
|
||
//! 未支持:嵌套闭包内读 import 名(仍为链接期快照)、defer(延迟求值)、
|
||
//! source-phase;star 转发与经再导出链的活值仍为快照;循环导入在编译期跳过。
|
||
|
||
use std::collections::{HashMap, HashSet};
|
||
use std::path::Path;
|
||
|
||
use crate::capture::{collect_captured_bindings, collect_direct_lexical_names, collect_own_binding_names};
|
||
use crate::expr::call::pack_arg_regs;
|
||
use crate::{CompileCtx, Emitter};
|
||
use oxc_allocator::Box;
|
||
use oxide_bytecode::module::Constant;
|
||
use oxide_bytecode::opcode::OpCode;
|
||
use oxide_ir::inst::Inst;
|
||
use oxide_ir::operand::Operand;
|
||
use oxide_ir::{IRFunction, ParamLayout};
|
||
use oxide_parser::{
|
||
BindingPattern, Declaration, ExportDefaultDeclarationKind, Expression, ImportAttributeKey,
|
||
ImportDeclarationSpecifier, ModuleExportName, Statement, VariableDeclarationKind, WithClause,
|
||
};
|
||
use oxide_types::MODULE_NAMESPACE_BINDING;
|
||
|
||
/// 数据模块种类(非 JS 源码模块)。
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum ModuleKind {
|
||
Js,
|
||
Json,
|
||
Text,
|
||
Bytes,
|
||
}
|
||
|
||
/// 已解析的依赖模块。
|
||
pub struct ResolvedModule {
|
||
/// 模块源码(JS / 数据内容)。
|
||
pub source: String,
|
||
/// 模块规范路径(用于自身导入识别与循环检测)。
|
||
pub path: String,
|
||
/// 模块种类。
|
||
pub kind: ModuleKind,
|
||
}
|
||
|
||
/// 模块加载器:由调用方(test262 runner / CLI)提供文件解析。
|
||
pub trait ModuleSourceLoader {
|
||
/// 解析依赖模块:按 `base_dir` 与 `specifier` 定位模块源码,`attributes` 为
|
||
/// with 子句的导入属性键值对。
|
||
fn resolve(
|
||
&mut self, base_dir: &str, specifier: &str, attributes: &[(&str, &str)],
|
||
) -> Result<ResolvedModule, String>;
|
||
}
|
||
|
||
/// `export default` 的默认导出名,也是匿名默认导出函数/类的隐式 `name`。
|
||
const DEFAULT_EXPORT_NAME: &str = "default";
|
||
|
||
/// 剥离可能的多层括号表达式,供隐式名分派判定内层函数/类形态。
|
||
fn strip_parens<'a, 'b>(expr: &'b Expression<'a>) -> &'b Expression<'a> {
|
||
let mut cur = expr;
|
||
while let Expression::ParenthesizedExpression(p) = cur {
|
||
cur = &p.expression;
|
||
}
|
||
cur
|
||
}
|
||
|
||
/// 把隐式名写到刚发射的匿名函数子模块。
|
||
///
|
||
/// # 副作用
|
||
/// - 修改 `ctx.nested` 末项的 `function_name`;已有名(具名函数)不覆盖。
|
||
///
|
||
/// # 注意事项
|
||
/// - 仅用于函数/箭头/生成器:它们在发射期只 push 一个子模块,末项即目标。
|
||
/// 类在构造器之后还会 push 方法子模块,末项不是构造器,类名必须经
|
||
/// `emit_class_with_binding` 的 `implicit_name` 参数落名。
|
||
fn set_implicit_name_of_last_nested(ctx: &mut CompileCtx, name: &str) {
|
||
if let Some(m) = ctx.nested.last_mut() {
|
||
if m.function_name.is_none() {
|
||
m.function_name = Some(name.to_string());
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 把 `ModuleExportName`(标识符名 / 标识符引用 / 字符串字面量)统一转为字符串导出名。
|
||
fn module_export_name_str(m: &ModuleExportName) -> String {
|
||
match m {
|
||
ModuleExportName::IdentifierName(i) => i.name.to_string(),
|
||
ModuleExportName::IdentifierReference(i) => i.name.to_string(),
|
||
ModuleExportName::StringLiteral(s) => s.value.to_string(),
|
||
}
|
||
}
|
||
|
||
/// 从 with 子句提取导入属性键值对(键取标识符或字符串字面量,值取字符串字面量)。
|
||
fn module_attributes(with_clause: &Option<Box<'_, WithClause<'_>>>) -> Vec<(String, String)> {
|
||
let mut out = Vec::new();
|
||
if let Some(wc) = with_clause {
|
||
for attr in &wc.with_entries {
|
||
let key = match &attr.key {
|
||
ImportAttributeKey::Identifier(id) => id.name.to_string(),
|
||
ImportAttributeKey::StringLiteral(s) => s.value.to_string(),
|
||
};
|
||
out.push((key, attr.value.value.to_string()));
|
||
}
|
||
}
|
||
out
|
||
}
|
||
|
||
/// 收集模块自身直接导出的名称集合(含字符串导出名与 default):
|
||
/// 供 self-import 链接期校验(自引用导入不存在的导出 → 编译期错误)。
|
||
fn module_own_export_names(body: &[Statement]) -> HashSet<String> {
|
||
let mut names = HashSet::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 {
|
||
names.insert(bi.name.to_string());
|
||
}
|
||
}
|
||
}
|
||
Declaration::FunctionDeclaration(fd) => {
|
||
if let Some(id) = &fd.id {
|
||
names.insert(id.name.to_string());
|
||
}
|
||
}
|
||
Declaration::ClassDeclaration(cl) => {
|
||
if let Some(id) = &cl.id {
|
||
names.insert(id.name.to_string());
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
} else {
|
||
for spec in &exp.specifiers {
|
||
names.insert(module_export_name_str(&spec.exported));
|
||
}
|
||
}
|
||
}
|
||
Statement::ExportDefaultDeclaration(_) => {
|
||
names.insert(DEFAULT_EXPORT_NAME.to_string());
|
||
}
|
||
Statement::ExportAllDeclaration(exp) => {
|
||
if let Some(exported) = &exp.exported {
|
||
names.insert(module_export_name_str(exported));
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
names
|
||
}
|
||
|
||
/// 收集模块实例化期即初始化为 undefined 的导出名(VarScopedDeclarations):
|
||
/// `export var`、`export function`、`export default function`(具名与匿名)以及
|
||
/// 经 `export { x }` 本地再导出、底层绑定为 var/function 的导出名。
|
||
///
|
||
/// # 边界与前提
|
||
/// - lexical(let/const/class)与跨模块再导出(`export { x } from`)不在集合内,
|
||
/// 其命名空间条目保持未初始化(TDZ)。
|
||
/// - `export { x as y }` 只认本模块顶层直接声明的 var/function;自导入别名等
|
||
/// 间接名不判真(按 TDZ 处理)。
|
||
/// - 匿名 `export default function` 的导出名是 `default`,按 HoistableDeclaration
|
||
/// 预初始化。
|
||
fn module_var_scoped_export_names(body: &[Statement]) -> HashSet<String> {
|
||
let mut var_locals: HashSet<String> = HashSet::new();
|
||
for stmt in body {
|
||
match stmt {
|
||
Statement::VariableDeclaration(vd) => {
|
||
if matches!(vd.kind, VariableDeclarationKind::Var) {
|
||
for d in &vd.declarations {
|
||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||
var_locals.insert(bi.name.to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Statement::FunctionDeclaration(fd) => {
|
||
if let Some(id) = &fd.id {
|
||
var_locals.insert(id.name.to_string());
|
||
}
|
||
}
|
||
Statement::ExportNamedDeclaration(exp) => {
|
||
if let Some(decl) = &exp.declaration {
|
||
match decl {
|
||
Declaration::VariableDeclaration(vd) => {
|
||
if matches!(vd.kind, VariableDeclarationKind::Var) {
|
||
for d in &vd.declarations {
|
||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||
var_locals.insert(bi.name.to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Declaration::FunctionDeclaration(fd) => {
|
||
if let Some(id) = &fd.id {
|
||
var_locals.insert(id.name.to_string());
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
|
||
let mut names = HashSet::new();
|
||
for stmt in body {
|
||
match stmt {
|
||
Statement::ExportNamedDeclaration(exp) => {
|
||
if let Some(decl) = &exp.declaration {
|
||
match decl {
|
||
Declaration::VariableDeclaration(vd) => {
|
||
if matches!(vd.kind, VariableDeclarationKind::Var) {
|
||
for d in &vd.declarations {
|
||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||
names.insert(bi.name.to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Declaration::FunctionDeclaration(fd) => {
|
||
if let Some(id) = &fd.id {
|
||
names.insert(id.name.to_string());
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
} else if exp.source.is_none() {
|
||
for spec in &exp.specifiers {
|
||
if var_locals.contains(module_export_name_str(&spec.local).as_str()) {
|
||
names.insert(module_export_name_str(&spec.exported));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Statement::ExportDefaultDeclaration(exp) => {
|
||
if matches!(&exp.declaration, ExportDefaultDeclarationKind::FunctionDeclaration(_)) {
|
||
names.insert(DEFAULT_EXPORT_NAME.to_string());
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
names
|
||
}
|
||
|
||
/// 判断模块是否含可重赋导出:导出源绑定既非顶层 const 也非导入本地名时为真。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 依据 `module_export_name_map`(导出名 → 源绑定名)与顶层 const 绑定名集合:
|
||
/// var/let/function/class 与 default(含合成 `*default*`)均判真,const-only 判假。
|
||
/// - 经导入本地名再导出(`export { x }` 底层为 import 绑定)不判真:源绑定不可重赋,
|
||
/// 其可变性归来源模块。star 转发名不在映射表内,天然不参与判定。
|
||
///
|
||
/// # 注意事项
|
||
/// - 保守超集:`export default 42` 之类不可重赋值也判真,仅使该依赖多一次预注册,
|
||
/// 导入方读值等价。
|
||
fn module_has_reassignable_export(body: &[Statement], export_name_map: &HashMap<String, String>) -> bool {
|
||
let mut const_names = HashSet::new();
|
||
let mut import_names = HashSet::new();
|
||
for stmt in body {
|
||
match stmt {
|
||
Statement::VariableDeclaration(vd) => {
|
||
if matches!(vd.kind, VariableDeclarationKind::Const) {
|
||
for d in &vd.declarations {
|
||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||
const_names.insert(bi.name.to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Statement::ExportNamedDeclaration(exp) => {
|
||
if let Some(Declaration::VariableDeclaration(vd)) = &exp.declaration {
|
||
if matches!(vd.kind, VariableDeclarationKind::Const) {
|
||
for d in &vd.declarations {
|
||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||
const_names.insert(bi.name.to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Statement::ImportDeclaration(imp) => {
|
||
if let Some(specifiers) = &imp.specifiers {
|
||
for sp in specifiers {
|
||
let local = match sp {
|
||
ImportDeclarationSpecifier::ImportSpecifier(s) => &s.local.name,
|
||
ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => &s.local.name,
|
||
ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => &s.local.name,
|
||
};
|
||
import_names.insert(local.to_string());
|
||
}
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
export_name_map
|
||
.values()
|
||
.any(|source| !const_names.contains(source) && !import_names.contains(source))
|
||
}
|
||
|
||
/// 判断语句是否为提升的函数声明(含 export 包装的函数声明)。
|
||
fn is_hoisted_function_decl(stmt: &Statement) -> bool {
|
||
match stmt {
|
||
Statement::FunctionDeclaration(_) => true,
|
||
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` 为模块文件路径;入口即归一为规范绝对路径(依赖解析基准 =
|
||
/// 其父目录),与加载器 resolve 的绝对规范口径对齐,使自导入身份比较稳定。
|
||
pub fn emit_program_module(
|
||
&self, program: &oxide_parser::Program, module_path: &str, loader: &mut dyn ModuleSourceLoader,
|
||
) -> Result<IRFunction, String> {
|
||
crate::emit_debug!("emit_program_module: {} stmts", program.body.len());
|
||
// 顶层模块路径归一为规范绝对路径:自导入身份比较(resolved.path ==
|
||
// module_path)与 base_dir 需与加载器 resolve 的 canonicalize 绝对口径
|
||
// 一致——若保留相对发现路径,自导入比较恒 false、自导入被当外部依赖
|
||
// 重编译。文件不存在(虚拟/内存模块)时 canonicalize 失败,保留原路径。
|
||
let canonical = std::fs::canonicalize(module_path)
|
||
.map(|p| p.to_string_lossy().into_owned())
|
||
.unwrap_or_else(|_| module_path.to_string());
|
||
let module_path = canonical.as_str();
|
||
let mut ctx = CompileCtx::new();
|
||
// 模块顶层 var/function 不写全局对象(模块作用域绑定)。
|
||
ctx.is_global_scope = false;
|
||
// ES module 顶层恒严格模式(模块代码是严格模式代码,嵌套函数经父 ctx 继承)。
|
||
ctx.is_strict = true;
|
||
let mut path_stack = vec![module_path.to_string()];
|
||
self.emit_module_into_ctx(program, module_path, loader, &mut path_stack, &mut ctx, true)?;
|
||
Ok(ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None))
|
||
}
|
||
|
||
/// 模块体 emit(顶层与依赖模块共用入口)。
|
||
///
|
||
/// `top_level` 区分收尾返回口径:顶层模块求值完成值按规范为空记录
|
||
/// (对外 undefined),依赖模块经 `__moduleEval` 以返回值作命名空间对象。
|
||
#[allow(clippy::too_many_arguments)]
|
||
fn emit_module_into_ctx(
|
||
&self, program: &oxide_parser::Program, module_path: &str, loader: &mut dyn ModuleSourceLoader,
|
||
path_stack: &mut Vec<String>, ctx: &mut CompileCtx, top_level: bool,
|
||
) -> Result<(), String> {
|
||
let body = &program.body;
|
||
|
||
// —— 预声明(镜像 emit_program;import/export 包装的声明也计入 hoisting)——
|
||
self.predeclare_function_declarations(body, ctx);
|
||
// 模块顶层词法声明名排除镜像登记(词法绑定遮蔽同名全局属性,同 program.rs)。
|
||
let lexical_excluded = collect_direct_lexical_names(body);
|
||
self.pre_register_builtin_references(body, &lexical_excluded, ctx);
|
||
self.predeclare_var_declarations(body, ctx);
|
||
// 模块 lexical 声明入模块环境(非全局对象),不做受限全局名检查。
|
||
let _ = self.predeclare_lexical_declarations(body, ctx);
|
||
|
||
// 闭包捕获分析(import 绑定名纳入 own_bindings,供嵌套函数 cell 捕获)。
|
||
ctx.own_bindings = collect_own_binding_names(&[], body);
|
||
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", &[])?;
|
||
ctx.module_ns_reg = Some(ns_reg);
|
||
|
||
// —— 依赖收集(import + re-export/star 的 source,按首次出现去重)——
|
||
let base_dir = Path::new(module_path)
|
||
.parent()
|
||
.map(|p| p.to_string_lossy().to_string())
|
||
.unwrap_or_default();
|
||
let mut deps: Vec<(String, Vec<(String, String)>)> = Vec::new();
|
||
for stmt in body {
|
||
let (spec, attrs) = match stmt {
|
||
Statement::ImportDeclaration(imp) => {
|
||
(imp.source.value.to_string(), module_attributes(&imp.with_clause))
|
||
}
|
||
Statement::ExportNamedDeclaration(exp) => {
|
||
if let Some(src) = &exp.source {
|
||
(src.value.to_string(), module_attributes(&exp.with_clause))
|
||
} else {
|
||
continue;
|
||
}
|
||
}
|
||
Statement::ExportAllDeclaration(exp) => {
|
||
(exp.source.value.to_string(), module_attributes(&exp.with_clause))
|
||
}
|
||
_ => continue,
|
||
};
|
||
if !deps.iter().any(|(s, _)| *s == spec) {
|
||
deps.push((spec, attrs));
|
||
}
|
||
}
|
||
|
||
// —— 编译并求值依赖 ——
|
||
for (spec, attrs) in deps {
|
||
let attr_refs: Vec<(&str, &str)> = attrs.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
|
||
let resolved = loader.resolve(&base_dir, &spec, &attr_refs)?;
|
||
let dep_ns_reg = if resolved.path == module_path {
|
||
// 自导入:读自身命名空间(依赖即本模块,勿递归)。
|
||
ns_reg
|
||
} else {
|
||
if path_stack.contains(&resolved.path) {
|
||
return Err(format!("circular module import not supported: {}", resolved.path));
|
||
}
|
||
path_stack.push(resolved.path.clone());
|
||
let (dep_ir, dep_reassignable) = match resolved.kind {
|
||
ModuleKind::Js => self.compile_js_dep(&resolved, loader, path_stack)?,
|
||
ModuleKind::Json => (self.compile_data_dep("json", &resolved.source)?, false),
|
||
ModuleKind::Text => (self.compile_data_dep("text", &resolved.source)?, false),
|
||
ModuleKind::Bytes => return Err("bytes module not supported".into()),
|
||
};
|
||
path_stack.pop();
|
||
ctx.nested.push(dep_ir);
|
||
ctx.module_dep_reassignable.insert(spec.clone(), dep_reassignable);
|
||
let fn_reg = ctx.alloc_reg();
|
||
ctx.inst(Inst::create_closure(Operand::Reg(fn_reg), ctx.nested.len() as u16));
|
||
self.emit_module_call(ctx, "__moduleEval", &[fn_reg])?
|
||
};
|
||
ctx.module_dep_ns_regs.insert(spec.clone(), dep_ns_reg);
|
||
ctx.module_dep_paths.insert(spec.clone(), resolved.path.clone());
|
||
if resolved.path == module_path {
|
||
// 自导入:绑定走别名语义(源导出在 body 执行中才就绪),
|
||
// 不能像普通依赖那样链接期快照。
|
||
ctx.module_self_import_specs.insert(spec.clone());
|
||
}
|
||
}
|
||
|
||
// —— 导入绑定初始化(普通依赖为链接期快照;命名空间绑定直接引用 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()));
|
||
// 导出名 → 本模块源绑定名:self-import 的局部名别名到该源绑定。star 转发的
|
||
// 自导入名不在表内(退化为下方非别名占位路径)。
|
||
let export_name_map = module_export_name_map(body, &ctx.module_self_import_specs);
|
||
// 依赖模块的可重赋导出标志:仅依赖模块(非入口)参与判定,入口模块恒假,
|
||
// 使纯导出入口(含 `export let x`)的 IR 逐字节不变。
|
||
ctx.module_live_dep = !top_level && module_has_reassignable_export(body, &export_name_map);
|
||
// live 命名空间门控:仅自导入 `import * as ns from './self'` 的模块需要预注册
|
||
// 真实导出槽,其余模块(含普通外部命名空间导入)编译产物逐字节不变。
|
||
let mut has_self_ns_import = false;
|
||
for stmt in body {
|
||
if let Statement::ImportDeclaration(imp) = stmt {
|
||
let dep_spec = imp.source.value.to_string();
|
||
let dep_ns_reg = *ctx
|
||
.module_dep_ns_regs
|
||
.get(&dep_spec)
|
||
.ok_or_else(|| format!("module dependency missing: {dep_spec}"))?;
|
||
let is_self = ctx.module_self_import_specs.contains(&dep_spec);
|
||
// 再导出重分类依据:非自导入局部名 → (依赖路径, 导入名)。
|
||
let dep_path = ctx.module_dep_paths.get(&dep_spec).cloned().unwrap_or_default();
|
||
if let Some(specifiers) = &imp.specifiers {
|
||
for sp in specifiers {
|
||
match sp {
|
||
ImportDeclarationSpecifier::ImportSpecifier(s) => {
|
||
let imported = module_export_name_str(&s.imported);
|
||
if !is_self {
|
||
ctx.module_import_origins
|
||
.insert(s.local.name.to_string(), (dep_path.clone(), imported.clone()));
|
||
}
|
||
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 {
|
||
let alias_reg = self.emit_self_alias_placeholder(s.local.name.as_str(), ctx)?;
|
||
ctx.module_self_aliases.entry(imported).or_default().push(alias_reg);
|
||
} else {
|
||
let name_reg = self.load_string_const(&imported, ctx);
|
||
let val_reg =
|
||
self.emit_module_call(ctx, "__moduleLinkGet", &[dep_ns_reg, name_reg])?;
|
||
self.emit_bind_target(
|
||
s.local.name.as_str(),
|
||
val_reg,
|
||
VariableDeclarationKind::Const,
|
||
true,
|
||
false,
|
||
ctx,
|
||
)?;
|
||
// 依赖含可重赋导出:登记活读映射,顶层读点改走
|
||
// `__moduleGet`,快照绑定仅服务嵌套闭包读。
|
||
if ctx.module_dep_reassignable.get(&dep_spec).copied().unwrap_or(false) {
|
||
ctx.module_live_imports
|
||
.insert(s.local.name.to_string(), (dep_ns_reg, imported.clone()));
|
||
}
|
||
}
|
||
}
|
||
ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => {
|
||
if !is_self {
|
||
ctx.module_import_origins.insert(
|
||
s.local.name.to_string(),
|
||
(dep_path.clone(), DEFAULT_EXPORT_NAME.to_string()),
|
||
);
|
||
}
|
||
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 {
|
||
let alias_reg = self.emit_self_alias_placeholder(s.local.name.as_str(), ctx)?;
|
||
ctx.module_self_aliases
|
||
.entry(DEFAULT_EXPORT_NAME.to_string())
|
||
.or_default()
|
||
.push(alias_reg);
|
||
} else {
|
||
let name_reg = self.load_string_const(DEFAULT_EXPORT_NAME, ctx);
|
||
let val_reg =
|
||
self.emit_module_call(ctx, "__moduleLinkGet", &[dep_ns_reg, name_reg])?;
|
||
self.emit_bind_target(
|
||
s.local.name.as_str(),
|
||
val_reg,
|
||
VariableDeclarationKind::Const,
|
||
true,
|
||
false,
|
||
ctx,
|
||
)?;
|
||
// 默认导入同命名导入:可重赋依赖的 default 导出活读。
|
||
if ctx.module_dep_reassignable.get(&dep_spec).copied().unwrap_or(false) {
|
||
ctx.module_live_imports.insert(
|
||
s.local.name.to_string(),
|
||
(dep_ns_reg, DEFAULT_EXPORT_NAME.to_string()),
|
||
);
|
||
}
|
||
}
|
||
}
|
||
ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => {
|
||
// 命名空间绑定引用 ns 对象本身:自导入时同一对象,
|
||
// body 执行后导出自然可见。
|
||
if is_self {
|
||
has_self_ns_import = true;
|
||
} else {
|
||
ctx.module_import_origins.insert(
|
||
s.local.name.to_string(),
|
||
(dep_path.clone(), MODULE_NAMESPACE_BINDING.to_string()),
|
||
);
|
||
}
|
||
self.emit_bind_target(
|
||
s.local.name.as_str(),
|
||
dep_ns_reg,
|
||
VariableDeclarationKind::Const,
|
||
true,
|
||
false,
|
||
ctx,
|
||
)?;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// live 命名空间激活:自导入 ns(本模块自身观测 ns)或本模块含可重赋导出
|
||
// (外部导入方需活读)。非 live 模块写穿与预注册均不发射,IR 逐字节不变。
|
||
let live_active = has_self_ns_import || ctx.module_live_dep;
|
||
ctx.module_live_ns_active = live_active;
|
||
|
||
// —— 写穿反演:源绑定槽寄存器 → 导出名集合。仅 live 模块填充:
|
||
// 非 live 模块不激活写穿,编译产物逐字节零 IR 变化。以槽位为键使块级/catch
|
||
// 同名遮蔽解析到内层槽时不命中;按名排序保证多导出名映射到同一源绑定的
|
||
// 发射顺序确定。——
|
||
if live_active {
|
||
let mut pairs: Vec<(&String, &String)> =
|
||
export_name_map.iter().map(|(exported, source)| (source, exported)).collect();
|
||
pairs.sort();
|
||
for (source, exported) in pairs {
|
||
let Some(reg) = ctx.scopes.symbols.lookup_any(source) else {
|
||
continue;
|
||
};
|
||
ctx.module_local_export_regs.entry(reg).or_default().push(exported.clone());
|
||
}
|
||
}
|
||
|
||
// —— live 命名空间预注册:早于 hoisted 函数声明的 __moduleSet 与 body 求值,
|
||
// 按名序预注册全部本地导出名,使自导入 ns 在读点先观察到未初始化状态。
|
||
// var/函数类导出(VarScopedDeclarations)在实例化期即初始化为 undefined,
|
||
// lexical/class 保持未初始化(TDZ)。 ——
|
||
if live_active {
|
||
let var_scoped = module_var_scoped_export_names(body);
|
||
let mut export_names: Vec<String> = own_export_names.into_iter().collect();
|
||
export_names.sort();
|
||
for name in export_names {
|
||
let name_reg = self.load_string_const(&name, ctx);
|
||
let var_like_reg = self.load_bool_const(var_scoped.contains(&name), ctx);
|
||
self.emit_module_call(ctx, "__modulePreRegister", &[ns_reg, name_reg, var_like_reg])?;
|
||
}
|
||
}
|
||
|
||
// —— 别名捕获后处理:源绑定被嵌套函数捕获时,别名必须落到同一 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,
|
||
));
|
||
}
|
||
}
|
||
}
|
||
// 被捕获模块词法(let/const/class)入口 TDZ 占位 cell:模块词法导出
|
||
// 既有预注册保持未初始化 TDZ(命名空间对象面,按导出名),入口 cell
|
||
// 只覆盖被嵌套函数捕获的名(按捕获名),两面名集独立。声明语句的
|
||
// MAKE_CELL 按占位更新语义原位翻转;直接子级 only,块级名不提升。
|
||
let mut lex_names: Vec<String> = crate::capture::collect_direct_lexical_names(body)
|
||
.into_iter()
|
||
.filter(|n| !ctx.param_names.contains(n.as_str()))
|
||
.filter(|n| ctx.captured_bindings.contains_key(n))
|
||
.collect();
|
||
lex_names.sort();
|
||
if !lex_names.is_empty() {
|
||
let undef_reg = self.emit_undefined(ctx);
|
||
for name in lex_names {
|
||
if let Some(&cell_idx) = ctx.captured_bindings.get(&name) {
|
||
// 未初始化标志折入 16 位立即数高字节(0x0100),dispatch 侧
|
||
// 按字节拆回两字段。
|
||
ctx.inst(Inst::new(
|
||
OpCode::MAKE_CELL,
|
||
Operand::Reg(undef_reg),
|
||
Operand::Imm(cell_idx as u16 | 0x0100),
|
||
Operand::None,
|
||
));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// —— re-export(export { x } from 'mod')链接检查:实例化期解析,须先于
|
||
// body 求值。$DONOTEVALUATE 是 test262 测试框架的断言工具,断言链接错误先于
|
||
// body 求值抛出,故链接检查必须先于 body 发射。——
|
||
for stmt in body {
|
||
if let Statement::ExportNamedDeclaration(exp) = stmt {
|
||
if exp.declaration.is_some() {
|
||
continue;
|
||
}
|
||
if let Some(src) = &exp.source {
|
||
let dep_spec = src.value.to_string();
|
||
if ctx.module_self_import_specs.contains(&dep_spec) {
|
||
continue; // 自身导出未就绪,body 执行中再解析
|
||
}
|
||
if let Some(&dep_ns_reg) = ctx.module_dep_ns_regs.get(&dep_spec) {
|
||
for spec in &exp.specifiers {
|
||
let local_name = module_export_name_str(&spec.local);
|
||
let name_reg = self.load_string_const(&local_name, ctx);
|
||
self.emit_module_call(ctx, "__moduleLinkGet", &[dep_ns_reg, name_reg])?;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// —— body(函数声明 hoisting 先发,随后其余;import 已由 prelude 处理)——
|
||
// directive 序言是 AST 独立字段(顶格字符串字面量,不在 body):纯字符串
|
||
// 无副作用,按源序先于 body 发射,与脚本 emit_program 的发射口径一致。
|
||
for dir in &program.directives {
|
||
self.emit_string_literal_expression(&dir.expression, ctx)?;
|
||
}
|
||
for stmt in body {
|
||
if is_hoisted_function_decl(stmt) {
|
||
self.emit_statement(stmt, ctx)?;
|
||
}
|
||
}
|
||
for stmt in body {
|
||
if is_hoisted_function_decl(stmt) {
|
||
continue;
|
||
}
|
||
if matches!(stmt, Statement::ImportDeclaration(_)) {
|
||
continue;
|
||
}
|
||
self.emit_statement(stmt, ctx)?;
|
||
}
|
||
|
||
// —— 自别名收尾刷新:star 复制等跨模块注册路径后占位槽可能仍为旧值,
|
||
// 封冻命名空间前统一从命名空间非抛出回读兜底。 ——
|
||
self.emit_self_alias_refresh(ctx, ns_reg)?;
|
||
|
||
// —— 收尾:封冻命名空间并返回(顶层 RETURN 亦终止 run)——
|
||
self.emit_module_call(ctx, "__moduleSeal", &[ns_reg])?;
|
||
if top_level {
|
||
// 顶层模块求值完成值为空记录(对外表现 undefined);依赖模块求值必须返回
|
||
// 命名空间对象(`__moduleEval` 以依赖返回值作命名空间)——两条路径的返回
|
||
// 值契约不同,顶层路径不得复用依赖路径的返回值。
|
||
let undef_idx = ctx.add_constant(Constant::Undefined);
|
||
let r = ctx.alloc_reg();
|
||
ctx.inst(Inst::load_const(Operand::Reg(r), undef_idx));
|
||
ctx.inst(Inst::ret(Operand::Reg(r), 0, 0));
|
||
} else {
|
||
ctx.inst(Inst::ret(Operand::Reg(ns_reg), 0, 0));
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// 编译 JS 依赖模块(递归),回传 IR 与「是否含可重赋导出」标志。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 标志取自依赖 ctx 的 `module_live_dep`;入口模块不参与,依赖模块恒按
|
||
/// `!top_level` 判定,故 const-only 依赖回传 false。
|
||
///
|
||
/// # 注意事项
|
||
/// - 导入方据标志决定是否登记活读映射;标志不改变本模块 IR。
|
||
fn compile_js_dep(
|
||
&self, resolved: &ResolvedModule, loader: &mut dyn ModuleSourceLoader, path_stack: &mut Vec<String>,
|
||
) -> Result<(IRFunction, bool), String> {
|
||
let alloc = oxide_parser::Allocator::default();
|
||
let program = oxide_parser::parse_module(&alloc, &resolved.source).map_err(|errs| {
|
||
format!(
|
||
"module parse error: {}",
|
||
errs.first().map(|e| e.message.as_str()).unwrap_or("parse failed")
|
||
)
|
||
})?;
|
||
let mut ctx = CompileCtx::new();
|
||
ctx.is_global_scope = false;
|
||
// 依赖模块经 parse_module 解析,顶层恒严格模式。
|
||
ctx.is_strict = true;
|
||
self.emit_module_into_ctx(&program, &resolved.path, loader, path_stack, &mut ctx, false)?;
|
||
let reassignable = ctx.module_live_dep;
|
||
Ok((ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None), reassignable))
|
||
}
|
||
|
||
/// 编译数据模块(json/text):body 仅为 __moduleData 调用 + RETURN。
|
||
fn compile_data_dep(&self, kind: &str, content: &str) -> Result<IRFunction, String> {
|
||
let mut ctx = CompileCtx::new();
|
||
let kind_reg = self.load_string_const(kind, &mut ctx);
|
||
let content_reg = self.load_string_const(content, &mut ctx);
|
||
let val_reg = self.emit_module_call(&mut ctx, "__moduleData", &[kind_reg, content_reg])?;
|
||
ctx.inst(Inst::ret(Operand::Reg(val_reg), 0, 0));
|
||
Ok(ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None))
|
||
}
|
||
|
||
/// 内部辅助调用:CALL_NATIVE 到全局 native(__module*),返回结果寄存器。
|
||
pub(crate) fn emit_module_call(&self, ctx: &mut CompileCtx, name: &str, arg_regs: &[u32]) -> Result<u32, String> {
|
||
let callee_reg = ctx.lookup_or_builtin(name)?;
|
||
let this_reg = ctx.alloc_reg();
|
||
let undef_idx = ctx.add_constant(Constant::Undefined);
|
||
ctx.inst(Inst::load_const(Operand::Reg(this_reg), undef_idx));
|
||
// CALL_NATIVE 按 regs[first_arg + i] 连续读参数:把非连续虚拟寄存器(vreg,经寄存器
|
||
// 分配后映射为物理号)打包为连续寄存器块。
|
||
let mut packed = arg_regs.to_vec();
|
||
let first_arg_reg = if packed.is_empty() { this_reg } else { pack_arg_regs(&mut packed, ctx) };
|
||
ctx.inst(Inst::call_native(
|
||
Operand::Reg(callee_reg),
|
||
Operand::Reg(this_reg),
|
||
Operand::Reg(first_arg_reg),
|
||
packed.len() as u8,
|
||
));
|
||
let result_reg = ctx.alloc_reg();
|
||
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::None, Operand::None));
|
||
Ok(result_reg)
|
||
}
|
||
|
||
pub(crate) fn load_string_const(&self, s: &str, ctx: &mut CompileCtx) -> u32 {
|
||
let idx = ctx.add_constant(Constant::String(s.to_string()));
|
||
let reg = ctx.alloc_reg();
|
||
ctx.inst(Inst::load_const(Operand::Reg(reg), idx));
|
||
reg
|
||
}
|
||
|
||
fn load_bool_const(&self, b: bool, ctx: &mut CompileCtx) -> u32 {
|
||
let idx = ctx.add_constant(Constant::Boolean(b));
|
||
let reg = ctx.alloc_reg();
|
||
ctx.inst(Inst::load_const(Operand::Reg(reg), idx));
|
||
reg
|
||
}
|
||
|
||
/// 自导入别名回写:导出名若被本模块 self-import 绑定别名引用,
|
||
/// export 语句执行时同步把值写回本地绑定槽。同名多槽(default 双绑定)
|
||
/// 逐槽并列回写。
|
||
fn emit_self_alias_write(&self, ctx: &mut CompileCtx, export_name: &str, value_reg: u32) {
|
||
let regs = ctx.module_self_aliases.get(export_name).cloned().unwrap_or_default();
|
||
for &alias_reg in ®s {
|
||
ctx.inst(Inst::new(
|
||
OpCode::STORE_VAR,
|
||
Operand::Reg(alias_reg),
|
||
Operand::Reg(value_reg),
|
||
Operand::None,
|
||
));
|
||
}
|
||
}
|
||
|
||
/// 自导入别名统一刷新:对每个登记的自导入占位名,从命名空间非抛出读回写
|
||
/// 占位槽(命名空间无该名时保留占位值,不抛错)。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 ®s {
|
||
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 转发的自导入名)时,
|
||
/// prelude 把局部名绑成已初始化的 undefined 常量槽并登记回写,由 export
|
||
/// 语句执行时就地刷新,body 收尾再统一从命名空间回读兜底。
|
||
/// 返回该本地绑定槽寄存器。
|
||
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(())
|
||
}
|
||
|
||
/// 发 `__moduleSetCell`:把导出条目挂到源绑定的共享 cell(cell_idx 常量传参)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `cell_idx` 来自 `ctx.captured_bindings`,在同一模块顶层帧的 `cell_stack`
|
||
/// 内由声明点的 MAKE_CELL 建好;导出注册点晚于声明点,故 cell 必已存在。
|
||
///
|
||
/// # 副作用
|
||
/// - 依赖运行期 `module_frame_cell(cell_idx)` 取当前字节码帧 cell,改命名空间
|
||
/// 条目状态为共享 cell。
|
||
fn emit_module_set_cell(
|
||
&self, ctx: &mut CompileCtx, ns_reg: u32, export_name: &str, cell_idx: u8,
|
||
) -> Result<(), String> {
|
||
let name_reg = self.load_string_const(export_name, ctx);
|
||
let idx = ctx.add_constant(Constant::Int(cell_idx as i32));
|
||
let cell_reg = ctx.alloc_reg();
|
||
ctx.inst(Inst::load_const(Operand::Reg(cell_reg), idx));
|
||
self.emit_module_call(ctx, "__moduleSetCell", &[ns_reg, name_reg, cell_reg])?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 导出注册:被捕获的源绑定在 live 模块挂共享 cell(闭包写随活读可见),
|
||
/// 其余回退 `__moduleSet` 快照/写穿路径。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 仅 `module_live_ns_active` 模块激活;非 live 模块恒回退 `__moduleSet`,
|
||
/// 编译产物与引入 Cell 前逐字节一致。
|
||
/// - `source_name` 命中 `module_live_imports`(导入快照)时回退:不得把导入
|
||
/// 本地槽误注册为活条目。
|
||
/// - 源绑定须在 `captured_bindings` 内且当前作用域可解析(`visible_binding_reg`
|
||
/// 有值)——块退出后残留的同名 cell 不可见,此时回退快照注册。
|
||
///
|
||
/// # 副作用
|
||
/// - 命中时发 `__moduleSetCell`(条目状态改 Cell);否则发 `__moduleSet`。
|
||
fn emit_export_registration(
|
||
&self, ctx: &mut CompileCtx, ns_reg: u32, exported_name: &str, source_name: &str, value_reg: u32,
|
||
) -> Result<(), String> {
|
||
if ctx.module_live_ns_active
|
||
&& !ctx.module_live_imports.contains_key(source_name)
|
||
&& ctx.visible_binding_reg(source_name).is_some()
|
||
{
|
||
if let Some(&cell_idx) = ctx.captured_bindings.get(source_name) {
|
||
return self.emit_module_set_cell(ctx, ns_reg, exported_name, cell_idx);
|
||
}
|
||
}
|
||
self.emit_module_set(ctx, ns_reg, exported_name, value_reg)
|
||
}
|
||
|
||
/// 发 `__moduleSetReexport`:注册带 `(dep_path, imported)` 来源身份的再导出。
|
||
fn emit_module_set_reexport(
|
||
&self, ctx: &mut CompileCtx, ns_reg: u32, export_name: &str, value_reg: u32, dep_path: &str, imported: &str,
|
||
) -> Result<(), String> {
|
||
let name_reg = self.load_string_const(export_name, ctx);
|
||
let path_reg = self.load_string_const(dep_path, ctx);
|
||
let imported_reg = self.load_string_const(imported, ctx);
|
||
self.emit_module_call(ctx, "__moduleSetReexport", &[ns_reg, name_reg, value_reg, path_reg, imported_reg])?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 顶层对导出源绑定的写入同步到命名空间:按反演表把新值写入每个导出名。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 仅在模块顶层 live 模块激活:`module_local_export_regs` 只由自导入 ns 模块
|
||
/// 填充,嵌套函数 ctx 不继承该表,行为自然为零。
|
||
/// - `name` 按当前作用域解析到绑定槽,槽不在表内(未声明名、块级/catch 同名
|
||
/// 遮蔽等)时直接返回,不误写同名导出。
|
||
/// - 调用方保证 `value_reg` 是写入后的当前值;const 抛错分支不得调用本函数。
|
||
///
|
||
/// # 副作用
|
||
/// - 每个命中导出名发射一次 `__moduleSet` 调用,更新命名空间条目与真实槽值。
|
||
pub(crate) fn emit_module_write_through(
|
||
&self, name: &str, value_reg: u32, ctx: &mut CompileCtx,
|
||
) -> Result<(), String> {
|
||
let Some(ns_reg) = ctx.module_ns_reg else {
|
||
return Ok(());
|
||
};
|
||
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 {
|
||
self.emit_module_set(ctx, ns_reg, &export_name, value_reg)?;
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// 别名捕获后处理:把源/别名对合并到同一 cell。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `captured_bindings` 是名字级索引;源与别名共用寄存器槽,但捕获分析按
|
||
/// 名字给出各自(或仅一方)的 entry。同一源的多个别名须并入同一 cell:
|
||
/// 声明点的 MAKE_CELL 只按源名发射,未被覆盖的别名 cell 会停留未初始化。
|
||
/// - 权威 cell 优先取源名自身的 entry(源被捕获时声明点的 MAKE_CELL 写它);
|
||
/// 源未被捕获而别名被捕获时取组内别名已分配的 entry,再把源名补进映射。
|
||
/// - 两者都未被捕获时读路径直接读源寄存器,天然活值,无需处理。
|
||
///
|
||
/// # 副作用
|
||
/// - 修改 `captured_bindings` 映射;每个名字仍占一个 entry,容量口径不变
|
||
/// (`cells_needed` 按名字集大小取,cell 下标可重复,见 `assemble_ir`)。
|
||
fn reconcile_alias_captures(&self, ctx: &mut CompileCtx) {
|
||
let pairs = std::mem::take(&mut ctx.module_alias_pairs);
|
||
// 以最终基源名为组键统一组内全部名字。逐个 pair 直接覆盖会让同源的多个
|
||
// 别名中后处理者把源改指向新 cell,先处理的别名与源分裂成两个 cell。
|
||
let mut authority: HashMap<String, u8> = HashMap::new();
|
||
for (local, source) in pairs {
|
||
let source = ctx.scopes.symbols.resolve_alias_base(&source).to_string();
|
||
let cell = authority.get(&source).copied().or_else(|| {
|
||
ctx.captured_bindings
|
||
.get(source.as_str())
|
||
.or_else(|| ctx.captured_bindings.get(local.as_str()))
|
||
.copied()
|
||
});
|
||
let Some(cell) = cell else { continue };
|
||
authority.insert(source.clone(), cell);
|
||
ctx.captured_bindings.insert(source, cell);
|
||
ctx.captured_bindings.insert(local, cell);
|
||
}
|
||
// cell 下标由名字排序分配,可重复使用;容量按名字集大小取,故每个下标
|
||
// 必须落在名字集范围内,否则子函数 upvalue 会索引到未分配的 cell。
|
||
debug_assert!(
|
||
ctx.captured_bindings
|
||
.values()
|
||
.all(|&i| (i as usize) < ctx.captured_bindings.len()),
|
||
"cell 下标须落在 captured_bindings 名字集范围内"
|
||
);
|
||
}
|
||
|
||
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)
|
||
}
|
||
|
||
/// export 语句 emit:声明照常 + 导出值就地注册到命名空间。
|
||
pub(crate) fn emit_module_export_domain(
|
||
&self, stmt: &Statement, ctx: &mut CompileCtx,
|
||
) -> Result<Option<u32>, String> {
|
||
let ns_reg = ctx.module_ns_reg.ok_or_else(|| "export outside module context".to_string())?;
|
||
match stmt {
|
||
Statement::ExportNamedDeclaration(exp) => {
|
||
if let Some(decl) = &exp.declaration {
|
||
match decl {
|
||
Declaration::VariableDeclaration(vd) => {
|
||
self.emit_variable_declaration(vd, ctx)?;
|
||
for d in &vd.declarations {
|
||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||
let name = bi.name.as_str();
|
||
let val_reg = self.load_var_reg(name, ctx)?;
|
||
self.emit_export_registration(ctx, ns_reg, name, name, val_reg)?;
|
||
self.emit_self_alias_write(ctx, name, val_reg);
|
||
}
|
||
}
|
||
}
|
||
Declaration::FunctionDeclaration(fd) => {
|
||
self.emit_function_declaration(fd, ctx)?;
|
||
if let Some(id) = &fd.id {
|
||
let name = id.name.as_str();
|
||
let val_reg = self.load_var_reg(name, ctx)?;
|
||
self.emit_export_registration(ctx, ns_reg, name, name, val_reg)?;
|
||
self.emit_self_alias_write(ctx, name, val_reg);
|
||
}
|
||
}
|
||
Declaration::ClassDeclaration(cl) => {
|
||
self.emit_class_declaration(cl, ctx)?;
|
||
if let Some(id) = &cl.id {
|
||
let name = id.name.as_str();
|
||
let val_reg = self.load_var_reg(name, ctx)?;
|
||
self.emit_export_registration(ctx, ns_reg, name, name, val_reg)?;
|
||
self.emit_self_alias_write(ctx, name, val_reg);
|
||
}
|
||
}
|
||
_ => return Err("unsupported export declaration".into()),
|
||
}
|
||
} else {
|
||
let dep_source = exp.source.as_ref().map(|src| src.value.to_string());
|
||
for spec in &exp.specifiers {
|
||
let local_name = module_export_name_str(&spec.local);
|
||
let exported_name = module_export_name_str(&spec.exported);
|
||
// 有 source:来源身份为 (依赖模块路径, 导入名)。
|
||
if let Some(source) = &dep_source {
|
||
let dep_ns_reg = ctx
|
||
.module_dep_ns_regs
|
||
.get(source.as_str())
|
||
.copied()
|
||
.ok_or_else(|| format!("export from unknown module: {source}"))?;
|
||
let dep_path = ctx
|
||
.module_dep_paths
|
||
.get(source.as_str())
|
||
.cloned()
|
||
.ok_or_else(|| format!("export from unknown module: {source}"))?;
|
||
let name_reg = self.load_string_const(&local_name, ctx);
|
||
let val_reg = self.emit_module_call(ctx, "__moduleLinkGet", &[dep_ns_reg, name_reg])?;
|
||
self.emit_module_set_reexport(
|
||
ctx,
|
||
ns_reg,
|
||
&exported_name,
|
||
val_reg,
|
||
&dep_path,
|
||
&local_name,
|
||
)?;
|
||
// 自导入名可能来自 star 转发(占位路径):导出语句执行时
|
||
// 就地刷新本地槽,覆盖同模块 body 内导出后读取的形态。
|
||
self.emit_self_alias_write(ctx, &exported_name, val_reg);
|
||
continue;
|
||
}
|
||
// 无 source 但局部名是导入绑定:按规范重分类为间接导出,来源为
|
||
// (依赖模块路径, 导入名);命名空间导入的导入名为共享哨兵。
|
||
if let Some((dep_path, imported)) = ctx.module_import_origins.get(&local_name).cloned() {
|
||
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_self_alias_write(ctx, &exported_name, val_reg);
|
||
continue;
|
||
}
|
||
// 本地导出:来源恒为 Local。
|
||
let val_reg = self.load_var_reg(&local_name, ctx)?;
|
||
self.emit_export_registration(ctx, ns_reg, &exported_name, &local_name, val_reg)?;
|
||
}
|
||
}
|
||
}
|
||
Statement::ExportDefaultDeclaration(exp) => {
|
||
let (val_reg, source_name) = match &exp.declaration {
|
||
ExportDefaultDeclarationKind::FunctionDeclaration(fd) => {
|
||
let reg = self.emit_function_expression(fd, ctx)?;
|
||
// 具名 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);
|
||
}
|
||
// 具名 default 的注册源是该 id 绑定(可重赋),匿名退化为合成名。
|
||
(reg, binding.unwrap_or_else(|| SYNTHETIC_DEFAULT_BINDING.to_string()))
|
||
}
|
||
ExportDefaultDeclarationKind::ClassDeclaration(cl) => {
|
||
let reg = self.emit_class_with_binding(
|
||
cl,
|
||
ctx,
|
||
None,
|
||
cl.id.is_none().then_some(DEFAULT_EXPORT_NAME),
|
||
)?;
|
||
if let Some(id) = &cl.id {
|
||
self.emit_bind_target(
|
||
id.name.as_str(),
|
||
reg,
|
||
VariableDeclarationKind::Const,
|
||
true,
|
||
false,
|
||
ctx,
|
||
)?;
|
||
(reg, id.name.to_string())
|
||
} 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, SYNTHETIC_DEFAULT_BINDING.to_string())
|
||
}
|
||
}
|
||
other => {
|
||
let expr = other
|
||
.as_expression()
|
||
.ok_or_else(|| "unsupported export default declaration".to_string())?;
|
||
let reg = if crate::is_anonymous_function_definition(expr) {
|
||
match strip_parens(expr) {
|
||
// 匿名类:构造器落名后还会 push 方法子模块,必须经
|
||
// implicit_name 写构造器;具名类由 ctor_name 优先。
|
||
Expression::ClassExpression(cl) => {
|
||
self.emit_class_with_binding(cl, ctx, None, Some(DEFAULT_EXPORT_NAME))?
|
||
}
|
||
// 函数/生成器/箭头:发射期只 push 一个子模块,末项即目标;
|
||
// 具名函数表达式已有名,守卫不覆盖。
|
||
_ => {
|
||
let reg = self.emit_expression(expr, ctx)?;
|
||
set_implicit_name_of_last_nested(ctx, DEFAULT_EXPORT_NAME);
|
||
reg
|
||
}
|
||
}
|
||
} 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, SYNTHETIC_DEFAULT_BINDING.to_string())
|
||
}
|
||
};
|
||
self.emit_export_registration(ctx, ns_reg, DEFAULT_EXPORT_NAME, &source_name, val_reg)?;
|
||
self.emit_self_alias_write(ctx, DEFAULT_EXPORT_NAME, val_reg);
|
||
}
|
||
Statement::ExportAllDeclaration(exp) => {
|
||
let source = exp.source.value.as_str();
|
||
let dep_ns_reg = *ctx
|
||
.module_dep_ns_regs
|
||
.get(source)
|
||
.ok_or_else(|| format!("export * from unknown module: {source}"))?;
|
||
let dep_path = ctx
|
||
.module_dep_paths
|
||
.get(source)
|
||
.cloned()
|
||
.ok_or_else(|| format!("export * from unknown module: {source}"))?;
|
||
if let Some(exported) = &exp.exported {
|
||
// export * as ns from '...':命名空间再导出,绑定身份用共享哨兵。
|
||
let exported_name = module_export_name_str(exported);
|
||
self.emit_module_set_reexport(
|
||
ctx,
|
||
ns_reg,
|
||
&exported_name,
|
||
dep_ns_reg,
|
||
&dep_path,
|
||
MODULE_NAMESPACE_BINDING,
|
||
)?;
|
||
// 自导入的 star-as 名走占位路径:就地刷新为命名空间对象。
|
||
self.emit_self_alias_write(ctx, &exported_name, dep_ns_reg);
|
||
} else {
|
||
// 来源传播:star 复制按 (dep_path, 绑定) 判同名冲突。
|
||
let path_reg = self.load_string_const(&dep_path, ctx);
|
||
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()),
|
||
}
|
||
Ok(None)
|
||
}
|
||
}
|