feat: 跨模块可重赋导出活读

This commit is contained in:
2026-09-18 00:46:06 +00:00
parent 1855775956
commit 3de0f8a6cb
10 changed files with 447 additions and 44 deletions
@@ -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 (),
+7 -1
View File
@@ -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 导出的具名函数自引用重赋未活读");
}