diff --git a/crates/oxide_emit/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs index 8e113470..a5438eb6 100644 --- a/crates/oxide_emit/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -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 = 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 { diff --git a/crates/oxide_emit/src/class/method.rs b/crates/oxide_emit/src/class/method.rs index d2106f6c..0b03a6b1 100644 --- a/crates/oxide_emit/src/class/method.rs +++ b/crates/oxide_emit/src/class/method.rs @@ -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], ctx: &mut CompileCtx, + class_self_cell: Option, key_slots: &[Option], 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, ) -> Result { 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( diff --git a/crates/oxide_emit/src/emit.rs b/crates/oxide_emit/src/emit.rs index a8cb5956..3a067c30 100644 --- a/crates/oxide_emit/src/emit.rs +++ b/crates/oxide_emit/src/emit.rs @@ -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, diff --git a/crates/oxide_emit/src/expr/assignment.rs b/crates/oxide_emit/src/expr/assignment.rs index d6662709..cae86762 100644 --- a/crates/oxide_emit/src/expr/assignment.rs +++ b/crates/oxide_emit/src/expr/assignment.rs @@ -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), )); diff --git a/crates/oxide_emit/src/expr/identifier.rs b/crates/oxide_emit/src/expr/identifier.rs index faa0b4b6..284df815 100644 --- a/crates/oxide_emit/src/expr/identifier.rs +++ b/crates/oxide_emit/src/expr/identifier.rs @@ -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), )); diff --git a/crates/oxide_emit/src/expr/operator/mod.rs b/crates/oxide_emit/src/expr/operator/mod.rs index a50e8a41..3c858acb 100644 --- a/crates/oxide_emit/src/expr/operator/mod.rs +++ b/crates/oxide_emit/src/expr/operator/mod.rs @@ -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), )); diff --git a/crates/oxide_emit/src/shared/class_field.rs b/crates/oxide_emit/src/shared/class_field.rs index 6f81e0df..c669115f 100644 --- a/crates/oxide_emit/src/shared/class_field.rs +++ b/crates/oxide_emit/src/shared/class_field.rs @@ -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, 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)?; diff --git a/crates/oxide_emit/src/stmt/declaration/class_decl.rs b/crates/oxide_emit/src/stmt/declaration/class_decl.rs index a5d6e81f..421bfed3 100644 --- a/crates/oxide_emit/src/stmt/declaration/class_decl.rs +++ b/crates/oxide_emit/src/stmt/declaration/class_decl.rs @@ -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) } } diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 601fb7b0..de2c4c63 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -323,7 +323,8 @@ impl Vm { } #[allow(dead_code)] - pub(crate) fn dispatch_store_upvalue(&mut self, a: usize, b: usize) -> Result<(), String> { + pub(crate) fn dispatch_store_upvalue(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { + let const_flag = rd; let uv_idx = b; let src_val = self.regs[a]; if let Some(callee) = self.current_callee() { @@ -334,6 +335,10 @@ impl Vm { if !upvals[uv_idx].is_null() { unsafe { let cell = &mut *upvals[uv_idx]; + if const_flag != 0 && !cell.value.is_undefined() { + self.raise_error_kind("TypeError", "Assignment to constant variable")?; + return Ok(()); + } cell.value = src_val; cell.set_initialized(true); } diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 61d219dc..36d32d52 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -1244,7 +1244,7 @@ impl Vm { self.dispatch_load_upvalue(rd, instr)?; } OpCode::STORE_UPVALUE => { - self.dispatch_store_upvalue(a, b)?; + self.dispatch_store_upvalue(rd, a, b)?; } OpCode::CREATE_REGEXP => match self.dispatch_create_regexp(rd, a, b) { Ok(Some(result)) => return Ok(result),