feat: 支持基础 class 声明表达式与原型方法

This commit is contained in:
爱特每羊队
2026-06-09 08:38:21 +08:00
parent 5d1d25074e
commit 85e78bd248
13 changed files with 535 additions and 31 deletions
+70
View File
@@ -0,0 +1,70 @@
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)
}
#[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_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);
}
+11 -4
View File
@@ -158,20 +158,27 @@ fn fd_returns_new_array() {
fn this_in_function_reads_value() {
let mut vm = Vm::new();
let result = eval(&mut vm, "function f() { return this; } f()").unwrap();
assert!(result.is_object(), "expected object (function as this), got: {:?}", result);
assert!(result.is_undefined(), "expected undefined in strict-mode call, got: {:?}", result);
}
#[test]
fn this_in_function_assign_member() {
let mut vm = Vm::new();
let result = eval(&mut vm, "function f(m) { this.message = m; } f('hello'); 1").unwrap();
assert_eq!(result.as_int(), 1);
let err = eval(&mut vm, "function f(m) { this.message = m; } f('hello'); 1").unwrap_err();
assert!(err.contains("TypeError"), "expected TypeError, got: {err}");
}
#[test]
fn this_member_access() {
let mut vm = Vm::new();
let result = eval(&mut vm, "function f() { this.x = 42; return this.x; } f()").unwrap();
let err = eval(&mut vm, "function f() { this.x = 42; return this.x; } f()").unwrap_err();
assert!(err.contains("TypeError"), "expected TypeError, got: {err}");
}
#[test]
fn member_call_uses_receiver_as_this() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var o = { x: 42, f: function() { return this.x; } }; o.f()").unwrap();
assert_eq!(result.as_int(), 42);
}
+2 -2
View File
@@ -128,9 +128,9 @@ fn regression_new_expression_native_constructor_error_is_catchable() {
#[test]
fn regression_new_expression_bytecode_constructor_error_is_catchable() {
assert_eq!(
eval("try { function C() {} new C(); 0 } catch (e) { 1 }"),
eval("try { class C { constructor() { throw new Error('boom') } } new C(); 0 } catch (e) { 1 }"),
"1",
"expected bytecode constructor capability error to enter catch"
"expected bytecode class constructor error to enter catch"
);
}