mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 模块 var 与函数导出实例化期预初始化
This commit is contained in:
@@ -113,6 +113,23 @@ fn push_uninitialized_entry(table: *mut ModuleNsTable, name_si: u32) {
|
||||
);
|
||||
}
|
||||
|
||||
/// 追加一条已初始化为 undefined 的 `Value` 条目(来源为本地导出)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 供 var 作用域导出(`VarScopedDeclarations`)在实例化期预初始化:读时为
|
||||
/// undefined 而非 TDZ,写点由 `__moduleSet` 就地覆盖。
|
||||
fn push_var_initialized_entry(table: *mut ModuleNsTable, name_si: u32) {
|
||||
push_entry(
|
||||
table,
|
||||
name_si,
|
||||
ModuleNsOrigin::Local,
|
||||
ModuleNsState::Value {
|
||||
value: JsValue::undefined(),
|
||||
initialized: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 向条目表追加一条带来源身份的条目。
|
||||
///
|
||||
/// # 注意事项
|
||||
@@ -300,19 +317,21 @@ pub fn drop_module_ns_native(obj: &mut JsObject) -> u64 {
|
||||
bytes
|
||||
}
|
||||
|
||||
/// `__modulePreRegister(ns, name)`:live 模块在 body 求值前预注册静态导出名。
|
||||
/// `__modulePreRegister(ns, name, var_like)`:live 模块在 body 求值前预注册静态导出名。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 校验 ns 为 module namespace 对象,取导出名的 interned 键。
|
||||
/// 2. 无表时分配;键已有条目时 no-op(幂等)。
|
||||
/// 3. 经瞬态可扩展窗口在 ns 上定义真实数据属性 `undefined`(可写可枚举不可配置)。
|
||||
/// 4. 追加未初始化 `Value` 条目并按规范顺序重排导出键。
|
||||
/// 4. 追加条目:`var_like` 为真(var/函数类导出,属 `VarScopedDeclarations`)时
|
||||
/// 初始化为 undefined;否则保持未初始化(lexical/class 的 TDZ)。随后按规范顺序
|
||||
/// 重排导出键。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 在 ns 上定义真实属性槽、挂载/扩展条目表,并重排 `[[OwnPropertyKeys]]` 顺序。
|
||||
pub fn module_pre_register<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 3 {
|
||||
return type_error(vm, "__modulePreRegister: 2 arguments required");
|
||||
if args.len() < 4 {
|
||||
return type_error(vm, "__modulePreRegister: 3 arguments required");
|
||||
}
|
||||
let ns_val = vm.reg(args[1]);
|
||||
let ns_ptr = match vm.checked_object_ptr(ns_val, "__modulePreRegister: target is not an object") {
|
||||
@@ -322,6 +341,7 @@ pub fn module_pre_register<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
let name_val = vm.reg(args[2]);
|
||||
let name_si = vm.property_key_si(name_val);
|
||||
let var_like = oxide_runtime_api::to_boolean(vm.reg(args[3]));
|
||||
let obj = unsafe { &mut *ns_ptr };
|
||||
if !obj.is_module_namespace() {
|
||||
return type_error(vm, "__modulePreRegister: target is not a module namespace");
|
||||
@@ -342,7 +362,11 @@ pub fn module_pre_register<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
|
||||
let table = if existing.is_null() { install_ns_table(obj) } else { existing };
|
||||
push_uninitialized_entry(table, name_si);
|
||||
if var_like {
|
||||
push_var_initialized_entry(table, name_si);
|
||||
} else {
|
||||
push_uninitialized_entry(table, name_si);
|
||||
}
|
||||
crate::object::sort_namespace_exports(vm, obj);
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -153,6 +153,101 @@ fn module_own_export_names(body: &[Statement]) -> HashSet<String> {
|
||||
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
|
||||
}
|
||||
|
||||
/// 判断语句是否为提升的函数声明(含 export 包装的函数声明)。
|
||||
fn is_hoisted_function_decl(stmt: &Statement) -> bool {
|
||||
match stmt {
|
||||
@@ -513,13 +608,17 @@ impl Emitter {
|
||||
}
|
||||
|
||||
// —— live 命名空间预注册:早于 hoisted 函数声明的 __moduleSet 与 body 求值,
|
||||
// 按名序预注册全部本地导出名,使自导入 ns 在读点先观察到未初始化状态。 ——
|
||||
// 按名序预注册全部本地导出名,使自导入 ns 在读点先观察到未初始化状态。
|
||||
// var/函数类导出(VarScopedDeclarations)在实例化期即初始化为 undefined,
|
||||
// lexical/class 保持未初始化(TDZ)。 ——
|
||||
if has_self_ns_import {
|
||||
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);
|
||||
self.emit_module_call(ctx, "__modulePreRegister", &[ns_reg, name_reg])?;
|
||||
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])?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,6 +766,13 @@ impl Emitter {
|
||||
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 语句执行时同步把值写回本地绑定槽。
|
||||
fn emit_self_alias_write(&self, ctx: &mut CompileCtx, export_name: &str, value_reg: u32) {
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn bind_global(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
(
|
||||
"__modulePreRegister",
|
||||
oxide_builtins::module::module_pre_register::<crate::vm::Vm> as *const (),
|
||||
2,
|
||||
3,
|
||||
),
|
||||
("__moduleSet", oxide_builtins::module::module_set::<crate::vm::Vm> as *const (), 3),
|
||||
(
|
||||
|
||||
@@ -144,7 +144,8 @@ fn namespace_exotic_semantics() {
|
||||
);
|
||||
}
|
||||
|
||||
/// live 自导入 ns 的条目语义:预注册后初始化前读抛 ReferenceError,`in` /
|
||||
/// live 自导入 ns 的条目语义:lexical/class 导出预注册后初始化前读抛 ReferenceError,
|
||||
/// `export var` 在实例化期即初始化为 undefined(读得 undefined 不抛);`in` /
|
||||
/// `Reflect.has` 已为 true;初始化后读到活值;枚举未初始化名不抛错且键数正确。
|
||||
/// 循环读 `ns.y` 同时钉住 IC 慢路径与普通路径读结果一致。
|
||||
#[test]
|
||||
@@ -182,8 +183,8 @@ fn namespace_self_import_live_entries() {
|
||||
|
||||
let result = run_namespace_module(&dir);
|
||||
assert_eq!(
|
||||
result, "true|true|true;true,true,true;default,x,y;4;sx;256;3",
|
||||
"live 自导入 ns 的未初始化读 / 键存在性 / 活值读不符"
|
||||
result, "true|value|true;true,true,true;default,x,y;4;sx;256;3",
|
||||
"live 自导入 ns 的未初始化读 / var 预初始化 / 键存在性 / 活值读不符"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -295,6 +296,28 @@ fn namespace_write_through_covers_update_logical_destructuring() {
|
||||
assert_eq!(result, "2|7|9", "Update/逻辑/解构赋值的写穿缺失");
|
||||
}
|
||||
|
||||
/// `export var` 属 VarScopedDeclarations,模块实例化期即初始化为 undefined:
|
||||
/// body 语句执行前经命名空间读应得 undefined,而非 ReferenceError;`in` 已为 true。
|
||||
#[test]
|
||||
fn namespace_export_var_preinitialized() {
|
||||
let cwd = std::env::current_dir().expect("cwd");
|
||||
let dir = cwd.join("__module_namespace_var_preinit__");
|
||||
std::fs::create_dir_all(&dir).expect("mkdir");
|
||||
std::fs::write(
|
||||
dir.join("main.mjs"),
|
||||
"import * as ns from './main.mjs';\n \
|
||||
let before;\n \
|
||||
try { before = ns.y; } catch (e) { before = 'threw'; }\n \
|
||||
globalThis.__ns = [String(before), 'y' in ns].join('|');\n \
|
||||
export var y = 2;",
|
||||
)
|
||||
.expect("write main");
|
||||
let _cleanup = Cleanup(dir.clone());
|
||||
|
||||
let result = run_namespace_module(&dir);
|
||||
assert_eq!(result, "undefined|true", "export var 应在实例化期预初始化为 undefined");
|
||||
}
|
||||
|
||||
/// G2 反向守卫:外部导入的命名空间无条目表(非 live),枚举与 for-in 行为不变。
|
||||
#[test]
|
||||
fn external_namespace_enumeration_stays_unaffected() {
|
||||
|
||||
Reference in New Issue
Block a user