From 2063f4731b0a0ed9df49c0199cf1fd854f338b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Sun, 7 Jun 2026 10:20:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E5=AF=B9=E8=B1=A1=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_kernel/src/builtin.rs | 90 ++++++++++++++ crates/oxide_vm/src/builtins/error.rs | 115 ++++++++++++++++++ crates/oxide_vm/src/builtins/mod.rs | 1 + crates/oxide_vm/src/vm.rs | 28 ++++- crates/oxide_vm/tests/builtins_error_tests.rs | 109 +++++++++++++++++ 5 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 crates/oxide_vm/src/builtins/error.rs create mode 100644 crates/oxide_vm/tests/builtins_error_tests.rs diff --git a/crates/oxide_kernel/src/builtin.rs b/crates/oxide_kernel/src/builtin.rs index 2659095..46a8e34 100644 --- a/crates/oxide_kernel/src/builtin.rs +++ b/crates/oxide_kernel/src/builtin.rs @@ -34,6 +34,16 @@ pub struct ArrayMethods { pub flat_map: *const (), } +pub struct ErrorMethods { + pub error: *const (), + pub type_error: *const (), + pub reference_error: *const (), + pub range_error: *const (), + pub syntax_error: *const (), + pub uri_error: *const (), + pub eval_error: *const (), +} + pub struct BuiltinWorld { pub object_proto: P, pub array_proto: P, @@ -51,6 +61,12 @@ pub struct BuiltinWorld { pub boolean_constructor: P, pub error_constructor: P, pub symbol_constructor: P, + pub type_error_proto: P, + pub reference_error_proto: P, + pub range_error_proto: P, + pub syntax_error_proto: P, + pub uri_error_proto: P, + pub eval_error_proto: P, } fn intern_label(string_forge: &StringForge, label: &str) -> u32 { @@ -161,6 +177,14 @@ impl BuiltinWorld { si_name, ); + let error_proto_val = JsValue::from_js_object(error_proto.as_ptr() as *mut JsObject); + let type_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); + let reference_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); + let range_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); + let syntax_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); + let uri_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); + let eval_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); + Self { object_proto, array_proto, @@ -178,6 +202,12 @@ impl BuiltinWorld { boolean_constructor, error_constructor, symbol_constructor, + type_error_proto, + reference_error_proto, + range_error_proto, + syntax_error_proto, + uri_error_proto, + eval_error_proto, } } @@ -303,6 +333,66 @@ impl BuiltinWorld { ); } + pub fn bind_error_methods( + &self, + methods: &ErrorMethods, + string_forge: &StringForge, + shape_forge: &ShapeForge, + ) { + let ctor_ptr = P::as_ptr(&self.error_constructor) as *mut JsObject; + let ctor = unsafe { &mut *ctor_ptr }; + + let _ = Self::bind_method(ctor, shape_forge, string_forge, "Error", methods.error, 1); + let _ = Self::bind_method( + ctor, + shape_forge, + string_forge, + "TypeError", + methods.type_error, + 1, + ); + let _ = Self::bind_method( + ctor, + shape_forge, + string_forge, + "ReferenceError", + methods.reference_error, + 1, + ); + let _ = Self::bind_method( + ctor, + shape_forge, + string_forge, + "RangeError", + methods.range_error, + 1, + ); + let _ = Self::bind_method( + ctor, + shape_forge, + string_forge, + "SyntaxError", + methods.syntax_error, + 1, + ); + let _ = Self::bind_method( + ctor, + shape_forge, + string_forge, + "URIError", + methods.uri_error, + 1, + ); + let _ = Self::bind_method( + ctor, + shape_forge, + string_forge, + "EvalError", + methods.eval_error, + 1, + ); + } + pub fn bind_method( proto: &mut JsObject, shape_forge: &ShapeForge, diff --git a/crates/oxide_vm/src/builtins/error.rs b/crates/oxide_vm/src/builtins/error.rs new file mode 100644 index 0000000..30803e4 --- /dev/null +++ b/crates/oxide_vm/src/builtins/error.rs @@ -0,0 +1,115 @@ +use std::sync::Arc; + +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_types::mem::P; +use oxide_types::object::JsObject; +use oxide_types::value::JsValue; + +use crate::coercion; +use crate::native::NativeResult; +use crate::vm::Vm; + +fn create_error_object( + vm: &mut Vm, + error_proto_ptr: *mut JsObject, + name: &str, + message: &str, +) -> JsValue { + let obj = vm.epoch().alloc(JsObject::new_empty( + EMPTY_SHAPE_ID, + JsValue::from_js_object(error_proto_ptr), + )); + + let sf = Arc::clone(vm.kernel().string_forge()); + let sh = Arc::clone(vm.kernel().shape_forge()); + + let si_message = sf.intern("message").0; + let sh_message = sh.make_shape(EMPTY_SHAPE_ID, si_message); + let msg_val = vm.intern(message); + unsafe { + (*obj).set_shape_id(sh_message); + (*obj).set_prop_count(1); + (*obj).set_inline_prop(0, msg_val); + } + + let si_name = sf.intern("name").0; + let sh_name = sh.make_shape(sh_message, si_name); + let name_val = vm.intern(name); + unsafe { + (*obj).set_shape_id(sh_name); + (*obj).set_prop_count(2); + (*obj).set_inline_prop(1, name_val); + } + + JsValue::from_js_object(obj) +} + +pub fn create_type_error(vm: &mut Vm, msg: &str) -> JsValue { + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().type_error_proto) as *mut JsObject; + create_error_object(vm, proto_ptr, "TypeError", msg) +} + +pub fn create_reference_error(vm: &mut Vm, msg: &str) -> JsValue { + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().reference_error_proto) as *mut JsObject; + create_error_object(vm, proto_ptr, "ReferenceError", msg) +} + +pub fn create_range_error(vm: &mut Vm, msg: &str) -> JsValue { + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().range_error_proto) as *mut JsObject; + create_error_object(vm, proto_ptr, "RangeError", msg) +} + +pub fn create_syntax_error(vm: &mut Vm, msg: &str) -> JsValue { + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().syntax_error_proto) as *mut JsObject; + create_error_object(vm, proto_ptr, "SyntaxError", msg) +} + +fn get_msg(vm: &mut Vm, args: &[u8]) -> String { + if args.len() > 1 { + coercion::to_string(vm.kernel().string_forge().as_ref(), vm.reg(args[1])) + } else { + String::new() + } +} + +pub fn error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let msg = get_msg(vm, args); + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().error_proto) as *mut JsObject; + Ok(create_error_object(vm, proto_ptr, "Error", &msg)) +} + +pub fn type_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let msg = get_msg(vm, args); + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().type_error_proto) as *mut JsObject; + Ok(create_error_object(vm, proto_ptr, "TypeError", &msg)) +} + +pub fn reference_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let msg = get_msg(vm, args); + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().reference_error_proto) as *mut JsObject; + Ok(create_error_object(vm, proto_ptr, "ReferenceError", &msg)) +} + +pub fn range_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let msg = get_msg(vm, args); + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().range_error_proto) as *mut JsObject; + Ok(create_error_object(vm, proto_ptr, "RangeError", &msg)) +} + +pub fn syntax_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let msg = get_msg(vm, args); + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().syntax_error_proto) as *mut JsObject; + Ok(create_error_object(vm, proto_ptr, "SyntaxError", &msg)) +} + +pub fn uri_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let msg = get_msg(vm, args); + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().uri_error_proto) as *mut JsObject; + Ok(create_error_object(vm, proto_ptr, "URIError", &msg)) +} + +pub fn eval_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { + let msg = get_msg(vm, args); + let proto_ptr = P::as_ptr(&vm.kernel().builtin_world().eval_error_proto) as *mut JsObject; + Ok(create_error_object(vm, proto_ptr, "EvalError", &msg)) +} diff --git a/crates/oxide_vm/src/builtins/mod.rs b/crates/oxide_vm/src/builtins/mod.rs index e634afe..7634e0e 100644 --- a/crates/oxide_vm/src/builtins/mod.rs +++ b/crates/oxide_vm/src/builtins/mod.rs @@ -1,3 +1,4 @@ pub mod array; +pub mod error; pub mod global; pub mod object; diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 1436792..676daf6 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -8,7 +8,7 @@ use oxide_compiler::opcode::{self, OpCode}; use crate::coercion; use crate::native::NativeFn; -use oxide_kernel::builtin::{ArrayMethods, ObjectMethods}; +use oxide_kernel::builtin::{ArrayMethods, ErrorMethods, ObjectMethods}; use oxide_kernel::kernel::{KernelConfig, OxideKernel}; use oxide_kernel::prop_forge::PropTemplate; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; @@ -105,6 +105,32 @@ pub fn init_kernel_builtins(kernel: &Arc) { let bump2 = bumpalo::Bump::new(); global.set_prop_expand(cur_count, arr_val, &bump2); global.bump_generation(); + + let error_methods = ErrorMethods { + error: crate::builtins::error::error_constructor as *const (), + type_error: crate::builtins::error::type_error_constructor as *const (), + reference_error: crate::builtins::error::reference_error_constructor as *const (), + range_error: crate::builtins::error::range_error_constructor as *const (), + syntax_error: crate::builtins::error::syntax_error_constructor as *const (), + uri_error: crate::builtins::error::uri_error_constructor as *const (), + eval_error: crate::builtins::error::eval_error_constructor as *const (), + }; + kernel.builtin_world().bind_error_methods( + &error_methods, + kernel.string_forge().as_ref(), + kernel.shape_forge().as_ref(), + ); + + let si_err = kernel.string_forge().intern("Error").0; + let err_shape = kernel.shape_forge().make_shape(global.shape_id(), si_err); + let err_val = + JsValue::from_js_object(kernel.builtin_world().error_constructor.as_ptr() as *mut JsObject); + let cur_count = global.prop_count(); + global.set_shape_id(err_shape); + global.set_prop_count(cur_count + 1); + let bump3 = bumpalo::Bump::new(); + global.set_prop_expand(cur_count, err_val, &bump3); + global.bump_generation(); } impl Vm { diff --git a/crates/oxide_vm/tests/builtins_error_tests.rs b/crates/oxide_vm/tests/builtins_error_tests.rs new file mode 100644 index 0000000..c32edc6 --- /dev/null +++ b/crates/oxide_vm/tests/builtins_error_tests.rs @@ -0,0 +1,109 @@ +use oxide_vm::builtins::error; +use oxide_vm::vm::Vm; + +fn make_vm() -> Vm { + Vm::new() +} + +#[test] +fn error_constructor_creates_object() { + let mut vm = make_vm(); + let result = error::error_constructor(&mut vm, &[0u8]).unwrap(); + assert!(result.is_object()); +} + +#[test] +fn error_has_message() { + let mut vm = make_vm(); + let msg = vm.intern("test message"); + vm.set_reg(1, msg); + let result = error::error_constructor(&mut vm, &[0u8, 1u8]).unwrap(); + assert!(result.is_object()); +} + +#[test] +fn type_error_constructor_creates_object() { + let mut vm = make_vm(); + let result = error::type_error_constructor(&mut vm, &[0u8]).unwrap(); + assert!(result.is_object()); +} + +#[test] +fn reference_error_constructor_creates_object() { + let mut vm = make_vm(); + let result = error::reference_error_constructor(&mut vm, &[0u8]).unwrap(); + assert!(result.is_object()); +} + +#[test] +fn range_error_constructor_creates_object() { + let mut vm = make_vm(); + let result = error::range_error_constructor(&mut vm, &[0u8]).unwrap(); + assert!(result.is_object()); +} + +#[test] +fn syntax_error_constructor_creates_object() { + let mut vm = make_vm(); + let result = error::syntax_error_constructor(&mut vm, &[0u8]).unwrap(); + assert!(result.is_object()); +} + +#[test] +fn uri_error_constructor_creates_object() { + let mut vm = make_vm(); + let result = error::uri_error_constructor(&mut vm, &[0u8]).unwrap(); + assert!(result.is_object()); +} + +#[test] +fn eval_error_constructor_creates_object() { + let mut vm = make_vm(); + let result = error::eval_error_constructor(&mut vm, &[0u8]).unwrap(); + assert!(result.is_object()); +} + +#[test] +fn create_type_error_returns_jsvalue() { + let mut vm = make_vm(); + let err = error::create_type_error(&mut vm, "something went wrong"); + assert!(err.is_object()); +} + +#[test] +fn create_range_error_returns_jsvalue() { + let mut vm = make_vm(); + let err = error::create_range_error(&mut vm, "out of bounds"); + assert!(err.is_object()); +} + +#[test] +fn create_reference_error_returns_jsvalue() { + let mut vm = make_vm(); + let err = error::create_reference_error(&mut vm, "not defined"); + assert!(err.is_object()); +} + +#[test] +fn create_syntax_error_returns_jsvalue() { + let mut vm = make_vm(); + let err = error::create_syntax_error(&mut vm, "invalid syntax"); + assert!(err.is_object()); +} + +#[test] +fn error_proto_chain_subtype_points_to_error() { + let mut vm = make_vm(); + let err = error::create_type_error(&mut vm, "msg"); + let obj = unsafe { &*err.as_js_object_ptr() }; + assert!(obj.proto().is_object()); +} + +#[test] +fn error_name_is_string_property() { + let mut vm = make_vm(); + let err = error::error_constructor(&mut vm, &[0u8]).unwrap(); + let obj = unsafe { &*err.as_js_object_ptr() }; + let n = obj.prop_count(); + assert!(n >= 2); +}