mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: 默认导出匿名函数与类设置隐式名
This commit is contained in:
@@ -14,7 +14,7 @@ impl Emitter {
|
||||
// 类表达式:类名在类体内为 const 绑定;不向调用方复用寄存器时,
|
||||
// 由 emit_class_with_binding 新建块作用域声明(TDZ),类构建完成后
|
||||
// STORE_VAR 初始化再弹出作用域,类名不泄漏到外层。
|
||||
self.emit_class_with_binding(class, ctx, None)
|
||||
self.emit_class_with_binding(class, ctx, None, None)
|
||||
}
|
||||
|
||||
/// 类名绑定可复用调用方寄存器的类整体 emit。
|
||||
@@ -23,8 +23,11 @@ impl Emitter {
|
||||
/// 类表达式无外部槽时,在独立块作用域声明 const 绑定(未初始化)。
|
||||
/// 无论哪种形态,绑定都在 `extends` 求值前建立:extends 引用类名按规范
|
||||
/// 抛 TDZ ReferenceError;类构建完成后 `init_var` + `STORE_VAR` 初始化。
|
||||
///
|
||||
/// `implicit_name` 供 `export default` 的匿名类做 SetFunctionName:类名缺省时
|
||||
/// 以它为构造器 `name`;具名类由 `ctor_name` 优先,不受影响。
|
||||
pub(crate) fn emit_class_with_binding(
|
||||
&self, class: &Class, ctx: &mut CompileCtx, binding_reg: Option<u32>,
|
||||
&self, class: &Class, ctx: &mut CompileCtx, binding_reg: Option<u32>, implicit_name: Option<&str>,
|
||||
) -> Result<u32, String> {
|
||||
// 类定义体是严格模式代码:字段初始化/静态块/extends 表达式内嵌套函数以
|
||||
// 本 ctx 为父 ctx,类编译期间强制 strict(函数末尾恢复外层标志)。
|
||||
@@ -314,7 +317,7 @@ impl Emitter {
|
||||
ctx.in_derived_constructor = saved_derived;
|
||||
ctor_module.is_class_constructor = true;
|
||||
ctor_module.is_derived_constructor = is_derived;
|
||||
ctor_module.function_name = ctor_name.clone();
|
||||
ctor_module.function_name = ctor_name.clone().or_else(|| implicit_name.map(str::to_string));
|
||||
ctx.nested.push(ctor_module);
|
||||
// 键数组构建会在 keys 表达式中压入嵌套模块(箭头/函数/类键),
|
||||
// 必须在压入后用固定下标引用构造器模块,避免嵌套长度偏移。
|
||||
|
||||
@@ -18,8 +18,8 @@ use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_ir::{IRFunction, ParamLayout};
|
||||
use oxide_parser::{
|
||||
BindingPattern, Declaration, ExportDefaultDeclarationKind, ImportAttributeKey, ImportDeclarationSpecifier,
|
||||
ModuleExportName, Statement, VariableDeclarationKind, WithClause,
|
||||
BindingPattern, Declaration, ExportDefaultDeclarationKind, Expression, ImportAttributeKey,
|
||||
ImportDeclarationSpecifier, ModuleExportName, Statement, VariableDeclarationKind, WithClause,
|
||||
};
|
||||
|
||||
/// 数据模块种类(非 JS 源码模块)。
|
||||
@@ -50,6 +50,35 @@ pub trait ModuleSourceLoader {
|
||||
) -> 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 {
|
||||
@@ -109,7 +138,7 @@ fn module_own_export_names(body: &[Statement]) -> HashSet<String> {
|
||||
}
|
||||
}
|
||||
Statement::ExportDefaultDeclaration(_) => {
|
||||
names.insert("default".to_string());
|
||||
names.insert(DEFAULT_EXPORT_NAME.to_string());
|
||||
}
|
||||
Statement::ExportAllDeclaration(exp) => {
|
||||
if let Some(exported) = &exp.exported {
|
||||
@@ -302,7 +331,7 @@ impl Emitter {
|
||||
}
|
||||
ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => {
|
||||
if is_self {
|
||||
if !own_export_names.contains("default") {
|
||||
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);
|
||||
@@ -317,10 +346,10 @@ impl Emitter {
|
||||
ctx,
|
||||
)?;
|
||||
if let Ok(reg) = ctx.lookup(s.local.name.as_str()) {
|
||||
ctx.module_self_aliases.insert("default".to_string(), reg);
|
||||
ctx.module_self_aliases.insert(DEFAULT_EXPORT_NAME.to_string(), reg);
|
||||
}
|
||||
} else {
|
||||
let name_reg = self.load_string_const("default", ctx);
|
||||
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(
|
||||
@@ -573,11 +602,18 @@ impl Emitter {
|
||||
false,
|
||||
ctx,
|
||||
)?;
|
||||
} else {
|
||||
set_implicit_name_of_last_nested(ctx, DEFAULT_EXPORT_NAME);
|
||||
}
|
||||
reg
|
||||
}
|
||||
ExportDefaultDeclarationKind::ClassDeclaration(cl) => {
|
||||
let reg = self.emit_class_expression(cl, ctx)?;
|
||||
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(),
|
||||
@@ -594,11 +630,28 @@ impl Emitter {
|
||||
let expr = other
|
||||
.as_expression()
|
||||
.ok_or_else(|| "unsupported export default declaration".to_string())?;
|
||||
self.emit_expression(expr, ctx)?
|
||||
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)?
|
||||
}
|
||||
}
|
||||
};
|
||||
self.emit_module_set(ctx, ns_reg, "default", val_reg)?;
|
||||
self.emit_self_alias_write(ctx, "default", val_reg);
|
||||
self.emit_module_set(ctx, ns_reg, DEFAULT_EXPORT_NAME, val_reg)?;
|
||||
self.emit_self_alias_write(ctx, DEFAULT_EXPORT_NAME, val_reg);
|
||||
}
|
||||
Statement::ExportAllDeclaration(exp) => {
|
||||
let source = exp.source.value.as_str();
|
||||
|
||||
@@ -32,7 +32,7 @@ impl Emitter {
|
||||
};
|
||||
// 类名绑定在 emit_class_with_binding 内于 extends 求值后初始化:
|
||||
// 先声明未初始化绑定(TDZ),extends 引用类名按规范抛 ReferenceError。
|
||||
self.emit_class_with_binding(class, ctx, Some(var_reg))?;
|
||||
self.emit_class_with_binding(class, ctx, Some(var_reg), None)?;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,11 @@ fn pool_strings(ir: &IRFunction) -> HashSet<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 在顶层 nested 中按 `function_name` 查找模块。
|
||||
fn named_module<'a>(ir: &'a IRFunction, name: &str) -> Option<&'a IRFunction> {
|
||||
ir.nested.iter().find(|m| m.function_name.as_deref() == Some(name))
|
||||
}
|
||||
|
||||
/// 面 1:import 绑定 prelude 经 `__moduleLinkGet` 链接,依赖模块编入子模块树。
|
||||
#[test]
|
||||
fn import_binding_prelude() {
|
||||
@@ -163,3 +168,49 @@ fn json_data_module() {
|
||||
let pool = pool_strings(data);
|
||||
assert!(pool.contains("json") && pool.contains(json), "json 源文本应入池: {pool:?}");
|
||||
}
|
||||
|
||||
/// 面 6:`export default` 匿名函数/生成器/箭头/类经 SetFunctionName 落隐式名 "default"。
|
||||
/// 带方法的类必须命中构造器,而不是构造器之后 push 的方法子模块。
|
||||
#[test]
|
||||
fn export_default_anonymous_implicit_name() {
|
||||
let (ir, _) = emit_module("export default function() {}", "./entry.js", &[]).expect("匿名函数声明应编译成功");
|
||||
assert!(named_module(&ir, "default").is_some(), "匿名函数声明应落隐式名");
|
||||
|
||||
let (ir, _) = emit_module("export default function* () {}", "./entry.js", &[]).expect("匿名生成器声明应编译成功");
|
||||
assert!(named_module(&ir, "default").is_some_and(|m| m.is_generator), "匿名生成器应落隐式名");
|
||||
|
||||
let (ir, _) = emit_module("export default (function() {})", "./entry.js", &[]).expect("括号函数表达式应编译成功");
|
||||
assert!(named_module(&ir, "default").is_some(), "匿名函数表达式应落隐式名");
|
||||
|
||||
let (ir, _) = emit_module("export default (() => {})", "./entry.js", &[]).expect("箭头表达式应编译成功");
|
||||
assert!(named_module(&ir, "default").is_some_and(|m| m.is_arrow), "匿名箭头应落隐式名");
|
||||
|
||||
let (ir, _) = emit_module("export default class { m() {} }", "./entry.js", &[]).expect("匿名类声明应编译成功");
|
||||
let ctor = ir.nested.iter().find(|m| m.is_class_constructor).expect("类构造器应在 nested");
|
||||
assert_eq!(ctor.function_name.as_deref(), Some("default"), "匿名类构造器应落隐式名");
|
||||
assert!(named_module(&ir, "m").is_some(), "方法模块应保留自身名");
|
||||
|
||||
let (ir, _) = emit_module("export default (class { m() {} })", "./entry.js", &[]).expect("括号类表达式应编译成功");
|
||||
let ctor = ir.nested.iter().find(|m| m.is_class_constructor).expect("类构造器应在 nested");
|
||||
assert_eq!(ctor.function_name.as_deref(), Some("default"), "匿名类表达式构造器应落隐式名");
|
||||
assert!(named_module(&ir, "m").is_some(), "方法模块应保留自身名");
|
||||
}
|
||||
|
||||
/// 面 7:`export default` 具名变体与非常量表达式不被隐式名覆盖。
|
||||
#[test]
|
||||
fn export_default_implicit_name_named_unchanged() {
|
||||
let (ir, _) = emit_module("export default class Foo { m() {} }", "./entry.js", &[]).expect("具名类声明应编译成功");
|
||||
let ctor = ir.nested.iter().find(|m| m.is_class_constructor).expect("类构造器应在 nested");
|
||||
assert_eq!(ctor.function_name.as_deref(), Some("Foo"));
|
||||
|
||||
let (ir, _) =
|
||||
emit_module("export default (class Bar { m() {} })", "./entry.js", &[]).expect("具名类表达式应编译成功");
|
||||
let ctor = ir.nested.iter().find(|m| m.is_class_constructor).expect("类构造器应在 nested");
|
||||
assert_eq!(ctor.function_name.as_deref(), Some("Bar"));
|
||||
|
||||
let (ir, _) = emit_module("export default (function f() {})", "./entry.js", &[]).expect("具名函数表达式应编译成功");
|
||||
assert!(named_module(&ir, "f").is_some(), "具名函数表达式应保留自身名");
|
||||
|
||||
let (ir, _) = emit_module("export default 42", "./entry.js", &[]).expect("字面量应编译成功");
|
||||
assert!(named_module(&ir, "default").is_none(), "非函数值不应注入隐式名");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user