fix: 修复 THROW 错误类型与 Error 子类构造器
This commit is contained in:
@@ -1,11 +1,59 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::bind_constructor_hash;
|
|
||||||
use oxide_kernel::builtin::ErrorMethods;
|
use oxide_kernel::builtin::ErrorMethods;
|
||||||
use oxide_kernel::kernel::OxideKernel;
|
use oxide_kernel::kernel::OxideKernel;
|
||||||
|
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||||
use oxide_types::object::JsObject;
|
use oxide_types::object::JsObject;
|
||||||
use oxide_types::value::JsValue;
|
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) {
|
pub fn bind_error(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||||
let error_methods = ErrorMethods {
|
let error_methods = ErrorMethods {
|
||||||
error: crate::builtins::error::error_constructor as *const (),
|
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.ensure_hash_props().push(Box::new(err_val));
|
||||||
global.bump_generation();
|
global.bump_generation();
|
||||||
|
|
||||||
bind_constructor_hash!(
|
bind_error_subtype_constructor(
|
||||||
kernel,
|
kernel,
|
||||||
global,
|
global,
|
||||||
"TypeError",
|
"TypeError",
|
||||||
kernel.builtin_world().type_error_proto.as_ptr() as *mut JsObject,
|
kernel.builtin_world().type_error_proto.as_ptr() as *mut JsObject,
|
||||||
crate::builtins::error::type_error_constructor,
|
crate::builtins::error::type_error_constructor as *const (),
|
||||||
1
|
|
||||||
);
|
);
|
||||||
bind_constructor_hash!(
|
bind_error_subtype_constructor(
|
||||||
kernel,
|
kernel,
|
||||||
global,
|
global,
|
||||||
"ReferenceError",
|
"ReferenceError",
|
||||||
kernel.builtin_world().reference_error_proto.as_ptr() as *mut JsObject,
|
kernel.builtin_world().reference_error_proto.as_ptr() as *mut JsObject,
|
||||||
crate::builtins::error::reference_error_constructor,
|
crate::builtins::error::reference_error_constructor as *const (),
|
||||||
1
|
|
||||||
);
|
);
|
||||||
bind_constructor_hash!(
|
bind_error_subtype_constructor(
|
||||||
kernel,
|
kernel,
|
||||||
global,
|
global,
|
||||||
"RangeError",
|
"RangeError",
|
||||||
kernel.builtin_world().range_error_proto.as_ptr() as *mut JsObject,
|
kernel.builtin_world().range_error_proto.as_ptr() as *mut JsObject,
|
||||||
crate::builtins::error::range_error_constructor,
|
crate::builtins::error::range_error_constructor as *const (),
|
||||||
1
|
|
||||||
);
|
);
|
||||||
bind_constructor_hash!(
|
bind_error_subtype_constructor(
|
||||||
kernel,
|
kernel,
|
||||||
global,
|
global,
|
||||||
"SyntaxError",
|
"SyntaxError",
|
||||||
kernel.builtin_world().syntax_error_proto.as_ptr() as *mut JsObject,
|
kernel.builtin_world().syntax_error_proto.as_ptr() as *mut JsObject,
|
||||||
crate::builtins::error::syntax_error_constructor,
|
crate::builtins::error::syntax_error_constructor as *const (),
|
||||||
1
|
|
||||||
);
|
);
|
||||||
bind_constructor_hash!(
|
bind_error_subtype_constructor(
|
||||||
kernel,
|
kernel,
|
||||||
global,
|
global,
|
||||||
"URIError",
|
"URIError",
|
||||||
kernel.builtin_world().uri_error_proto.as_ptr() as *mut JsObject,
|
kernel.builtin_world().uri_error_proto.as_ptr() as *mut JsObject,
|
||||||
crate::builtins::error::uri_error_constructor,
|
crate::builtins::error::uri_error_constructor as *const (),
|
||||||
1
|
|
||||||
);
|
);
|
||||||
bind_constructor_hash!(
|
bind_error_subtype_constructor(
|
||||||
kernel,
|
kernel,
|
||||||
global,
|
global,
|
||||||
"EvalError",
|
"EvalError",
|
||||||
kernel.builtin_world().eval_error_proto.as_ptr() as *mut JsObject,
|
kernel.builtin_world().eval_error_proto.as_ptr() as *mut JsObject,
|
||||||
crate::builtins::error::eval_error_constructor,
|
crate::builtins::error::eval_error_constructor as *const (),
|
||||||
1
|
|
||||||
);
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -390,6 +390,30 @@ impl Vm {
|
|||||||
self.kernel.string_forge().lookup(val.as_string_index())
|
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 {
|
fn property_key_si(&mut self, val: JsValue) -> u32 {
|
||||||
if val.is_string() {
|
if val.is_string() {
|
||||||
return val.as_string_index();
|
return val.as_string_index();
|
||||||
@@ -1823,6 +1847,7 @@ impl Vm {
|
|||||||
OpCode::THROW => {
|
OpCode::THROW => {
|
||||||
let exc_value = self.regs[rd];
|
let exc_value = self.regs[rd];
|
||||||
self.exception_value = Some(exc_value);
|
self.exception_value = Some(exc_value);
|
||||||
|
self.pending_error_kind = Some(self.thrown_error_kind(exc_value));
|
||||||
match self.unwind() {
|
match self.unwind() {
|
||||||
Ok(()) => continue,
|
Ok(()) => continue,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
|
|||||||
@@ -137,3 +137,25 @@ fn reference_error_is_defined() {
|
|||||||
let result = eval("typeof ReferenceError").unwrap();
|
let result = eval("typeof ReferenceError").unwrap();
|
||||||
assert!(result.is_string());
|
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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -84,6 +84,34 @@ fn regression_throw_statement_errors() {
|
|||||||
result.contains("uncaught"),
|
result.contains("uncaught"),
|
||||||
"expected uncaught, got: {result}"
|
"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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user