mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 跨模块可重赋导出活读
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
//! 模块命名空间与求值辅助(import 实现内部用,非 JS 可见标准 API)。
|
||||
//!
|
||||
//! 命名约定 `__module*`:由编译器模块 prelude 发出(`lookup_or_builtin` 解析为全局槽,
|
||||
//! VM 在 frame push 时从 global 对象取回 native 函数)。当前为快照式链接:
|
||||
//! 依赖模块先整体求值并返回命名空间对象,导入方从中读取导出值;
|
||||
//! live binding / source-phase / defer 语义留待后续轮次。
|
||||
//! VM 在 frame push 时从 global 对象取回 native 函数)。依赖模块先整体求值并返回
|
||||
//! 命名空间对象,导入方从中读取导出值;依赖含可重赋导出时命名/默认导入经
|
||||
//! `__moduleGet` 活读,被捕获导出由 `__moduleSetCell` 挂共享 cell。
|
||||
//! source-phase / defer 语义留待后续轮次。
|
||||
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_runtime_api::{NativeResult, VmHost};
|
||||
@@ -512,6 +513,60 @@ pub fn module_set<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `__moduleSetCell(ns, name, cell_idx)`:把导出条目挂到模块顶层帧的共享 cell。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 校验 ns 为 module namespace 且已有条目表与同名条目(预注册保证存在)。
|
||||
/// 2. 取当前字节码帧第 `cell_idx` 个 cell(`VmHost::module_frame_cell`)。
|
||||
/// 3. 条目状态置 `Cell`,并把真实槽值同步为 cell 当前值一次。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 条目必须已预注册;无表、条目未命中或 cell 缺失均抛 TypeError,不静默退化。
|
||||
/// - `cell_idx` 来自 emit 期捕获索引,同一模块顶层帧内稳定。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 修改条目表状态与 ns 真实槽值;后续活值由共享 cell 承载。
|
||||
pub fn module_set_cell<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 4 {
|
||||
return type_error(vm, "__moduleSetCell: 3 arguments required");
|
||||
}
|
||||
let ns_val = vm.reg(args[1]);
|
||||
let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleSetCell: target is not an object") {
|
||||
Ok(Some(p)) => p,
|
||||
Ok(None) => return type_error(vm, "__moduleSetCell: 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 cell_idx = oxide_runtime_api::to_int32(vm.reg(args[3])) as u32;
|
||||
let obj = unsafe { &mut *ns_ptr };
|
||||
if !obj.is_module_namespace() {
|
||||
return type_error(vm, "__moduleSetCell: target is not a module namespace");
|
||||
}
|
||||
let table = ns_table_ptr(obj);
|
||||
if table.is_null() {
|
||||
return type_error(vm, "__moduleSetCell: export is not registered");
|
||||
}
|
||||
// SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。
|
||||
let Some(idx) = (unsafe { entry_index(&*table, name_si) }) else {
|
||||
return type_error(vm, "__moduleSetCell: export is not registered");
|
||||
};
|
||||
let Some(cell) = vm.module_frame_cell(cell_idx) else {
|
||||
return type_error(vm, "__moduleSetCell: cell is not available");
|
||||
};
|
||||
// SAFETY: cell 由 alloc_cell 分配并登记,至 full_reset 才释放。
|
||||
let value = unsafe { (*cell).value };
|
||||
// SAFETY: idx 来自刚完成的表内查找,仍在界内。
|
||||
unsafe {
|
||||
(&mut (*table).entries)[idx].state = ModuleNsState::Cell(cell);
|
||||
}
|
||||
// 真实槽同步一次 cell 当前值:条目表是活值权威,槽仅保留存在性与占位。
|
||||
if let Some(pos) = vm.get_own_property_slot(obj, name_si) {
|
||||
obj.set_prop_at(pos, value);
|
||||
}
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `__moduleSetReexport(ns, name, value, dep_path, imported)`:注册显式再导出
|
||||
/// (`export { x } from` / `export * as ns from` / 导入绑定再导出),记录跨模块
|
||||
/// 来源身份 `(dep_path, imported)`,使同一绑定经不同路径转发时来源 token 相等。
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use oxide_parser::{Expression, Statement};
|
||||
use oxide_parser::{Declaration, ExportDefaultDeclarationKind, Expression, Statement};
|
||||
|
||||
use super::collect_fn_param_names;
|
||||
use super::names::collect_own_binding_names;
|
||||
@@ -103,6 +103,60 @@ pub(crate) fn collect_captured_stmt(stmt: &Statement, own: &HashSet<String>, out
|
||||
}
|
||||
collect_class_capture_names(&cd.body, own, &class_shadow, out);
|
||||
}
|
||||
// export 包裹的变量/函数/类声明:捕获分析须下探声明体,否则被导出函数体内
|
||||
// 引用模块绑定(含自引用)不会被识别为捕获,闭包内读写退化为隐式全局。
|
||||
Statement::ExportNamedDeclaration(exp) => {
|
||||
if let Some(decl) = &exp.declaration {
|
||||
match decl {
|
||||
Declaration::VariableDeclaration(vd) => {
|
||||
for d in &vd.declarations {
|
||||
collect_captured_binding_keys(&d.id, own, out);
|
||||
if let Some(init) = &d.init {
|
||||
collect_captured_expr(init, own, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
Declaration::FunctionDeclaration(fd) => {
|
||||
let body: &[Statement] = fd.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||
collect_fn_default_captured(&fd.params, own, out);
|
||||
let mut fn_shadow = HashSet::new();
|
||||
fn_shadow.extend(collect_fn_param_names(&fd.params));
|
||||
fn_shadow.extend(collect_own_binding_names(&[], body));
|
||||
collect_capture_names_shadowed(body, own, &fn_shadow, out);
|
||||
}
|
||||
Declaration::ClassDeclaration(cd) => {
|
||||
let mut class_shadow = HashSet::new();
|
||||
if let Some(id) = &cd.id {
|
||||
class_shadow.insert(id.name.as_str().to_string());
|
||||
}
|
||||
collect_class_capture_names(&cd.body, own, &class_shadow, out);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::ExportDefaultDeclaration(exp) => match &exp.declaration {
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(fd) => {
|
||||
let body: &[Statement] = fd.body.as_ref().map(|b| &b.statements[..]).unwrap_or(&[]);
|
||||
collect_fn_default_captured(&fd.params, own, out);
|
||||
let mut fn_shadow = HashSet::new();
|
||||
fn_shadow.extend(collect_fn_param_names(&fd.params));
|
||||
fn_shadow.extend(collect_own_binding_names(&[], body));
|
||||
collect_capture_names_shadowed(body, own, &fn_shadow, out);
|
||||
}
|
||||
ExportDefaultDeclarationKind::ClassDeclaration(cd) => {
|
||||
let mut class_shadow = HashSet::new();
|
||||
if let Some(id) = &cd.id {
|
||||
class_shadow.insert(id.name.as_str().to_string());
|
||||
}
|
||||
collect_class_capture_names(&cd.body, own, &class_shadow, out);
|
||||
}
|
||||
other => {
|
||||
if let Some(e) = other.as_expression() {
|
||||
collect_captured_expr(e, own, out);
|
||||
}
|
||||
}
|
||||
},
|
||||
Statement::ExpressionStatement(es) => collect_captured_expr(&es.expression, own, out),
|
||||
Statement::ReturnStatement(rs) => {
|
||||
if let Some(a) = &rs.argument {
|
||||
|
||||
@@ -149,6 +149,19 @@ pub struct CompileCtx {
|
||||
/// 非自导入的导入局部名 → (依赖模块规范路径, 导入名)。命名空间导入的导入名为
|
||||
/// `oxide_kernel::MODULE_NAMESPACE_BINDING`;再导出据此重分类为间接导出。
|
||||
pub(crate) module_import_origins: HashMap<String, (String, String)>,
|
||||
/// 依赖 source 字符串 → 依赖模块是否含可重赋导出(`compile_js_dep` 回传;
|
||||
/// Json/Text 数据模块恒 false)。导入方据此把命名/默认导入登记为活读。
|
||||
pub(crate) module_dep_reassignable: HashMap<String, bool>,
|
||||
/// 导入本地名 → (依赖命名空间对象寄存器, 导出名):可重赋依赖的命名/默认导入
|
||||
/// 活读映射。顶层读点据此改发 `__moduleGet` 活读;仅模块顶层 ctx 填充,嵌套
|
||||
/// 函数 ctx 不继承,闭包内读退化为链接期快照。
|
||||
pub(crate) module_live_imports: HashMap<String, (u32, String)>,
|
||||
/// 本模块是否含可重赋导出。仅在依赖模块(`top_level == false`)且捕获/写穿
|
||||
/// 消费者需要时置真;入口模块恒假,保证纯导出入口 IR 零变化。
|
||||
pub(crate) module_live_dep: bool,
|
||||
/// live 命名空间激活:自导入 ns 或本模块含可重赋导出时为真。写穿反演与
|
||||
/// 导出预注册据此发射;非 live 模块编译产物逐字节不变。
|
||||
pub(crate) module_live_ns_active: bool,
|
||||
/// 自导入(import from 自身)的 source 字符串集合:绑定走别名语义,不能链接期快照。
|
||||
pub(crate) module_self_import_specs: HashSet<String>,
|
||||
/// 自导入别名:导出名 → 本地绑定槽寄存器(export 语句执行时回写绑定值)。
|
||||
@@ -240,6 +253,10 @@ impl CompileCtx {
|
||||
module_dep_ns_regs: HashMap::new(),
|
||||
module_dep_paths: HashMap::new(),
|
||||
module_import_origins: HashMap::new(),
|
||||
module_dep_reassignable: HashMap::new(),
|
||||
module_live_imports: HashMap::new(),
|
||||
module_live_dep: false,
|
||||
module_live_ns_active: false,
|
||||
module_self_import_specs: HashSet::new(),
|
||||
module_self_aliases: HashMap::new(),
|
||||
module_alias_pairs: Vec::new(),
|
||||
|
||||
@@ -10,8 +10,18 @@ use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
impl Emitter {
|
||||
/// 静态标识符读取:upvalue / 被捕获 cell / 全局槽。
|
||||
/// 静态标识符读取:可重赋依赖导入活读 / upvalue / 被捕获 cell / 全局槽。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 活读映射仅模块顶层 ctx 填充:嵌套函数内读 import 名仍走链接期快照
|
||||
/// (`current_upvalue_captures` 不在此列,闭包内不继承映射)。
|
||||
fn emit_static_identifier_read(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
// 可重赋依赖的命名/默认导入:读点直接查依赖命名空间当前值,跟随源模块重赋。
|
||||
if let Some((dep_ns, exported)) = ctx.module_live_imports.get(name).cloned() {
|
||||
let name_reg = self.load_string_const(&exported, ctx);
|
||||
return self.emit_module_call(ctx, "__moduleGet", &[dep_ns, name_reg]);
|
||||
}
|
||||
|
||||
for (uv_idx, up) in ctx.current_upvalue_captures.iter().enumerate() {
|
||||
if up.name == name {
|
||||
let r = ctx.alloc_reg();
|
||||
|
||||
+175
-37
@@ -1,10 +1,11 @@
|
||||
//! 模块编译:import/export 解析、依赖加载(递归)、快照式链接与命名空间。
|
||||
//!
|
||||
//! 普通依赖的导入绑定在 prelude 一次性链接为快照;自导入(`import ... from
|
||||
//! 自身`)的局部名解析为本模块源绑定的活别名,TDZ/提升/活值/不可变四语义全部
|
||||
//! 委托源绑定。`export` 语句就地注册导出值。
|
||||
//! 未支持:跨模块 live binding(导入绑定不跟随依赖模块值变化)、defer(延迟
|
||||
//! 求值)、source-phase;star 转发的自导入名退化为链接期快照;循环导入在编译期跳过。
|
||||
//! 普通依赖的导入绑定在 prelude 链接:依赖含可重赋导出时顶层读点经
|
||||
//! `__moduleGet` 活读,其余退化为快照;被闭包捕获的导出挂共享 cell,闭包内写
|
||||
//! 随活读可见。自导入(`import ... from 自身`)的局部名解析为本模块源绑定的活
|
||||
//! 别名,TDZ/提升/活值/不可变四语义全部委托源绑定。`export` 语句就地注册导出值。
|
||||
//! 未支持:嵌套闭包内读 import 名(仍为链接期快照)、defer(延迟求值)、
|
||||
//! source-phase;star 转发与经再导出链的活值仍为快照;循环导入在编译期跳过。
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::Path;
|
||||
@@ -248,6 +249,62 @@ fn module_var_scoped_export_names(body: &[Statement]) -> HashSet<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 {
|
||||
@@ -436,14 +493,15 @@ impl Emitter {
|
||||
return Err(format!("circular module import not supported: {}", resolved.path));
|
||||
}
|
||||
path_stack.push(resolved.path.clone());
|
||||
let dep_ir = match resolved.kind {
|
||||
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)?,
|
||||
ModuleKind::Text => self.compile_data_dep("text", &resolved.source)?,
|
||||
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])?
|
||||
@@ -468,6 +526,9 @@ impl Emitter {
|
||||
// 导出名 → 本模块源绑定名: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;
|
||||
@@ -517,6 +578,12 @@ impl Emitter {
|
||||
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) => {
|
||||
@@ -563,6 +630,13 @@ impl Emitter {
|
||||
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) => {
|
||||
@@ -591,11 +665,16 @@ impl Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
// —— 写穿反演:源绑定槽寄存器 → 导出名集合。仅 live 模块(自导入 ns)填充:
|
||||
// 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 has_self_ns_import {
|
||||
if live_active {
|
||||
let mut pairs: Vec<(&String, &String)> =
|
||||
export_name_map.iter().map(|(exported, source)| (source, exported)).collect();
|
||||
pairs.sort();
|
||||
@@ -611,7 +690,7 @@ impl Emitter {
|
||||
// 按名序预注册全部本地导出名,使自导入 ns 在读点先观察到未初始化状态。
|
||||
// var/函数类导出(VarScopedDeclarations)在实例化期即初始化为 undefined,
|
||||
// lexical/class 保持未初始化(TDZ)。 ——
|
||||
if has_self_ns_import {
|
||||
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();
|
||||
@@ -709,10 +788,17 @@ impl Emitter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 编译 JS 依赖模块(递归)。
|
||||
/// 编译 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, String> {
|
||||
) -> Result<(IRFunction, bool), String> {
|
||||
let alloc = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse_module(&alloc, &resolved.source).map_err(|errs| {
|
||||
format!(
|
||||
@@ -725,7 +811,8 @@ impl Emitter {
|
||||
// 依赖模块经 parse_module 解析,顶层恒严格模式。
|
||||
ctx.is_strict = true;
|
||||
self.emit_module_into_ctx(&program, &resolved.path, loader, path_stack, &mut ctx, false)?;
|
||||
Ok(ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None))
|
||||
let reassignable = ctx.module_live_dep;
|
||||
Ok((ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None), reassignable))
|
||||
}
|
||||
|
||||
/// 编译数据模块(json/text):body 仅为 __moduleData 调用 + RETURN。
|
||||
@@ -759,7 +846,7 @@ impl Emitter {
|
||||
Ok(result_reg)
|
||||
}
|
||||
|
||||
fn load_string_const(&self, s: &str, ctx: &mut CompileCtx) -> u32 {
|
||||
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));
|
||||
@@ -803,6 +890,53 @@ impl Emitter {
|
||||
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,
|
||||
@@ -919,7 +1053,7 @@ impl Emitter {
|
||||
if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||
let name = bi.name.as_str();
|
||||
let val_reg = self.load_var_reg(name, ctx)?;
|
||||
self.emit_module_set(ctx, ns_reg, name, val_reg)?;
|
||||
self.emit_export_registration(ctx, ns_reg, name, name, val_reg)?;
|
||||
self.emit_self_alias_write(ctx, name, val_reg);
|
||||
}
|
||||
}
|
||||
@@ -929,7 +1063,7 @@ impl Emitter {
|
||||
if let Some(id) = &fd.id {
|
||||
let name = id.name.as_str();
|
||||
let val_reg = self.load_var_reg(name, ctx)?;
|
||||
self.emit_module_set(ctx, ns_reg, name, val_reg)?;
|
||||
self.emit_export_registration(ctx, ns_reg, name, name, val_reg)?;
|
||||
self.emit_self_alias_write(ctx, name, val_reg);
|
||||
}
|
||||
}
|
||||
@@ -938,7 +1072,7 @@ impl Emitter {
|
||||
if let Some(id) = &cl.id {
|
||||
let name = id.name.as_str();
|
||||
let val_reg = self.load_var_reg(name, ctx)?;
|
||||
self.emit_module_set(ctx, ns_reg, name, val_reg)?;
|
||||
self.emit_export_registration(ctx, ns_reg, name, name, val_reg)?;
|
||||
self.emit_self_alias_write(ctx, name, val_reg);
|
||||
}
|
||||
}
|
||||
@@ -982,12 +1116,12 @@ impl Emitter {
|
||||
}
|
||||
// 本地导出:来源恒为 Local。
|
||||
let val_reg = self.load_var_reg(&local_name, ctx)?;
|
||||
self.emit_module_set(ctx, ns_reg, &exported_name, val_reg)?;
|
||||
self.emit_export_registration(ctx, ns_reg, &exported_name, &local_name, val_reg)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::ExportDefaultDeclaration(exp) => {
|
||||
let val_reg = match &exp.declaration {
|
||||
let (val_reg, source_name) = match &exp.declaration {
|
||||
ExportDefaultDeclarationKind::FunctionDeclaration(fd) => {
|
||||
let reg = self.emit_function_expression(fd, ctx)?;
|
||||
// 具名 default 函数绑定自身 id 名;匿名 default 函数绑定合成
|
||||
@@ -1000,13 +1134,14 @@ impl Emitter {
|
||||
.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 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
|
||||
// 具名 default 的注册源是该 id 绑定(可重赋),匿名退化为合成名。
|
||||
(reg, binding.unwrap_or_else(|| SYNTHETIC_DEFAULT_BINDING.to_string()))
|
||||
}
|
||||
ExportDefaultDeclarationKind::ClassDeclaration(cl) => {
|
||||
let reg = self.emit_class_with_binding(
|
||||
@@ -1024,19 +1159,22 @@ 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, 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())
|
||||
}
|
||||
reg
|
||||
}
|
||||
other => {
|
||||
let expr = other
|
||||
@@ -1072,10 +1210,10 @@ impl Emitter {
|
||||
ctx,
|
||||
)?;
|
||||
}
|
||||
reg
|
||||
(reg, SYNTHETIC_DEFAULT_BINDING.to_string())
|
||||
}
|
||||
};
|
||||
self.emit_module_set(ctx, ns_reg, DEFAULT_EXPORT_NAME, val_reg)?;
|
||||
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) => {
|
||||
|
||||
@@ -326,3 +326,43 @@ fn write_through_only_for_live_modules() {
|
||||
let (ir, _) = emit_module("export let a = 1; a = 2;", "./entry.js", &[]).expect("非 live 模块应编译成功");
|
||||
assert_eq!(native_calls_to(&ir, "__moduleSet"), 1, "非 live 模块重赋不得写穿");
|
||||
}
|
||||
|
||||
/// 面 12:可重赋依赖激活活读与 Cell——依赖预注册并挂共享 cell,导入方顶层读走
|
||||
/// `__moduleGet`;const-only 依赖零变化,导入方仍走链接期快照、依赖不预注册。
|
||||
#[test]
|
||||
fn reassignable_dep_activates_live_read_and_cell() {
|
||||
let (ir, _) = emit_module(
|
||||
"import { x } from './dep.js'; x;",
|
||||
"./entry.js",
|
||||
&[("./dep.js", ModuleKind::Js, "export const x = 1;")],
|
||||
)
|
||||
.expect("const-only 依赖应编译成功");
|
||||
assert_eq!(native_calls_to(&ir, "__moduleGet"), 0, "const-only 依赖导入方不得活读");
|
||||
assert!(native_calls_to(&ir, "__moduleLinkGet") >= 1, "导入绑定仍走链接期快照");
|
||||
let dep = &ir.nested[0];
|
||||
assert_eq!(native_calls_to(dep, "__modulePreRegister"), 0, "const-only 依赖不得预注册");
|
||||
assert_eq!(native_calls_to(dep, "__moduleSetCell"), 0, "const-only 依赖不得挂 cell");
|
||||
|
||||
let (ir, _) = emit_module(
|
||||
"import { x } from './dep.js'; x;",
|
||||
"./entry.js",
|
||||
&[(
|
||||
"./dep.js",
|
||||
ModuleKind::Js,
|
||||
"var x = 1; export { x }; globalThis.f = function(){ x = 2; };",
|
||||
)],
|
||||
)
|
||||
.expect("可重赋依赖应编译成功");
|
||||
let dep = &ir.nested[0];
|
||||
assert!(native_calls_to(dep, "__modulePreRegister") >= 1, "可重赋依赖应预注册导出");
|
||||
assert!(native_calls_to(dep, "__moduleSetCell") >= 1, "被捕获导出应挂共享 cell");
|
||||
assert!(native_calls_to(&ir, "__moduleGet") >= 1, "导入方顶层读应活读");
|
||||
}
|
||||
|
||||
/// 面 13:入口模块的可重赋导出不激活预注册(`module_live_dep` 仅依赖模块成立)。
|
||||
#[test]
|
||||
fn entry_module_reassignable_export_not_pre_registered() {
|
||||
let (ir, _) = emit_module("export let x = 1; x = 2;", "./entry.js", &[]).expect("入口模块应编译成功");
|
||||
assert_eq!(native_calls_to(&ir, "__modulePreRegister"), 0, "入口模块不得预注册");
|
||||
assert_eq!(native_calls_to(&ir, "__moduleSetCell"), 0, "入口模块不得挂 cell");
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::mem::Epoch;
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::object::{Cell, JsObject, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
/// builtins 依赖的 `Vm` 能力集合。
|
||||
@@ -157,6 +157,12 @@ pub trait VmHost {
|
||||
fn step_rng(&mut self);
|
||||
fn math_rng_value(&self) -> f64;
|
||||
fn sub_module_function_name(&self, gen: u32, sub_idx: u16) -> String;
|
||||
/// 取当前字节码帧 `cell_stack.last()[cell_idx]` 的共享 cell 指针。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 仅在同一 VM 世代的字节码帧执行期读取:native 调用不压 `cell_stack`,故
|
||||
/// builtin 内读到的是调用方模块帧;`cell_idx` 越界或槽为空返回 `None`。
|
||||
fn module_frame_cell(&self, cell_idx: u32) -> Option<*mut Cell>;
|
||||
/// 动态编译一个函数体(`Function` 构造器用):把参数列表与函数体编译为可调用
|
||||
/// 函数对象。编译或解析失败返回 `Err`,由调用方转为 `SyntaxError`。
|
||||
fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String>;
|
||||
|
||||
@@ -34,6 +34,11 @@ pub fn bind_global(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
3,
|
||||
),
|
||||
("__moduleSet", oxide_builtins::module::module_set::<crate::vm::Vm> as *const (), 3),
|
||||
(
|
||||
"__moduleSetCell",
|
||||
oxide_builtins::module::module_set_cell::<crate::vm::Vm> as *const (),
|
||||
3,
|
||||
),
|
||||
(
|
||||
"__moduleSetReexport",
|
||||
oxide_builtins::module::module_set_reexport::<crate::vm::Vm> as *const (),
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||
|
||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::mem::Epoch;
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::object::{Cell, JsObject, PropAttributes};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use super::Vm;
|
||||
@@ -161,6 +161,12 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
.and_then(|m| m.function_name.clone())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
fn module_frame_cell(&self, cell_idx: u32) -> Option<*mut Cell> {
|
||||
self.cell_stack
|
||||
.last()
|
||||
.and_then(|cells| cells.get(cell_idx as usize).copied())
|
||||
.filter(|p| !p.is_null())
|
||||
}
|
||||
fn create_dynamic_function(&mut self, params: &[String], body: &str) -> Result<JsValue, String> {
|
||||
self.create_dynamic_function(params, body)
|
||||
}
|
||||
|
||||
@@ -338,3 +338,75 @@ fn external_namespace_enumeration_stays_unaffected() {
|
||||
let result = run_namespace_module(&dir);
|
||||
assert_eq!(result, "z|1|1", "非 live 外部命名空间枚举行为不应变化");
|
||||
}
|
||||
|
||||
/// 跨模块可重赋导出活读:依赖模块经闭包重赋源绑定后,导入方命名绑定读到新值。
|
||||
#[test]
|
||||
fn cross_module_named_import_reads_live_value() {
|
||||
let cwd = std::env::current_dir().expect("cwd");
|
||||
let dir = cwd.join("__module_cross_live__");
|
||||
std::fs::create_dir_all(&dir).expect("mkdir");
|
||||
std::fs::write(
|
||||
dir.join("dep.mjs"),
|
||||
"var x = 1;\nexport { x };\nglobalThis.__bump = function () { x = 2; };",
|
||||
)
|
||||
.expect("write dep");
|
||||
std::fs::write(
|
||||
dir.join("main.mjs"),
|
||||
"import { x } from './dep.mjs';\n \
|
||||
const before = x;\n \
|
||||
globalThis.__bump();\n \
|
||||
const after = x;\n \
|
||||
globalThis.__ns = [before, after].join('|');",
|
||||
)
|
||||
.expect("write main");
|
||||
let _cleanup = Cleanup(dir.clone());
|
||||
|
||||
let result = run_namespace_module(&dir);
|
||||
assert_eq!(result, "1|2", "命名导入未跟随依赖源绑定重赋");
|
||||
}
|
||||
|
||||
/// 一源两别名:同一导出的两个导入局部名经活读同步更新,且互不分裂。
|
||||
#[test]
|
||||
fn cross_module_aliased_imports_read_live_value() {
|
||||
let cwd = std::env::current_dir().expect("cwd");
|
||||
let dir = cwd.join("__module_cross_live_alias__");
|
||||
std::fs::create_dir_all(&dir).expect("mkdir");
|
||||
std::fs::write(
|
||||
dir.join("dep.mjs"),
|
||||
"var x = 1;\nexport { x };\nglobalThis.__bump = function () { x = 2; };",
|
||||
)
|
||||
.expect("write dep");
|
||||
std::fs::write(
|
||||
dir.join("main.mjs"),
|
||||
"import { x as y, x as z } from './dep.mjs';\n \
|
||||
const before = [y, z];\n \
|
||||
globalThis.__bump();\n \
|
||||
globalThis.__ns = [before[0], before[1], y, z].join('|');",
|
||||
)
|
||||
.expect("write main");
|
||||
let _cleanup = Cleanup(dir.clone());
|
||||
|
||||
let result = run_namespace_module(&dir);
|
||||
assert_eq!(result, "1|1|2|2", "一源两别名未同步读到重赋后的活值");
|
||||
}
|
||||
|
||||
/// 默认导出具名函数体的自引用重赋:函数体内 `fn = 2` 写模块级绑定,
|
||||
/// 导入方再次读 default 得新值(含捕获分析下探 export 声明与共享 cell)。
|
||||
#[test]
|
||||
fn cross_module_default_export_reads_live_value() {
|
||||
let cwd = std::env::current_dir().expect("cwd");
|
||||
let dir = cwd.join("__module_cross_live_default__");
|
||||
std::fs::create_dir_all(&dir).expect("mkdir");
|
||||
std::fs::write(dir.join("dep.mjs"), "export default function fn() { fn = 2; return 1; }").expect("write dep");
|
||||
std::fs::write(
|
||||
dir.join("main.mjs"),
|
||||
"import val from './dep.mjs';\n \
|
||||
const ret = val();\n \
|
||||
globalThis.__ns = [ret, val].join('|');",
|
||||
)
|
||||
.expect("write main");
|
||||
let _cleanup = Cleanup(dir.clone());
|
||||
|
||||
let result = run_namespace_module(&dir);
|
||||
assert_eq!(result, "1|2", "default 导出的具名函数自引用重赋未活读");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user