fix: 修复 THROW 错误类型与 Error 子类构造器

This commit is contained in:
2026-06-08 23:14:02 +08:00
parent be199ca077
commit 1c68a03654
4 changed files with 136 additions and 19 deletions
+61 -19
View File
@@ -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<OxideKernel>,
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<OxideKernel>, 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<OxideKernel>, 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 (),
);
{
+25
View File
@@ -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),
@@ -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"
);
}
+28
View File
@@ -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]