fix: class 类名绑定 TDZ 与 const 语义(extends 引用类名抛 ReferenceError,类构建后初始化绑定)

This commit is contained in:
爱特每羊队
2026-08-12 15:37:58 +08:00
parent d1b5c533fc
commit 4818465234
2 changed files with 56 additions and 6 deletions
+53 -4
View File
@@ -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<u32, String> {
// 类表达式:类名在类体内为 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<u32>,
) -> Result<u32, String> {
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;
@@ -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,