From 9002a1aa776c9142cbf98fe3baf63fc6a494b6cc Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 16 Aug 2026 13:32:25 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=B1=BB=E8=A1=A8=E8=BE=BE=E5=BC=8F?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=E5=90=8D=20cell=20=E6=8C=89=E5=A3=B0?= =?UTF-8?q?=E6=98=8E=E8=B7=AF=E5=BE=84=E9=97=A8=E6=8E=A7=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E8=A6=86=E7=9B=96=E5=A4=96=E5=B1=82=E5=90=8C=E5=90=8D?= =?UTF-8?q?=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/class/emit.rs | 23 +++++++++++----- crates/oxide_vm/tests/class_tests.rs | 41 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/crates/oxide_emit/src/class/emit.rs b/crates/oxide_emit/src/class/emit.rs index cc6eda4e..e2949816 100644 --- a/crates/oxide_emit/src/class/emit.rs +++ b/crates/oxide_emit/src/class/emit.rs @@ -27,6 +27,10 @@ impl Emitter { &self, class: &Class, ctx: &mut CompileCtx, binding_reg: Option, ) -> Result { let ctor_name = class.id.as_ref().map(|id| id.name.to_string()); + // 类声明由调用方提供外层绑定槽(binding_reg=Some),类表达式无外部槽 + // (None,类名是类体内独立 const 绑定)。真实名 cell 仅声明路径指向 + // 本作用域绑定;表达式路径按名查 captured_bindings 会命中外层同名绑定。 + let is_class_decl = binding_reg.is_some(); let mut pushed_scope = false; let binding_reg = if let Some(name) = ctor_name.as_deref() { match binding_reg { @@ -402,13 +406,18 @@ impl Emitter { } // 类名被外层嵌套函数捕获时,真实名 cell 同样须初始化(类元素自引用走 // @@class_self_* 合成 cell,二者 cell_idx 不同则各发一次)。 - if let Some(&cell_idx) = ctx.captured_bindings.get(name) { - ctx.inst(Inst::new( - OpCode::MAKE_CELL, - Operand::Reg(binding_reg), - Operand::Imm(cell_idx as u16), - Operand::None, - )); + // 仅类声明:类名是本作用域绑定,captured_bindings 按名命中的才是 + // 它的 cell;类表达式名是类体内独立 const 绑定,按名查询只会命中 + // 外层同名绑定 cell,MAKE_CELL 会把类值误写进外层绑定。 + if is_class_decl { + if let Some(&cell_idx) = ctx.captured_bindings.get(name) { + ctx.inst(Inst::new( + OpCode::MAKE_CELL, + Operand::Reg(binding_reg), + Operand::Imm(cell_idx as u16), + Operand::None, + )); + } } } diff --git a/crates/oxide_vm/tests/class_tests.rs b/crates/oxide_vm/tests/class_tests.rs index 64293bb7..950a875c 100644 --- a/crates/oxide_vm/tests/class_tests.rs +++ b/crates/oxide_vm/tests/class_tests.rs @@ -810,3 +810,44 @@ fn private_field_nullish_coalesce_short_circuits_on_defined() { .unwrap(); assert_num(result, 10.0); } + +// ── 类表达式真实名 cell 边界:类名绑定不触碰外层同名绑定 ── + +// 类表达式名是类体内独立 const 绑定:真实名 cell 初始化不得按名命中外层 +// 同名绑定 cell 并覆盖之。外层 var E 被嵌套函数捕获时,读 E 应仍是原值。 +#[test] +fn class_expression_inner_name_does_not_clobber_outer_binding() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var E = 1; var h = class E { m() { return E; } }; function g() { return E; } g()", + ) + .unwrap(); + assert_eq!(result.as_int(), 1); +} + +// 类表达式名不得破坏外层同名 let 的 TDZ:提前调用的捕获函数读该名仍抛 +// ReferenceError,而非读到被类值污染的 cell。 +#[test] +fn class_expression_inner_name_preserves_outer_let_tdz() { + let mut vm = Vm::new(); + let err = eval( + &mut vm, + "function g(){ return E; } var h = class E { m(){ return E; } }; var r = g(); let E = 5; r", + ) + .unwrap_err(); + assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}"); +} + +// 类声明路径不回归:真实名 cell 仍须初始化(嵌套函数经 cell 读类值), +// 且外层同名 var 不被类声明覆盖。 +#[test] +fn class_declaration_captured_name_cell_still_initialized() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var E = 1; function mk(){ class E { static n(){ return 5; } } return function(){ return E.n(); }; } var f = mk(); f()*10 + E", + ) + .unwrap(); + assert_num(result, 51.0); +}