fix: class 类名绑定经 dedicated cell 捕获(C===Cv 恒等)+ upvalue 写入 const guard(getter 赋值抛 TypeError)

This commit is contained in:
2026-08-12 17:30:28 +08:00
parent 0a4e42334d
commit 0c22a0e968
10 changed files with 60 additions and 31 deletions
+30 -1
View File
@@ -122,6 +122,22 @@ impl Emitter {
let (ctor_reg, proto_reg, super_reg) = self.emit_class_header(class, ctx)?;
let self_binding = ctor_name.as_deref().map(|name| vec![(name, binding_reg)]).unwrap_or_default();
// Class-name binding cell: class elements (ctor/method/field) that reference the
// class name capture it through this dedicated cell. Reads inside the class then
// resolve via LOAD_UPVALUE (same cell as the binding), so identity with other
// references survives epoch promotion (which rewrites cell values) and inline
// accessor calls (which clear the register file). The synthetic key keeps user
// bindings with the same name (outer scopes) untouched.
let class_self_cell: Option<u8> = ctor_name.as_deref().map(|_| {
let cell_idx = ctx
.captured_bindings
.values()
.copied()
.max()
.map_or(0, |m| m.saturating_add(1));
ctx.captured_bindings.insert(format!("@@class_self_{cell_idx}"), cell_idx);
cell_idx
});
let saved_derived = ctx.in_derived_constructor;
let saved_private_names = ctx.scopes.private_name_map.clone();
@@ -178,6 +194,9 @@ impl Emitter {
if let Some(c) = brand_cell {
extra_upvalue_names.push(("@@class_brand", c));
}
if let (Some(name), Some(c)) = (ctor_name.as_deref(), class_self_cell) {
extra_upvalue_names.push((name, c));
}
let emit_instance_fields = |compiler: &Emitter, field_ctx: &mut CompileCtx| -> Result<(), String> {
// 先写 brand 槽(私有方法/访问器的 brand 检查依据),再初始化字段。
@@ -358,7 +377,7 @@ impl Emitter {
Operand::None,
));
}
self.emit_class_methods(&class.body.body, ctor_reg, proto_reg, &self_binding, &key_slots, ctx)?;
self.emit_class_methods(&class.body.body, ctor_reg, proto_reg, &self_binding, class_self_cell, &key_slots, ctx)?;
self.emit_class_static_elements(&class.body.body, ctor_reg, &key_slots, ctx)?;
@@ -371,6 +390,16 @@ impl Emitter {
Operand::Reg(ctor_reg),
Operand::None,
));
if let Some(cell_idx) = class_self_cell {
// Initialize the class-name cell with the constructor and release TDZ
// (the placeholder cell was created by the element closures at hoist time).
ctx.inst(Inst::new(
OpCode::MAKE_CELL,
Operand::Reg(binding_reg),
Operand::Imm(cell_idx as u16),
Operand::None,
));
}
}
if pushed_scope {
+9 -5
View File
@@ -12,9 +12,10 @@ use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey};
const CLASS_METHOD_ATTRS: u32 = 0b101;
impl Emitter {
#[allow(clippy::too_many_arguments)]
pub(crate) fn emit_class_methods(
&self, elements: &[ClassElement], ctor_reg: u32, proto_reg: u32, self_binding: &[(&str, u32)],
key_slots: &[Option<u8>], ctx: &mut CompileCtx,
class_self_cell: Option<u8>, key_slots: &[Option<u8>], ctx: &mut CompileCtx,
) -> Result<(), String> {
for (element, &key_slot) in elements.iter().zip(key_slots) {
if let ClassElement::MethodDefinition(method) = element {
@@ -24,7 +25,7 @@ impl Emitter {
}
if matches!(method.key, PropertyKey::PrivateIdentifier(_)) {
let home_reg = if method.r#static { ctor_reg } else { proto_reg };
self.emit_private_method_init(Operand::Reg(home_reg), method, Operand::Reg(home_reg), ctx)?;
self.emit_private_method_init(Operand::Reg(home_reg), method, Operand::Reg(home_reg), self_binding, class_self_cell, ctx)?;
continue;
}
let home_reg = if method.r#static { ctor_reg } else { proto_reg };
@@ -39,7 +40,7 @@ impl Emitter {
self.class_property_name(&method.key)?
};
let accessor_reg =
self.emit_class_method_function(method, &method_name, Operand::Reg(home_reg), ctx, self_binding)?;
self.emit_class_method_function(method, &method_name, Operand::Reg(home_reg), ctx, self_binding, class_self_cell)?;
match method.kind {
MethodDefinitionKind::Method => {
// class 方法按规范为非枚举数据属性(DefineMethod:writable/configurable,enumerable=false)。
@@ -79,7 +80,7 @@ impl Emitter {
pub(crate) fn emit_class_method_function(
&self, method: &oxide_parser::MethodDefinition, method_name: &str, home_reg: Operand, ctx: &mut CompileCtx,
self_binding: &[(&str, u32)],
self_binding: &[(&str, u32)], class_self_cell: Option<u8>,
) -> Result<u32, String> {
let (param_names, body_stmts) = self.extract_function_parts(method.value.as_ref())?;
let saved_instance = ctx.in_instance_method;
@@ -88,12 +89,15 @@ impl Emitter {
ctx.in_static_method = method.r#static;
// 私有方法/访问器访问需对接收者做 brand 检查:方法函数捕获类 brand 对象
// (@@class_brand upvalue,值 = 类原型)。
let extra_uv: Vec<(&str, u8)> = ctx
let mut extra_uv: Vec<(&str, u8)> = ctx
.captured_bindings
.get("@@class_brand")
.map(|c| ("@@class_brand", *c))
.into_iter()
.collect();
if let (Some((name, _)), Some(c)) = (self_binding.first(), class_self_cell) {
extra_uv.push((name, c));
}
let method_value = method.value.as_ref();
let mut method_module = if method_value.r#async {
self.compile_function_body_with_field_hooks_gen(