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

640 lines
21 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);
}
// ── class 生成器方法 ──
// 实例生成器方法:yield 顺序与 done 收敛,生成器对象经 next 驱动。
#[test]
fn class_generator_method_yields_in_order() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { *g() { yield 10; yield 20; } } var it = new C().g(); [it.next().value, it.next().value, it.next().done].join(',')",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "10,20,true");
}
// 静态生成器方法:挂在构造函数上,调用形态与实例方法一致。
#[test]
fn class_static_generator_method_yields() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class D { static *sg() { yield 30; } } var it = D.sg(); [it.next().value, it.next().done].join(',')",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "30,true");
}
// 生成器方法 throw 传播:throw 进挂起点,异常可被 body 内 catch 拦截。
#[test]
fn class_generator_method_throw_reaches_inner_catch() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class F { *t() { try { yield 1; } catch (e) { yield 'caught'; } } } var it = new F().t(); var r1 = it.next(); var r2 = it.throw(new Error('x')); [r1.value, r2.value].join(',')",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "1,caught");
}
// 生成器方法 next(arg):参数作为 yield 表达式结果传入 body。
#[test]
fn class_generator_method_next_arg_feeds_yield_expression() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class G { *a() { var v = yield 1; yield v * 2; } } var it = new G().a(); var r1 = it.next(); var r2 = it.next(21); [r1.value, r2.value].join(',')",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "1,42");
}
// 生成器方法 yield* 委托:与函数式生成器一致的委托序列。
#[test]
fn class_generator_method_yield_star_delegates() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class H { *ys() { yield* [1, 2, 3]; } } Array.from(new H().ys()).join(',')").unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "1,2,3");
}
// 生成器方法 return():提前关闭生成器并携带返回值。
#[test]
fn class_generator_method_return_closes_with_value() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class I { *rt() { yield 1; yield 2; } } var it = new I().rt(); var r1 = it.next(); var r2 = it.return(99); [r1.value, r2.value, r2.done].join(',')",
)
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "1,99,true");
}