From c1dcc7a8f38971ca000fe7541dd82a1d8f65b146 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 22:16:17 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=BB=98=E8=AE=A4=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=8C=BF=E5=90=8D=E5=87=BD=E6=95=B0=E4=B8=8E=E7=B1=BB=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E9=9A=90=E5=BC=8F=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/class/emit.rs | 9 ++- crates/oxide_emit/src/module.rs | 73 ++++++++++++++++--- .../src/stmt/declaration/class_decl.rs | 2 +- crates/oxide_emit/tests/module_link_tests.rs | 51 +++++++++++++ 4 files changed, 121 insertions(+), 14 deletions(-) diff --git a/crates/oxide_emit/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs index 1bd0e94d..060737fd 100644 --- a/crates/oxide_emit/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -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, + &self, class: &Class, ctx: &mut CompileCtx, binding_reg: Option, implicit_name: Option<&str>, ) -> Result { // 类定义体是严格模式代码:字段初始化/静态块/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 表达式中压入嵌套模块(箭头/函数/类键), // 必须在压入后用固定下标引用构造器模块,避免嵌套长度偏移。 diff --git a/crates/oxide_emit/src/module.rs b/crates/oxide_emit/src/module.rs index 2a30dc62..82d90fa6 100644 --- a/crates/oxide_emit/src/module.rs +++ b/crates/oxide_emit/src/module.rs @@ -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; } +/// `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 { } } 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(); diff --git a/crates/oxide_emit/src/stmt/declaration/class_decl.rs b/crates/oxide_emit/src/stmt/declaration/class_decl.rs index b40d367a..599766ab 100644 --- a/crates/oxide_emit/src/stmt/declaration/class_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/class_decl.rs @@ -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) } } diff --git a/crates/oxide_emit/tests/module_link_tests.rs b/crates/oxide_emit/tests/module_link_tests.rs index 7b42e92a..a4573055 100644 --- a/crates/oxide_emit/tests/module_link_tests.rs +++ b/crates/oxide_emit/tests/module_link_tests.rs @@ -80,6 +80,11 @@ fn pool_strings(ir: &IRFunction) -> HashSet { .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(), "非函数值不应注入隐式名"); +}