diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index ce90c5c..0a8fb01 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -416,7 +416,7 @@ impl Vm { self.raise_error_kind("ReferenceError", "super() used outside derived constructor")?; return Ok(true); } - if !self.regs[254].is_undefined() { + if frame.super_called { self.raise_error_kind("ReferenceError", "super() called more than once")?; return Ok(true); } @@ -424,6 +424,7 @@ impl Vm { self.raise_error_kind("ReferenceError", "super() without derived this")?; return Ok(true); }; + let callee = frame.callee; let new_target = self.regs[255]; if !new_target.is_object() { @@ -431,7 +432,15 @@ impl Vm { return Ok(true); } let new_target_obj = unsafe { &*new_target.as_js_object_ptr() }; - let super_ctor = new_target_obj.proto(); + // super 构造器取当前帧 callee(执行中的 derived 构造器)的原型,而非 newTarget: + // newTarget 跨 SUPER_CALL 压帧保持为最外层类,取其原型在中间 derived 层会 + // 解析回同一父类,导致父帧被反复压入(多层继承栈溢出)。 + if !callee.is_object() { + self.raise_error_kind("TypeError", "super() without constructor callee")?; + return Ok(true); + } + let callee_obj = unsafe { &*callee.as_js_object_ptr() }; + let super_ctor = callee_obj.proto(); if !super_ctor.is_object() { self.raise_error_kind("TypeError", "super constructor is not an object")?; return Ok(true); @@ -459,6 +468,7 @@ impl Vm { } self.regs[254] = instance; self.regs[rd] = self.regs[254]; + self.mark_super_called(); } NativeResult::Err(err_val) => { self.exception_value = Some(err_val); @@ -479,6 +489,7 @@ impl Vm { } self.regs[254] = instance; self.regs[rd] = self.regs[254]; + self.mark_super_called(); } Err(e) => return Err(e), } @@ -528,6 +539,16 @@ impl Vm { Ok(()) } + /// 置位当前 derived 帧的 super_called:native super 构造器同步返回后标记 + /// super() 已成功调用(字节码父构造器路径由 do_return 在返回时置位)。 + fn mark_super_called(&mut self) { + if let Some(frame) = self.frames.last_mut() { + if frame.is_derived_constructor { + frame.super_called = true; + } + } + } + pub(crate) fn dispatch_super_get_prop(&mut self, rd: usize, a: usize, b: usize) -> Result { vm_trace!("SUPER_GET_PROP rd={} a={} b={}", rd, a, b); let key_val = self.regs[b]; @@ -891,7 +912,7 @@ impl Vm { self.raise_error_kind("ReferenceError", "super() used outside derived constructor")?; return Ok(true); } - if !self.regs[254].is_undefined() { + if frame.super_called { self.raise_error_kind("ReferenceError", "super() called more than once")?; return Ok(true); } @@ -899,13 +920,22 @@ impl Vm { self.raise_error_kind("ReferenceError", "super() without derived this")?; return Ok(true); }; + let callee = frame.callee; + let new_target = self.regs[255]; if !new_target.is_object() { self.raise_error_kind("TypeError", "super() new.target is not an object")?; return Ok(true); } let new_target_obj = unsafe { &*new_target.as_js_object_ptr() }; - let super_ctor = new_target_obj.proto(); + // 同 dispatch_super_call:super 构造器取当前帧 callee 的原型(newTarget + // 保持为最外层类,取其原型在中间 derived 层会反复压入同一父帧)。 + if !callee.is_object() { + self.raise_error_kind("TypeError", "super() without constructor callee")?; + return Ok(true); + } + let callee_obj = unsafe { &*callee.as_js_object_ptr() }; + let super_ctor = callee_obj.proto(); if !super_ctor.is_object() { self.raise_error_kind("TypeError", "super constructor is not an object")?; return Ok(true); @@ -931,6 +961,7 @@ impl Vm { } self.regs[254] = instance; self.regs[rd] = instance; + self.mark_super_called(); } Err(_) => { let exc = self diff --git a/crates/oxide_vm/src/dispatch/misc.rs b/crates/oxide_vm/src/dispatch/misc.rs index 0ccef61..46e106b 100644 --- a/crates/oxide_vm/src/dispatch/misc.rs +++ b/crates/oxide_vm/src/dispatch/misc.rs @@ -119,9 +119,13 @@ impl Vm { pub(crate) fn dispatch_load_var(&mut self, rd: usize, a: usize) -> Result { vm_trace!("LOAD_VAR rd={} r{}={:?}", rd, a, self.regs[a]); + // a==254 即读 this:derived 构造帧在 super() 成功前读 this 抛 ReferenceError。 if a == 254 - && self.frames.last().map(|frame| frame.is_derived_constructor).unwrap_or(false) - && self.regs[a].is_undefined() + && self + .frames + .last() + .map(|frame| frame.is_derived_constructor && !frame.super_called) + .unwrap_or(false) { self.raise_error_kind("ReferenceError", "must call super constructor before using 'this'")?; return Ok(true); diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index f989313..9091b66 100644 --- a/crates/oxide_vm/src/session_gc.rs +++ b/crates/oxide_vm/src/session_gc.rs @@ -831,6 +831,7 @@ mod tests { construct_result_reg: None, constructed_this: Some(JsValue::from_js_object(child_session)), is_derived_constructor: false, + super_called: false, continuation: FrameContinuation::None, }); vm.save_stack.push(JsValue::from_js_object(frame_session)); diff --git a/crates/oxide_vm/src/suspended.rs b/crates/oxide_vm/src/suspended.rs index 5cdd31e..10c3016 100644 --- a/crates/oxide_vm/src/suspended.rs +++ b/crates/oxide_vm/src/suspended.rs @@ -283,6 +283,7 @@ impl SuspendedFrame { construct_result_reg: f.construct_result_reg, constructed_this: f.constructed_this.map(&mut rewrite), is_derived_constructor: f.is_derived_constructor, + super_called: f.super_called, continuation: f.continuation, }), spill_stack: self.spill_stack.iter().copied().map(&mut rewrite).collect(), @@ -343,6 +344,7 @@ mod tests { construct_result_reg: None, constructed_this: Some(tag), is_derived_constructor: false, + super_called: false, continuation: FrameContinuation::None, } } diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index f8a2351..31373d0 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -212,6 +212,14 @@ pub struct CallFrame { pub construct_result_reg: Option, pub constructed_this: Option, pub is_derived_constructor: bool, + /// derived 构造帧是否已成功调用 super()(父构造器正常返回后置位)。 + /// + /// 置位识别依赖 ABI 不变量:`construct_result_reg == Some(254)` 仅 SUPER_CALL + /// 压帧产生(emit 从寄存器 ≥1 分配,254 保留给 this);父构造器抛错走 unwind + /// 弹帧不进 do_return,不置位。此标志取代依赖 regs[254] 值判定 super 状态的 + /// 旧方案——多层继承时中间 derived 帧由 SUPER_CALL 以构造 this 压入, + /// regs[254] 恒非 undefined,值判定三重失效。 + pub super_called: bool, pub continuation: FrameContinuation, } @@ -1317,6 +1325,7 @@ impl Vm { construct_result_reg, constructed_this, is_derived_constructor: obj.is_derived_constructor(), + super_called: false, continuation, }); @@ -2365,6 +2374,7 @@ mod tests { construct_result_reg: None, constructed_this: None, is_derived_constructor: false, + super_called: false, continuation: super::FrameContinuation::None, }); vm.save_stack.push(JsValue::undefined()); diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index d9a32dd..ff867a6 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -423,12 +423,14 @@ impl Vm { let construct_result_reg = frame.construct_result_reg; let constructed_this = frame.constructed_this; let is_derived_constructor = frame.is_derived_constructor; + let super_called = frame.super_called; let continuation = frame.continuation; - let callee_this = self.regs[254]; vm_trace!("RETURN frame: continuation={:?}, derived={}", continuation, is_derived_constructor); self.restore_frame(frame); if let (Some(target_reg), Some(constructed_this)) = (construct_result_reg, constructed_this) { - if is_derived_constructor && result.is_undefined() && callee_this.is_undefined() { + // 规范 §9.2.2.2:derived 构造器返回非对象值(含 undefined/null/原始值) + // 且未调用 super() 时抛 ReferenceError;调过 super() 则回退构造 this。 + if is_derived_constructor && !result.is_object() && !super_called { self.raise_error_kind("ReferenceError", "derived constructor must call super()")?; return Ok(None); } @@ -447,6 +449,17 @@ impl Vm { } } } + // 父构造器正常返回:super() 调用完成,置位调用方 derived 帧的 super_called。 + // 识别依据:construct_result_reg == Some(254) 仅 SUPER_CALL 压帧产生(emit + // 从寄存器 ≥1 分配,254 保留给 this);父构造器抛错走 unwind 弹帧不进 + // 本函数,不置位——后续 this 访问 / 再调 super 仍按未初始化报错。 + if construct_result_reg == Some(254) { + if let Some(caller) = self.frames.last_mut() { + if caller.is_derived_constructor { + caller.super_called = true; + } + } + } // 生成器/异步/构造内嵌 dispatch:帧全部弹出后把结果交付恢复方,而非继续执行。 if self.frames.is_empty() && (self.generator_dispatch || self.async_dispatch || self.construct_dispatch) { // 构造内嵌:弹帧已把构造结果(非对象回退 this)写入 regs[0],直接交付。 diff --git a/crates/oxide_vm/tests/class_tests.rs b/crates/oxide_vm/tests/class_tests.rs index dcc487f..6cea3bd 100644 --- a/crates/oxide_vm/tests/class_tests.rs +++ b/crates/oxide_vm/tests/class_tests.rs @@ -124,6 +124,100 @@ fn derived_constructor_this_before_super_throws_reference_error() { assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}"); } +#[test] +fn three_level_inheritance_super_chain_constructs_normally() { + // 多层继承(≥3 层):中间 derived 帧由 SUPER_CALL 压入,合法 super() 不得误报 + // more-than-once,构造链须逐层完成。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class A { constructor(){ this.v = 1; } } class B extends A { constructor(){ super(); this.w = 2; } } class C extends B { constructor(){ super(); this.u = 3; } } var c = new C(); c.v + c.w + c.u", + ) + .unwrap(); + assert_num(result, 6.0); +} + +#[test] +fn intermediate_derived_returning_without_super_throws() { + // 中间 derived 构造器未调 super 直接 return:须抛 ReferenceError(此前 + // regs[254] 值判定因 this 为外层构造对象而漏检)。 + let mut vm = Vm::new(); + let err = eval( + &mut vm, + "class A{} class B extends A{ constructor(){ return; } } class C extends B{} new C()", + ) + .unwrap_err(); + assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}"); +} + +#[test] +fn derived_returning_without_super_throws_reference_error() { + let mut vm = Vm::new(); + let err = eval(&mut vm, "class A{} class B extends A{ constructor(){ return; } } new B()").unwrap_err(); + assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}"); +} + +#[test] +fn intermediate_derived_this_before_super_throws() { + // 中间 derived 帧由 SUPER_CALL 压入后 super() 前读 this:须抛 ReferenceError。 + let mut vm = Vm::new(); + let err = eval( + &mut vm, + "class A{} class B extends A{ constructor(){ this.x = 1; super(); } } class C extends B{ constructor(){ super(); } } new C()", + ) + .unwrap_err(); + assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}"); +} + +#[test] +fn intermediate_derived_double_super_throws() { + // 中间 derived 帧二次调用 super():须抛 more-than-once(字节码父构造器返回后 + // 置位 super_called 生效)。 + let mut vm = Vm::new(); + let err = eval( + &mut vm, + "class A{} class B extends A{ constructor(){ super(); super(); } } class C extends B{} new C()", + ) + .unwrap_err(); + assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}"); +} + +#[test] +fn derived_constructor_returning_primitive_without_super_throws() { + // 规范 §9.2.2.2:derived 返回非对象值(42/null)且未调 super → ReferenceError, + // 而非静默回退构造 this。 + let mut vm = Vm::new(); + let err = eval(&mut vm, "class A{} class B extends A{ constructor(){ return 42; } } new B()").unwrap_err(); + assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}"); +} + +#[test] +fn derived_constructor_returning_null_without_super_throws() { + let mut vm = Vm::new(); + let err = eval(&mut vm, "class A{} class B extends A{ constructor(){ return null; } } new B()").unwrap_err(); + assert!(err.contains("ReferenceError"), "expected ReferenceError, got: {err}"); +} + +#[test] +fn derived_constructor_returning_object_overrides_instance() { + // derived 返回对象值:直接作为构造结果,不校验 super。 + let mut vm = Vm::new(); + let result = eval(&mut vm, "class A{} class B extends A{ constructor(){ return { x: 1 }; } } new B().x").unwrap(); + assert_eq!(result.as_int(), 1); +} + +#[test] +fn derived_constructor_primitive_return_after_super_preserves_this() { + // derived 调过 super 后返回原始值:回退构造 this,实例字段保留。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "class A{ constructor(){ this.v = 5; } } class B extends A{ constructor(){ super(); return 42; } } new B().v", + ) + .unwrap(); + assert_num(result, 5.0); +} + #[test] fn derived_method_super_call_uses_current_receiver() { let mut vm = Vm::new();