fix: 构造帧 super 状态改显式标志修复多层继承与 return 语义

derived 构造器 super 状态改由 CallFrame.super_called 显式标志承载,
取代依赖 regs[254] 值判定:多层继承中间 derived 帧由 SUPER_CALL 压入
this=构造对象,值判定三重失效(未调 super 不报错 / this-before-super
失效 / 合法 super() 误判 more-than-once)。父构造器正常返回经
construct_result_reg==Some(254) 识别置位调用方帧(该 ABI 不变量仅
SUPER_CALL 压帧满足),抛错走 unwind 不置位。

do_return 检查放宽为非对象返回值(含 undefined/null/原始值)且未调
super 时抛 ReferenceError(规范 §9.2.2.2),调过 super 回退构造 this。

super 构造器改取当前帧 callee 原型而非 newTarget 原型:newTarget 跨
SUPER_CALL 压帧保持为最外层类,取其原型在中间 derived 层解析回同一
父类导致无限压帧。
This commit is contained in:
2026-08-16 01:23:52 +08:00
parent 3df08dddff
commit 8c4b766e70
7 changed files with 163 additions and 8 deletions
+35 -4
View File
@@ -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<bool, String> {
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
+6 -2
View File
@@ -119,9 +119,13 @@ impl Vm {
pub(crate) fn dispatch_load_var(&mut self, rd: usize, a: usize) -> Result<bool, String> {
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);
+1
View File
@@ -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));
+2
View File
@@ -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,
}
}
+10
View File
@@ -212,6 +212,14 @@ pub struct CallFrame {
pub construct_result_reg: Option<u8>,
pub constructed_this: Option<JsValue>,
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());
+15 -2
View File
@@ -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],直接交付。
+94
View File
@@ -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();