From 621956114c77e81df567f22bc222a29ee77ba046 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 12 Aug 2026 15:37:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20class=20=E7=B1=BB=E5=90=8D=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=20TDZ=20=E4=B8=8E=20const=20=E8=AF=AD=E4=B9=89?= =?UTF-8?q?=EF=BC=88extends=20=E5=BC=95=E7=94=A8=E7=B1=BB=E5=90=8D?= =?UTF-8?q?=E6=8A=9B=20ReferenceError=EF=BC=8C=E7=B1=BB=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E5=90=8E=E5=88=9D=E5=A7=8B=E5=8C=96=E7=BB=91=E5=AE=9A=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/class/emit.rs | 57 +++++++++++++++++-- .../src/stmt/declaration/class_decl.rs | 5 +- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/crates/oxide_emit/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs index 6162d545..8e113470 100644 --- a/crates/oxide_emit/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -2,15 +2,49 @@ //! //! 函数:`emit_class`。 -use crate::{CompileCtx, Emitter, FunctionBodyContext}; +use crate::{symbol_table::ScopeKind, CompileCtx, Emitter, FunctionBodyContext}; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; -use oxide_parser::{Class, ClassElement, Expression, MethodDefinitionKind, PropertyKey}; +use oxide_parser::{Class, ClassElement, Expression, MethodDefinitionKind, PropertyKey, VariableDeclarationKind}; impl Emitter { pub(crate) fn emit_class(&self, class: &Class, ctx: &mut CompileCtx) -> Result { + // 类表达式:类名在类体内为 const 绑定;不向调用方复用寄存器时, + // 由 emit_class_with_binding 新建块作用域声明(TDZ),类构建完成后 + // STORE_VAR 初始化再弹出作用域,类名不泄漏到外层。 + self.emit_class_with_binding(class, ctx, None) + } + + /// 类名绑定可复用调用方寄存器的类整体 emit。 + /// + /// 类声明由调用方先在外层声明绑定(未初始化),`binding_reg` 复用该槽; + /// 类表达式无外部槽时,在独立块作用域声明 const 绑定(未初始化)。 + /// 无论哪种形态,绑定都在 `extends` 求值前建立:extends 引用类名按规范 + /// 抛 TDZ ReferenceError;类构建完成后 `init_var` + `STORE_VAR` 初始化。 + pub(crate) fn emit_class_with_binding( + &self, class: &Class, ctx: &mut CompileCtx, binding_reg: Option, + ) -> Result { + let ctor_name = class.id.as_ref().map(|id| id.name.to_string()); + let mut pushed_scope = false; + let binding_reg = if let Some(name) = ctor_name.as_deref() { + match binding_reg { + Some(reg) => { + pushed_scope = false; + reg + } + None => { + let reg = ctx.alloc_reg(); + ctx.push_scope_with_kind(ScopeKind::BlockScope); + ctx.declare(name, reg, VariableDeclarationKind::Const, true)?; + pushed_scope = true; + reg + } + } + } else { + 0 + }; let elements = &class.body.body; let mut constructor_method = None; let mut instance_field_indices = Vec::new(); @@ -87,8 +121,8 @@ impl Emitter { let any_computed = computed_slot > 0; let (ctor_reg, proto_reg, super_reg) = self.emit_class_header(class, ctx)?; - let ctor_name = class.id.as_ref().map(|id| id.name.to_string()); - let self_binding = ctor_name.as_deref().map(|name| vec![(name, ctor_reg)]).unwrap_or_default(); + let self_binding = ctor_name.as_deref().map(|name| vec![(name, binding_reg)]).unwrap_or_default(); + let saved_derived = ctx.in_derived_constructor; let saved_private_names = ctx.scopes.private_name_map.clone(); let saved_private_kinds = ctx.scopes.private_element_kinds.clone(); @@ -327,6 +361,21 @@ impl Emitter { self.emit_class_methods(&class.body.body, ctor_reg, proto_reg, &self_binding, &key_slots, ctx)?; self.emit_class_static_elements(&class.body.body, ctor_reg, &key_slots, ctx)?; + + // 类构建完成:初始化类名绑定并写入类构造器(类内方法引用该寄存器/捕获)。 + if let Some(name) = ctor_name.as_deref() { + ctx.init_var(name); + ctx.inst(Inst::new( + OpCode::STORE_VAR, + Operand::Reg(binding_reg), + Operand::Reg(ctor_reg), + Operand::None, + )); + } + + if pushed_scope { + ctx.pop_scope(); + } ctx.scopes.private_name_map = saved_private_names; ctx.scopes.private_element_kinds = saved_private_kinds; ctx.scopes.private_brand_id = saved_brand_id; diff --git a/crates/oxide_emit/src/stmt/declaration/class_decl.rs b/crates/oxide_emit/src/stmt/declaration/class_decl.rs index f1e9350a..a5d6e81f 100644 --- a/crates/oxide_emit/src/stmt/declaration/class_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/class_decl.rs @@ -26,8 +26,9 @@ impl Emitter { reg } }; - ctx.init_var(&name); - let ctor_reg = self.emit_class(class, ctx)?; + // 类名绑定在 emit_class_with_binding 内于 extends 求值后初始化: + // 先声明未初始化绑定(TDZ),extends 引用类名按规范抛 ReferenceError。 + let ctor_reg = self.emit_class_with_binding(class, ctx, Some(var_reg))?; if let Some(&cell_idx) = ctx.captured_bindings.get(&name) { ctx.inst(Inst::new( OpCode::MAKE_CELL,