From 0b33f4253adb521477bb2e6bdafe6ab3b16b582c Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 17 Sep 2026 15:59:13 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=A1=A5=E5=85=A8=20emit=20=E5=B0=8F?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=96=87=E6=A1=A3=E5=B9=B6=E6=94=B9=E5=86=99?= =?UTF-8?q?=E7=B1=BB=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/class/emit.rs | 13 +++++-------- crates/oxide_emit/src/class/prototype.rs | 5 +++-- crates/oxide_emit/src/shared/chain.rs | 11 +++++++++++ crates/oxide_emit/src/shared/class_field.rs | 2 ++ crates/oxide_emit/src/symbol_table.rs | 4 ++++ 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/crates/oxide_emit/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs index aad80b13..1bd0e94d 100644 --- a/crates/oxide_emit/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -130,12 +130,10 @@ 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. + // 类名绑定 cell:引用类名的类元素(构造器/方法/字段)经此专用 cell 捕获。 + // 类体内的读取经 LOAD_UPVALUE 命中同一 cell,使类名与其它引用在 epoch 晋升 + // (会改写 cell 值)与内联 accessor 调用(会清空寄存器文件)之后仍保持 identity + // 一致;合成键 @@class_self_N 不影响外层作用域的同名用户绑定。 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); @@ -403,8 +401,7 @@ impl Emitter { 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). + // 以构造器初始化类名 cell 并解除 TDZ(占位 cell 已由类元素闭包在 hoist 期创建)。 ctx.inst(Inst::new( OpCode::MAKE_CELL, Operand::Reg(binding_reg), diff --git a/crates/oxide_emit/src/class/prototype.rs b/crates/oxide_emit/src/class/prototype.rs index 5b6df8c3..94f1fa3e 100644 --- a/crates/oxide_emit/src/class/prototype.rs +++ b/crates/oxide_emit/src/class/prototype.rs @@ -11,8 +11,9 @@ impl Emitter { &self, ctor_reg: u32, proto_reg: u32, super_reg: Option, sub_idx: u16, ctx: &mut CompileCtx, ) -> Result<(), String> { ctx.inst(Inst::create_closure(Operand::Reg(ctor_reg), sub_idx)); - // 类原型 session 直分:实例 [[Prototype]] 与 Class.prototype 两侧须同一 - // 对象,epoch 分配在逃逸写晋升时克隆出第二份分裂 identity。 + // 类原型对象直接分配在 session 层:实例 [[Prototype]] 与 Class.prototype 属性 + // 两个引用须指向同一对象。若分配在 epoch 层,首次逃逸写会将其晋升克隆为 + // session 层的第二个对象,两个引用不再 identity 相同。 ctx.inst(Inst::new( OpCode::NEW_SESSION_OBJECT, Operand::Reg(proto_reg), diff --git a/crates/oxide_emit/src/shared/chain.rs b/crates/oxide_emit/src/shared/chain.rs index 2a269d8c..6405b11b 100644 --- a/crates/oxide_emit/src/shared/chain.rs +++ b/crates/oxide_emit/src/shared/chain.rs @@ -11,6 +11,7 @@ use oxide_ir::operand::{LabelId, Operand}; use oxide_parser::{ChainElement, Expression, LogicalOperator, PropertyKey}; impl Emitter { + /// 可选链短路守卫:`reg` 为 null 或 undefined 时跳转到短路标签 `short_label`。 fn emit_optional_guard(&self, reg: u32, short_label: LabelId, ctx: &mut CompileCtx) -> Result<(), String> { let dup_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(reg), Operand::None)); @@ -18,6 +19,7 @@ impl Emitter { Ok(()) } + /// 静态成员读取并保留 base 寄存器,返回 `(值寄存器, base 寄存器)` 供链上后续调用取 this。 fn emit_static_member_get_preserve_base( &self, member: &oxide_parser::StaticMemberExpression, short_label: Option, ctx: &mut CompileCtx, ) -> Result<(u32, u32), String> { @@ -36,6 +38,7 @@ impl Emitter { Ok((value_reg, obj_reg)) } + /// 计算成员读取并保留 base 寄存器;常量字符串键折叠为 IC 静态路径,其余走动态属性读,返回 `(值寄存器, base 寄存器)`。 fn emit_computed_member_get_preserve_base( &self, member: &oxide_parser::ComputedMemberExpression, short_label: Option, ctx: &mut CompileCtx, ) -> Result<(u32, u32), String> { @@ -66,6 +69,7 @@ impl Emitter { Ok((value_reg, obj_reg)) } + /// 链上调用:成员 / 私有字段调用的 this 取链 base,其余调用为 undefined;实参打包与内置调用分派在此完成。 fn emit_chain_call( &self, call: &oxide_parser::CallExpression, short_label: Option, ctx: &mut CompileCtx, ) -> Result { @@ -160,6 +164,7 @@ impl Emitter { Ok(result_reg) } + /// 递归发射链元素并返回结果寄存器;可短路点由 `short_label` 标记。 fn emit_chainable_expression( &self, expr: &Expression, short_label: Option, ctx: &mut CompileCtx, ) -> Result { @@ -198,6 +203,7 @@ impl Emitter { } } + /// 发射单个 `ChainElement`,按变体分派成员读取 / 私有字段 / 调用;TS 非空断言不支持,直接报错。 pub(crate) fn emit_chain_element( &self, element: &ChainElement, short_label: Option, ctx: &mut CompileCtx, ) -> Result { @@ -235,6 +241,7 @@ impl Emitter { } } + /// 逻辑赋值短路测试:按 And / Or / Coalesce 选择跳转条件,未短路则落入赋值路径。 pub(crate) fn emit_logical_assign_test( &self, op: LogicalOperator, test_reg: u32, store_label: LabelId, end_label: LabelId, ctx: &mut CompileCtx, ) -> Result<(), String> { @@ -249,6 +256,7 @@ impl Emitter { Ok(()) } + /// 对象属性静态读:键已由调用方取为字符串,本函数按常量键走 IC 读并返回属性寄存器。 pub(crate) fn emit_object_property_read(&self, src_reg: u32, key: &str, ctx: &mut CompileCtx) -> u32 { let prop_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(prop_reg), Operand::Reg(src_reg), Operand::None)); @@ -259,6 +267,7 @@ impl Emitter { prop_reg } + /// 将属性键求值为寄存器:字面量键直接取常量,其余形态走通用表达式分发。 fn emit_property_key_expression(&self, key: &PropertyKey, ctx: &mut CompileCtx) -> Result { match key { // 标识符计算键(`{[k]: a}`)走通用表达式分发:经 emit_static_identifier_read @@ -293,6 +302,8 @@ impl Emitter { } } + /// 读取对象属性并产出键信息:非计算键走字符串常量 IC 路径,计算键在字面量可折叠时走 IC,否则动态求值。 + /// 返回 `(结果寄存器, 常量键名, 动态键寄存器)`,后两者互补为 None。 pub(crate) fn emit_object_property_read_key( &self, src_reg: u32, key: &PropertyKey, computed: bool, ctx: &mut CompileCtx, ) -> Result<(u32, Option, Option), String> { diff --git a/crates/oxide_emit/src/shared/class_field.rs b/crates/oxide_emit/src/shared/class_field.rs index c669115f..6f31c1d1 100644 --- a/crates/oxide_emit/src/shared/class_field.rs +++ b/crates/oxide_emit/src/shared/class_field.rs @@ -56,6 +56,7 @@ impl Emitter { } } + /// 私有字段初始化:以私有名 id 做 `init_private` 写实例 own 属性。 pub(crate) fn emit_private_field_init( &self, target: Operand, name: &str, value: Option<&Expression>, ctx: &mut CompileCtx, ) -> Result<(), String> { @@ -65,6 +66,7 @@ impl Emitter { Ok(()) } + /// 私有方法初始化:方法经 `init_private` 写入,getter / setter 对经 `define_accessor` 共享同一私有名 id。 pub(crate) fn emit_private_method_init( &self, target: Operand, method: &oxide_parser::MethodDefinition, home_reg: Operand, self_binding: &[(&str, u32)], class_self_cell: Option, ctx: &mut CompileCtx, diff --git a/crates/oxide_emit/src/symbol_table.rs b/crates/oxide_emit/src/symbol_table.rs index bffb84d1..ea9fbd6d 100644 --- a/crates/oxide_emit/src/symbol_table.rs +++ b/crates/oxide_emit/src/symbol_table.rs @@ -59,6 +59,7 @@ impl SymbolTable { }); } + /// 按给定类型压入新作用域(函数或块)。 pub(crate) fn push_scope_with_kind(&mut self, kind: ScopeKind) { self.scopes.push(Scope { bindings: HashMap::new(), kind }); } @@ -70,6 +71,7 @@ impl SymbolTable { } } + /// 从内向外查找最近的函数作用域下标,作为 `var` 的提升目标;全局作用域即函数作用域,故恒能命中,兜底返回 0。 pub(crate) fn find_var_target_scope(&self) -> usize { for (i, scope) in self.scopes.iter().enumerate().rev() { if scope.kind == ScopeKind::FunctionScope { @@ -119,6 +121,7 @@ impl SymbolTable { Err(format!("Identifier '{name}' is not defined")) } + /// 从内向外查绑定并仅返回寄存器号;不检查初始化态,供无需 TDZ 判定的写路径使用。 pub(crate) fn lookup_any(&self, name: &str) -> Option { self.scopes .iter() @@ -126,6 +129,7 @@ impl SymbolTable { .find_map(|scope| scope.bindings.get(name).map(|binding| binding.reg)) } + /// 从内向外查绑定,返回绑定引用及其作用域下标(0 = 全局作用域,供顶层判定)。 pub(crate) fn lookup_any_binding(&self, name: &str) -> Option<(&Binding, usize)> { for (i, scope) in self.scopes.iter().enumerate().rev() { if let Some(b) = scope.bindings.get(name) {