mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: Error原型属性、规范toString、V8风格stack trace
This commit is contained in:
@@ -55,11 +55,13 @@ fn js_error_kind_name(kind: JsErrorKind) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn format_error_message(kind: &str, msg: &str) -> String {
|
||||
if msg.is_empty() || msg == kind || msg.starts_with(&format!("{kind}:")) {
|
||||
pub(crate) fn format_error_message(name: &str, msg: &str) -> String {
|
||||
if name.is_empty() {
|
||||
msg.to_string()
|
||||
} else if msg.is_empty() {
|
||||
name.to_string()
|
||||
} else {
|
||||
format!("{kind}: {msg}")
|
||||
format!("{name}: {msg}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use oxide_builtins::error;
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::mem::P;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
@@ -115,7 +116,7 @@ fn error_name_is_string_property() {
|
||||
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);
|
||||
assert_eq!(n, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -142,3 +143,224 @@ fn error_subtype_constructors_produce_named_objects() {
|
||||
assert_eq!(format!("{}", eval("new TypeError('boom').name == 'TypeError'").unwrap()), "true");
|
||||
assert_eq!(format!("{}", eval("new SyntaxError('boom').name == 'SyntaxError'").unwrap()), "true");
|
||||
}
|
||||
|
||||
// New tests for prototype properties + constructor fixes
|
||||
|
||||
#[test]
|
||||
fn error_prototype_has_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
.lookup_position(unsafe { &*proto_ptr }.shape_id(), name_si);
|
||||
assert!(si.is_some(), "Error.prototype should have 'name' property");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_prototype_has_message() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let msg_si = vm.kernel_core().perm_interner().intern("message").0;
|
||||
let proto_ptr = P::as_ptr(&bw.error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
.lookup_position(unsafe { &*proto_ptr }.shape_id(), msg_si);
|
||||
assert!(si.is_some(), "Error.prototype should have 'message' property");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.type_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
.lookup_position(unsafe { &*proto_ptr }.shape_id(), name_si);
|
||||
assert!(si.is_some(), "TypeError.prototype should have 'name'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reference_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.reference_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
.lookup_position(unsafe { &*proto_ptr }.shape_id(), name_si);
|
||||
assert!(si.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.range_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
.lookup_position(unsafe { &*proto_ptr }.shape_id(), name_si);
|
||||
assert!(si.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syntax_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.syntax_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
.lookup_position(unsafe { &*proto_ptr }.shape_id(), name_si);
|
||||
assert!(si.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uri_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.uri_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
.lookup_position(unsafe { &*proto_ptr }.shape_id(), name_si);
|
||||
assert!(si.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_error_prototype_name() {
|
||||
let vm = make_vm();
|
||||
let bw = vm.session().builtin_world();
|
||||
let name_si = vm.kernel_core().perm_interner().intern("name").0;
|
||||
let proto_ptr = P::as_ptr(&bw.eval_error_proto) as *mut oxide_types::object::JsObject;
|
||||
let si = vm
|
||||
.kernel_core()
|
||||
.shape_forge()
|
||||
.lookup_position(unsafe { &*proto_ptr }.shape_id(), name_si);
|
||||
assert!(si.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_constructor_no_own_props() {
|
||||
let mut vm = make_vm();
|
||||
let err = error::error_constructor(&mut vm, &[0u8]).unwrap();
|
||||
let obj = unsafe { &*err.as_js_object_ptr() };
|
||||
assert_eq!(obj.prop_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_constructor_with_message() {
|
||||
let mut vm = make_vm();
|
||||
let msg = vm.new_string("boom");
|
||||
vm.set_reg(1, msg);
|
||||
let err = error::error_constructor(&mut vm, &[0u8, 1u8]).unwrap();
|
||||
let obj = unsafe { &*err.as_js_object_ptr() };
|
||||
assert_eq!(obj.prop_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_type_error_no_own_name() {
|
||||
let mut vm = make_vm();
|
||||
let err = error::create_type_error(&mut vm, "something broken");
|
||||
let obj = unsafe { &*err.as_js_object_ptr() };
|
||||
assert_eq!(obj.prop_count(), 1);
|
||||
}
|
||||
|
||||
// Plan 02: toString tests
|
||||
|
||||
#[test]
|
||||
fn error_to_string_basic() {
|
||||
let result = eval("new Error('test').toString()").unwrap();
|
||||
assert!(result.is_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_to_string_empty_message() {
|
||||
let result = eval("new Error().toString()").unwrap();
|
||||
let vm = make_vm();
|
||||
let s = vm.lookup_str(result);
|
||||
assert_eq!(s, Some("Error".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_to_string_type_error() {
|
||||
let result = eval("new TypeError('bad').toString()").unwrap();
|
||||
let vm = make_vm();
|
||||
let s = vm.lookup_str(result);
|
||||
assert_eq!(s, Some("TypeError: bad".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_to_string_name_only() {
|
||||
let result = eval("Error.prototype.toString.call({name: 'E'})").unwrap();
|
||||
let vm = make_vm();
|
||||
let s = vm.lookup_str(result);
|
||||
assert_eq!(s, Some("E".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_to_string_non_object_throws() {
|
||||
let mut vm = make_vm();
|
||||
vm.set_reg(0, JsValue::int(42));
|
||||
let result = error::error_to_string(&mut vm, &[0u8]);
|
||||
match result {
|
||||
oxide_runtime_api::NativeResult::Err(_) => {}
|
||||
_ => panic!("expected Err, got Ok or TailCall"),
|
||||
}
|
||||
}
|
||||
|
||||
// format_error_message tests
|
||||
|
||||
#[test]
|
||||
fn format_error_message_both() {
|
||||
let result = oxide_runtime_api::format_error_message("TypeError", "bad arg");
|
||||
assert_eq!(result, "TypeError: bad arg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_error_message_empty_msg() {
|
||||
let result = oxide_runtime_api::format_error_message("Error", "");
|
||||
assert_eq!(result, "Error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_error_message_empty_name() {
|
||||
let result = oxide_runtime_api::format_error_message("", "msg");
|
||||
assert_eq!(result, "msg");
|
||||
}
|
||||
|
||||
// stack tests
|
||||
|
||||
#[test]
|
||||
fn error_stack_is_string() {
|
||||
let result = eval("typeof new Error().stack()").unwrap();
|
||||
let vm = make_vm();
|
||||
let s = vm.lookup_str(result);
|
||||
assert_eq!(s, Some("string".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_stack_starts_with_header() {
|
||||
let result = eval("new Error().stack()").unwrap();
|
||||
let vm = make_vm();
|
||||
let s = vm.lookup_str(result).unwrap();
|
||||
assert!(s.starts_with("Error"), "stack should start with 'Error', got: {}", s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_stack_frame_format() {
|
||||
let result = eval("(function foo() { return new Error('boom').stack(); })()").unwrap();
|
||||
let vm = make_vm();
|
||||
let s = vm.lookup_str(result).unwrap();
|
||||
assert!(s.contains(" at "), "stack should have 4-space indent, got: {}", s);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user