This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_vm/tests/class_tests.rs
T
pjh123 0c6aa7fbef 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 层解析回同一
父类导致无限压帧。
2026-08-16 01:23:52 +08:00

570 lines
18 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
vm.run(&module)
}
fn assert_num(value: JsValue, expected: f64) {
let actual = if value.is_int() { value.as_int() as f64 } else { value.as_double() };
assert!((actual - expected).abs() < 0.0001, "expected {expected}, got {actual}");
}
#[test]
fn class_declaration_is_function_value() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A {} ; typeof A").unwrap();
let ty = vm.lookup_str(result).expect("typeof result should be string");
assert_eq!(ty, "function");
}
#[test]
fn class_method_is_on_prototype() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { m() { return 1; } } new A().m()").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn class_instance_getter_returns_value() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { get x() { return 2; } } new A().x").unwrap();
assert_eq!(result.as_int(), 2);
}
#[test]
fn class_instance_setter_updates_receiver() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { set x(v) { this.y = v; } } var a = new A(); a.x = 4; a.y").unwrap();
assert_eq!(result.as_int(), 4);
}
#[test]
fn class_static_getter_returns_value() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { static get x() { return 5; } } A.x").unwrap();
assert_eq!(result.as_int(), 5);
}
#[test]
fn class_constructor_initializes_instance_state() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { constructor(x) { this.x = x; } } new A(3).x").unwrap();
assert_eq!(result.as_int(), 3);
}
#[test]
fn class_expression_constructs_instances() {
let mut vm = Vm::new();
let result = eval(&mut vm, "const C = class Foo { method() { return 2; } }; new C().method()").unwrap();
assert_eq!(result.as_int(), 2);
}
#[test]
fn class_expression_inner_name_is_visible_in_method_body() {
let mut vm = Vm::new();
let result = eval(&mut vm, "const C = class Foo { self() { return Foo; } }; new C().self() === C").unwrap();
assert!(
result.is_bool() && result.as_bool(),
"expected inner class name to resolve to constructor"
);
}
#[test]
fn class_constructor_cannot_be_called_without_new() {
let mut vm = Vm::new();
let err = eval(&mut vm, "class A {} ; A()").unwrap_err();
assert!(err.contains("TypeError"), "expected TypeError, got: {err}");
}
#[test]
fn class_constructor_object_return_overrides_instance() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { constructor() { return { x: 1 }; } } new A().x").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn class_constructor_primitive_return_preserves_instance() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { constructor() { this.x = 1; return 5; } } new A().x").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn derived_constructor_super_initializes_parent_state() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class A { constructor(){ this.x = 1 } } class B extends A { constructor(){ super(); this.y = 2 } } let b = new B(); b.x + b.y",
)
.unwrap();
assert_num(result, 3.0);
}
#[test]
fn derived_default_constructor_delegates_to_parent() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { constructor(){ this.x = 1 } } class B extends A {} new B().x").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn derived_constructor_this_before_super_throws_reference_error() {
let mut vm = Vm::new();
let err = eval(
&mut vm,
"class A { constructor(){ this.x = 1 } } class B extends A { constructor(){ this.x = 2; super(); } } new B()",
)
.unwrap_err();
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();
let result = eval(
&mut vm,
"class A { m(){ return this.x } } class B extends A { constructor(){ super(); this.x = 2 } m(){ return super.m() + 1 } } new B().m()",
)
.unwrap();
assert_num(result, 3.0);
}
#[test]
fn static_method_is_callable_on_constructor() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { static m(){ return 1 } } A.m()").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn static_and_instance_methods_do_not_collide() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class A { m(){ return 1 } static m(){ return 2 } } let a = new A(); a.m() + A.m()",
)
.unwrap();
assert_num(result, 3.0);
}
#[test]
fn derived_static_method_super_call_resolves_parent_constructor() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class A { static m(){ return 1 } } class B extends A { static n(){ return super.m() + 1 } } B.n()",
)
.unwrap();
assert_num(result, 2.0);
}
#[test]
fn inherited_static_method_is_found_on_constructor_chain() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class A { static m(){ return 4 } } class B extends A {} B.m()").unwrap();
assert_eq!(result.as_int(), 4);
}
#[test]
fn public_class_field_initializer_sets_own_property() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class C { x = 1; } new C().x").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn public_class_field_without_initializer_is_undefined() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class C { x; } typeof new C().x").unwrap();
let ty = vm.lookup_str(result).expect("typeof result should be string");
assert_eq!(ty, "undefined");
}
#[test]
fn public_class_fields_run_before_base_constructor_body() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class C { x = this.y; constructor(){ this.y = 2; } } typeof new C().x").unwrap();
let ty = vm.lookup_str(result).expect("typeof result should be string");
assert_eq!(ty, "undefined");
}
#[test]
fn public_class_fields_run_after_super_in_derived_constructor() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class B { constructor(){ this.b = 1; } } class D extends B { x = this.b; } new D().x",
)
.unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn static_class_field_sets_constructor_property() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class C { static x = 1; } C.x").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn static_class_block_binds_this_to_constructor() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { static x = 1; static { this.y = this.x + 1; } static z = this.y + 1; } C.z",
)
.unwrap();
assert_num(result, 3.0);
}
#[test]
fn computed_public_class_method_key_is_supported() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var k = 'm'; class C { [k](){ return 3; } } new C().m()").unwrap();
assert_eq!(result.as_int(), 3);
}
#[test]
fn computed_public_class_field_key_is_supported() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var k = 'x'; class C { [k] = 5; } new C().x").unwrap();
assert_eq!(result.as_int(), 5);
}
#[test]
fn computed_static_class_field_key_is_supported() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var k = 'x'; class C { static [k] = 7; } C.x").unwrap();
assert_eq!(result.as_int(), 7);
}
#[test]
fn private_class_field_read_returns_value() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class C { #x = 1; get(){ return this.#x; } } new C().get()").unwrap();
assert_eq!(result.as_int(), 1);
}
#[test]
fn private_class_field_write_round_trips() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x; set(v){ this.#x = v; } get(){ return this.#x; } } var c = new C(); c.set(4); c.get()",
)
.unwrap();
assert_eq!(result.as_int(), 4);
}
#[test]
fn private_class_method_call_uses_receiver() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class C { #m(){ return 4; } get(){ return this.#m(); } } new C().get()").unwrap();
assert_eq!(result.as_int(), 4);
}
#[test]
fn private_class_brand_in_checks_presence() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 1; has(o){ return #x in o; } } var c = new C(); c.has(c) && !c.has({})",
)
.unwrap();
assert!(result.is_bool() && result.as_bool());
}
#[test]
fn private_class_missing_brand_throws_type_error() {
let mut vm = Vm::new();
let err = eval(&mut vm, "class C { #x = 1; get(o){ return o.#x; } } new C().get({})").unwrap_err();
assert!(err.contains("TypeError"), "expected TypeError, got: {err}");
}
#[test]
fn private_static_field_and_method_work_on_constructor() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { static #x = 2; static #m(){ return this.#x + 3; } static get(){ return this.#m(); } } C.get()",
)
.unwrap();
assert_num(result, 5.0);
}
#[test]
fn derived_private_field_initializes_after_super() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class B { constructor(){ this.b = 3; } } class D extends B { #x = this.b; get(){ return this.#x; } } new D().get()",
)
.unwrap();
assert_eq!(result.as_int(), 3);
}
#[test]
fn private_class_fields_are_hidden_from_reflection_and_string_access() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 1; y = 2; keys(){ return Object.keys(this).length + Object.getOwnPropertyNames(this).length + (this['#x'] === undefined ? 10 : 0); } } new C().keys()",
)
.unwrap();
assert_num(result, 12.0);
}
#[test]
fn private_class_fields_are_hidden_from_for_in() {
let mut vm = Vm::new();
// 公有字段 y 可枚举(计数 1),私有 #x 不可见:若 #x 泄漏则计数为 2。
let result = eval(
&mut vm,
"class C { #x = 1; y = 2; } var o = new C(); var n = 0; for (var k in o) { n = n + 1; } n",
)
.unwrap();
assert_num(result, 1.0);
}
#[test]
fn private_class_same_name_different_classes_do_not_share_brand() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class A { #x = 1; has(o){ return #x in o; } } class B { #x = 2; } var a = new A(); a.has(a) && !a.has(new B())",
)
.unwrap();
assert!(result.is_bool() && result.as_bool());
}
#[test]
fn this_survives_native_call_inside_method() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { v=7; m(){ Object.keys(this); Object.getOwnPropertyNames(this); return this.v; } } new C().m()",
)
.unwrap();
assert_eq!(result.as_int(), 7);
}
#[test]
fn private_field_access_does_not_pierce_prototype() {
// P4:Object.create(instance) 的对象访问 #x 抛 TypeError(字段不跨原型链)。
let mut vm = Vm::new();
let err = eval(&mut vm, "class C { #x = 1; m() { return this.#x; } } Object.create(new C()).m()").unwrap_err();
assert!(err.contains("TypeError"), "expected TypeError, got: {err}");
}
#[test]
fn private_brand_in_does_not_pierce_prototype() {
// P5:#x in Object.create(instance) 为 false(PrivateFieldIn 只查 own)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 1; check(o) { return #x in o; } } var inst = new C(); var o = Object.create(inst); new C().check(o) === false && new C().check(inst) === true",
)
.unwrap();
assert!(result.as_bool());
}
#[test]
fn private_method_in_requires_own_brand() {
// 方法场景:#m in Object.create(instance) 为 false(无 own brand)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #m(){ return 1; } check(o) { return #m in o; } } var inst = new C(); new C().check(inst) === true && new C().check(Object.create(inst)) === false",
)
.unwrap();
assert!(result.as_bool());
}
#[test]
fn private_field_read_write_after_two_statements() {
// 回归:字段 set/get 跨语句边界正常(不依赖 brand cell 值比较)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 0; set(v){ this.#x = v; } get(){ return this.#x; } } var c = new C(); c.set(4); c.get()",
)
.unwrap();
assert_eq!(result.as_int(), 4);
}
#[test]
fn computed_field_key_evaluated_once_at_class_definition() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n=0; class C { [++n] = 1; [++n] = 2; } n").unwrap();
assert_num(result, 2.0);
}
#[test]
fn computed_field_key_once_but_value_per_instance() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var n=0; class C { [++n] = ++n; } var a=new C(); var b=new C(); n*100 + a[1]*10 + b[1]",
)
.unwrap();
assert_num(result, 323.0);
}
#[test]
fn computed_keys_evaluate_in_source_order_across_static_and_instance() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var i=0; class C { [i++] = i++; static [i++] = i++; [i++] = i++; } var c = new C(); i*1000 + c[0]*100 + c[2]*10 + C[1]",
)
.unwrap();
assert_num(result, 6453.0);
}
#[test]
fn field_definition_does_not_trigger_inherited_setter() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var log=[]; class B { set x(v){ log.push('setter'); } } class C extends B { x = 1; } var c = new C(); log.length*100 + (c.hasOwnProperty('x')?10:0) + c.x",
)
.unwrap();
assert_num(result, 11.0);
}
#[test]
fn field_value_captures_outer_variable() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n=0; class C { x = ++n; } var c = new C(); n*10 + c.x").unwrap();
assert_num(result, 11.0);
}
#[test]
fn class_expression_field_value_captures_outer_variable() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n=0; var C = class { x = ++n; }; new C(); n").unwrap();
assert_num(result, 1.0);
}
#[test]
fn field_initializers_run_once_on_second_super_call() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var n=0; class B {} var C = class extends B { field = ++n; constructor(){ super(); super(); } }; var err; try { new C(); } catch(e) { err = e.name; } (err === 'ReferenceError'?1:0)*10 + n",
)
.unwrap();
assert_num(result, 11.0);
}
#[test]
fn postfix_increment_on_captured_cell_returns_old_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var n=0; var arr = (function(){ return [n++, n, n++]; })(); arr[0]*1000 + arr[1]*100 + arr[2]*10 + n",
)
.unwrap();
assert_num(result, 112.0);
}