fix: class 类名绑定经 dedicated cell 捕获(C===Cv 恒等)+ upvalue 写入 const guard(getter 赋值抛 TypeError)
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1130,7 +1130,7 @@ impl Emitter {
|
||||
);
|
||||
// 类字段 computed key 数组等合成捕获:直接追加 upvalue(cell_idx 由父分配)。
|
||||
for (name, cell_idx) in extra_upvalue_names {
|
||||
if !ctx.current_upvalue_captures.iter().any(|u| u.name == *name) {
|
||||
if !ctx.own_bindings.contains(*name) && !ctx.current_upvalue_captures.iter().any(|u| u.name == *name) {
|
||||
ctx.current_upvalue_captures.push(UpvalueCapture {
|
||||
name: (*name).to_string(),
|
||||
enclosing_reg: 0,
|
||||
|
||||
@@ -28,6 +28,7 @@ impl Emitter {
|
||||
}
|
||||
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
|
||||
let captured_cell = ctx.captured_bindings.get(name).copied();
|
||||
let const_flag = if ctx.lookup_const_flag(name) { 1 } else { 0 };
|
||||
if let Some(uv) = uv_idx {
|
||||
let val_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(
|
||||
@@ -39,7 +40,7 @@ impl Emitter {
|
||||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_UPVALUE,
|
||||
Operand::None,
|
||||
Operand::Imm(const_flag),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(uv as u16),
|
||||
));
|
||||
@@ -266,10 +267,11 @@ impl Emitter {
|
||||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||||
let const_flag = if ctx.lookup_const_flag(name) { 1 } else { 0 };
|
||||
if let Some(uv) = uv_idx {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_UPVALUE,
|
||||
Operand::None,
|
||||
Operand::Imm(const_flag),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(uv as u16),
|
||||
));
|
||||
|
||||
@@ -116,8 +116,8 @@ impl Emitter {
|
||||
/// 静态标识符写入:upvalue / 被捕获 cell / 普通槽(含 const guard)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `const_flag` 为 1 时走 STORE_VAR 运行时 const guard(对 const 再赋值报错)。
|
||||
/// - upvalue 与 cell 写穿共享单元,不设 guard。
|
||||
/// - `const_flag` 为 1 时走 STORE_VAR / STORE_UPVALUE 运行时 const guard(对 const 再赋值报错)。
|
||||
/// - cell 写穿共享单元,不设 guard(类名/闭包捕获的初始化写入路径);upvalue 按 const_flag 设 guard。
|
||||
pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
|
||||
// 循环 update 段:被捕获绑定走寄存器而非 cell(C 风格 for 每迭代 fresh,
|
||||
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||||
@@ -136,7 +136,7 @@ impl Emitter {
|
||||
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_UPVALUE,
|
||||
Operand::None,
|
||||
Operand::Imm(const_flag),
|
||||
Operand::Reg(val_reg),
|
||||
Operand::Imm(uv_idx as u16),
|
||||
));
|
||||
|
||||
@@ -455,9 +455,10 @@ impl Emitter {
|
||||
};
|
||||
let new_reg = ctx.alloc_reg();
|
||||
ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg)));
|
||||
let const_flag = if ctx.lookup_const_flag(name) { 1 } else { 0 };
|
||||
ctx.inst(Inst::new(
|
||||
OpCode::STORE_UPVALUE,
|
||||
Operand::None,
|
||||
Operand::Imm(const_flag),
|
||||
Operand::Reg(new_reg),
|
||||
Operand::Imm(uv as u16),
|
||||
));
|
||||
|
||||
@@ -66,13 +66,14 @@ impl Emitter {
|
||||
}
|
||||
|
||||
pub(crate) fn emit_private_method_init(
|
||||
&self, target: Operand, method: &oxide_parser::MethodDefinition, home_reg: Operand, ctx: &mut CompileCtx,
|
||||
&self, target: Operand, method: &oxide_parser::MethodDefinition, home_reg: Operand,
|
||||
self_binding: &[(&str, u32)], class_self_cell: Option<u8>, ctx: &mut CompileCtx,
|
||||
) -> Result<(), String> {
|
||||
let PropertyKey::PrivateIdentifier(private) = &method.key else {
|
||||
return Err("expected private method key".into());
|
||||
};
|
||||
let name = private.name.as_str();
|
||||
let method_reg = self.emit_class_method_function(method, name, home_reg, ctx, &[])?;
|
||||
let method_reg = self.emit_class_method_function(method, name, home_reg, ctx, self_binding, class_self_cell)?;
|
||||
match method.kind {
|
||||
MethodDefinitionKind::Method => {
|
||||
let key_reg = self.emit_private_id_reg(name, ctx)?;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
//! 类声明语句 emit:`emit_class_declaration_statement` 声明类名并初始化类对象。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_parser::{Statement, VariableDeclarationKind};
|
||||
|
||||
impl Emitter {
|
||||
@@ -28,17 +25,7 @@ impl Emitter {
|
||||
};
|
||||
// 类名绑定在 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,
|
||||
Operand::Reg(var_reg),
|
||||
Operand::Imm(cell_idx as u16),
|
||||
Operand::None,
|
||||
));
|
||||
} else {
|
||||
ctx.inst(Inst::new(OpCode::STORE_VAR, Operand::Reg(var_reg), Operand::Reg(ctor_reg), Operand::None));
|
||||
}
|
||||
self.emit_class_with_binding(class, ctx, Some(var_reg))?;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user