From 1c68a0365423662f9da3a4fb0e0ef8ed6a61ff47 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Mon, 8 Jun 2026 23:14:02 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20THROW=20=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=B1=BB=E5=9E=8B=E4=B8=8E=20Error=20=E5=AD=90?= =?UTF-8?q?=E7=B1=BB=E6=9E=84=E9=80=A0=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/bindings/bind_error.rs | 80 ++++++++++++++----- crates/oxide_vm/src/vm.rs | 25 ++++++ crates/oxide_vm/tests/builtins_error_tests.rs | 22 +++++ crates/oxide_vm/tests/regression_tests.rs | 28 +++++++ 4 files changed, 136 insertions(+), 19 deletions(-) diff --git a/crates/oxide_vm/src/bindings/bind_error.rs b/crates/oxide_vm/src/bindings/bind_error.rs index e64a8ef9..50ab6f8c 100644 --- a/crates/oxide_vm/src/bindings/bind_error.rs +++ b/crates/oxide_vm/src/bindings/bind_error.rs @@ -1,11 +1,59 @@ use std::sync::Arc; -use crate::bind_constructor_hash; use oxide_kernel::builtin::ErrorMethods; use oxide_kernel::kernel::OxideKernel; +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::JsObject; use oxide_types::value::JsValue; +fn bind_error_subtype_constructor( + kernel: &Arc, + global: &mut JsObject, + name: &str, + proto_ptr: *mut JsObject, + ctor_fn: *const (), +) { + let sf = kernel.string_forge().as_ref(); + let sh = kernel.shape_forge().as_ref(); + let function_proto_ptr = kernel.builtin_world().function_proto.as_ptr() as *mut JsObject; + + let mut ctor = Box::new(JsObject::new_empty( + EMPTY_SHAPE_ID, + JsValue::from_js_object(function_proto_ptr), + )); + ctor.set_function(true); + ctor.set_native_fn(Some(ctor_fn)); + ctor.set_native_arg_count(1); + + let si_prototype = sf.intern("prototype").0; + let si_name = sf.intern("name").0; + let name_si = sf.intern(name).0; + + let ctor_shape1 = sh.make_shape(EMPTY_SHAPE_ID, si_prototype); + let ctor_shape2 = sh.make_shape(ctor_shape1, si_name); + ctor.set_shape_id(ctor_shape2); + ctor.ensure_hash_props() + .push(Box::new(JsValue::from_js_object(proto_ptr))); + ctor.ensure_hash_props() + .push(Box::new(JsValue::string(name_si, 0))); + + let ctor_ptr = Box::into_raw(ctor); + + let proto = unsafe { &mut *proto_ptr }; + let proto_ctor_shape = sh.make_shape(proto.shape_id(), sf.intern("constructor").0); + proto.set_shape_id(proto_ctor_shape); + proto + .ensure_hash_props() + .push(Box::new(JsValue::from_js_object(ctor_ptr))); + + let global_shape = sh.make_shape(global.shape_id(), name_si); + global.set_shape_id(global_shape); + global + .ensure_hash_props() + .push(Box::new(JsValue::from_js_object(ctor_ptr))); + global.bump_generation(); +} + pub fn bind_error(kernel: &Arc, global: &mut JsObject) { let error_methods = ErrorMethods { error: crate::builtins::error::error_constructor as *const (), @@ -32,53 +80,47 @@ pub fn bind_error(kernel: &Arc, global: &mut JsObject) { global.ensure_hash_props().push(Box::new(err_val)); global.bump_generation(); - bind_constructor_hash!( + bind_error_subtype_constructor( kernel, global, "TypeError", kernel.builtin_world().type_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::type_error_constructor, - 1 + crate::builtins::error::type_error_constructor as *const (), ); - bind_constructor_hash!( + bind_error_subtype_constructor( kernel, global, "ReferenceError", kernel.builtin_world().reference_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::reference_error_constructor, - 1 + crate::builtins::error::reference_error_constructor as *const (), ); - bind_constructor_hash!( + bind_error_subtype_constructor( kernel, global, "RangeError", kernel.builtin_world().range_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::range_error_constructor, - 1 + crate::builtins::error::range_error_constructor as *const (), ); - bind_constructor_hash!( + bind_error_subtype_constructor( kernel, global, "SyntaxError", kernel.builtin_world().syntax_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::syntax_error_constructor, - 1 + crate::builtins::error::syntax_error_constructor as *const (), ); - bind_constructor_hash!( + bind_error_subtype_constructor( kernel, global, "URIError", kernel.builtin_world().uri_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::uri_error_constructor, - 1 + crate::builtins::error::uri_error_constructor as *const (), ); - bind_constructor_hash!( + bind_error_subtype_constructor( kernel, global, "EvalError", kernel.builtin_world().eval_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::eval_error_constructor, - 1 + crate::builtins::error::eval_error_constructor as *const (), ); { diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 26da7736..3b763021 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -390,6 +390,30 @@ impl Vm { self.kernel.string_forge().lookup(val.as_string_index()) } + fn thrown_error_kind(&self, val: JsValue) -> &'static str { + if !val.is_object() { + return "Error"; + } + let name_si = self.kernel.string_forge().intern("name").0; + let obj = unsafe { &*val.as_js_object_ptr() }; + let Some(name_val) = self.resolve_property(obj, name_si) else { + return "Error"; + }; + let Some(name) = self.lookup_str(name_val) else { + return "Error"; + }; + match name.as_str() { + "TypeError" => "TypeError", + "ReferenceError" => "ReferenceError", + "RangeError" => "RangeError", + "SyntaxError" => "SyntaxError", + "URIError" => "URIError", + "EvalError" => "EvalError", + "Error" => "Error", + _ => "Error", + } + } + fn property_key_si(&mut self, val: JsValue) -> u32 { if val.is_string() { return val.as_string_index(); @@ -1823,6 +1847,7 @@ impl Vm { OpCode::THROW => { let exc_value = self.regs[rd]; self.exception_value = Some(exc_value); + self.pending_error_kind = Some(self.thrown_error_kind(exc_value)); match self.unwind() { Ok(()) => continue, Err(e) => return Err(e), diff --git a/crates/oxide_vm/tests/builtins_error_tests.rs b/crates/oxide_vm/tests/builtins_error_tests.rs index f3df53c1..1e9cf014 100644 --- a/crates/oxide_vm/tests/builtins_error_tests.rs +++ b/crates/oxide_vm/tests/builtins_error_tests.rs @@ -137,3 +137,25 @@ fn reference_error_is_defined() { let result = eval("typeof ReferenceError").unwrap(); assert!(result.is_string()); } + +#[test] +fn error_subtype_constructors_produce_named_objects() { + assert_eq!( + format!("{}", eval("new Error('boom').name == 'Error'").unwrap()), + "true" + ); + assert_eq!( + format!( + "{}", + eval("new TypeError('boom').name == 'TypeError'").unwrap() + ), + "true" + ); + assert_eq!( + format!( + "{}", + eval("new SyntaxError('boom').name == 'SyntaxError'").unwrap() + ), + "true" + ); +} diff --git a/crates/oxide_vm/tests/regression_tests.rs b/crates/oxide_vm/tests/regression_tests.rs index 38adee39..1a9d04bd 100644 --- a/crates/oxide_vm/tests/regression_tests.rs +++ b/crates/oxide_vm/tests/regression_tests.rs @@ -84,6 +84,34 @@ fn regression_throw_statement_errors() { result.contains("uncaught"), "expected uncaught, got: {result}" ); + assert!( + result.contains("uncaught Error"), + "expected string throw to default to Error, got: {result}" + ); +} + +#[test] +fn regression_throw_statement_preserves_type_error_kind() { + assert_eq!( + eval("try { throw new TypeError('boom') } catch (e) { e.name == 'TypeError' }"), + "true" + ); + assert_eq!( + eval("throw new TypeError('boom')"), + "vm error: uncaught TypeError: {object}" + ); +} + +#[test] +fn regression_throw_statement_preserves_syntax_error_kind() { + assert_eq!( + eval("try { throw new SyntaxError('boom') } catch (e) { e.name == 'SyntaxError' }"), + "true" + ); + assert_eq!( + eval("throw new SyntaxError('boom')"), + "vm error: uncaught SyntaxError: {object}" + ); } #[test]