mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Stringify unknown JavaScript objects in global exception handlers
When turning DOM exceptions into `ErrorInfo` always try to stringify the JavaScript value, even if it's an object that isn't a `DOMException` or native exception. This means that exceptions that extend the `Error` prototype are now stringified. The result is that test output for WPT global assertion failures is more useful. For instance for the test include-frames-from-child-same-origin-grandchild.sub.html: Before: ``` uncaught exception: unknown (can't convert to string) ``` After: ``` uncaught exception: Error: assert_equals: expected 4 but got 3 ```
This commit is contained in:
parent
633f14df11
commit
e68ebd2617
3 changed files with 81 additions and 30 deletions
|
@ -29,8 +29,7 @@ use js::jsval::UndefinedValue;
|
||||||
use js::rust::wrappers::JS_ErrorFromException;
|
use js::rust::wrappers::JS_ErrorFromException;
|
||||||
use js::rust::wrappers::JS_GetPendingException;
|
use js::rust::wrappers::JS_GetPendingException;
|
||||||
use js::rust::wrappers::JS_SetPendingException;
|
use js::rust::wrappers::JS_SetPendingException;
|
||||||
use js::rust::HandleObject;
|
use js::rust::{HandleObject, HandleValue, MutableHandleValue};
|
||||||
use js::rust::MutableHandleValue;
|
|
||||||
use libc::c_uint;
|
use libc::c_uint;
|
||||||
use std::slice::from_raw_parts;
|
use std::slice::from_raw_parts;
|
||||||
|
|
||||||
|
@ -181,7 +180,7 @@ pub struct ErrorInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorInfo {
|
impl ErrorInfo {
|
||||||
unsafe fn from_native_error(cx: *mut JSContext, object: HandleObject) -> Option<ErrorInfo> {
|
unsafe fn from_native_error(object: HandleObject, cx: *mut JSContext) -> Option<ErrorInfo> {
|
||||||
let report = JS_ErrorFromException(cx, object);
|
let report = JS_ErrorFromException(cx, object);
|
||||||
if report.is_null() {
|
if report.is_null() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -209,10 +208,10 @@ impl ErrorInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(ErrorInfo {
|
Some(ErrorInfo {
|
||||||
filename: filename,
|
filename,
|
||||||
message: message,
|
message,
|
||||||
lineno: lineno,
|
lineno,
|
||||||
column: column,
|
column,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,6 +228,37 @@ impl ErrorInfo {
|
||||||
column: 0,
|
column: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn from_object(object: HandleObject, cx: *mut JSContext) -> Option<ErrorInfo> {
|
||||||
|
if let Some(info) = ErrorInfo::from_native_error(object, cx) {
|
||||||
|
return Some(info);
|
||||||
|
}
|
||||||
|
if let Some(info) = ErrorInfo::from_dom_exception(object, cx) {
|
||||||
|
return Some(info);
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn from_value(value: HandleValue, cx: *mut JSContext) -> ErrorInfo {
|
||||||
|
if value.is_object() {
|
||||||
|
rooted!(in(cx) let object = value.to_object());
|
||||||
|
if let Some(info) = ErrorInfo::from_object(object.handle(), cx) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match USVString::from_jsval(cx, value, ()) {
|
||||||
|
Ok(ConversionResult::Success(USVString(string))) => ErrorInfo {
|
||||||
|
message: format!("uncaught exception: {}", string),
|
||||||
|
filename: String::new(),
|
||||||
|
lineno: 0,
|
||||||
|
column: 0,
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("uncaught exception: failed to stringify primitive");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report a pending exception, thereby clearing it.
|
/// Report a pending exception, thereby clearing it.
|
||||||
|
@ -248,29 +278,7 @@ pub unsafe fn report_pending_exception(cx: *mut JSContext, dispatch_event: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ClearPendingException(cx);
|
JS_ClearPendingException(cx);
|
||||||
let error_info = if value.is_object() {
|
let error_info = ErrorInfo::from_value(value.handle(), cx);
|
||||||
rooted!(in(cx) let object = value.to_object());
|
|
||||||
ErrorInfo::from_native_error(cx, object.handle())
|
|
||||||
.or_else(|| ErrorInfo::from_dom_exception(object.handle(), cx))
|
|
||||||
.unwrap_or_else(|| ErrorInfo {
|
|
||||||
message: format!("uncaught exception: unknown (can't convert to string)"),
|
|
||||||
filename: String::new(),
|
|
||||||
lineno: 0,
|
|
||||||
column: 0,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
match USVString::from_jsval(cx, value.handle(), ()) {
|
|
||||||
Ok(ConversionResult::Success(USVString(string))) => ErrorInfo {
|
|
||||||
message: format!("uncaught exception: {}", string),
|
|
||||||
filename: String::new(),
|
|
||||||
lineno: 0,
|
|
||||||
column: 0,
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!("Uncaught exception: failed to stringify primitive");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
error!(
|
error!(
|
||||||
"Error at {}:{}:{} {}",
|
"Error at {}:{}:{} {}",
|
||||||
|
|
|
@ -13746,6 +13746,13 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"global_exception_stringification.html": [
|
||||||
|
"b1c6cb6c390cca290085eea61ba4bf2f34aa4475",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"globals": {
|
"globals": {
|
||||||
"entry.html": [
|
"entry.html": [
|
||||||
"f963385342adbd92e4858a507c88155b4ed4371f",
|
"f963385342adbd92e4858a507c88155b4ed4371f",
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!-- doctype html -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="testOutput"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
setup({ 'allow_uncaught_exception': true });
|
||||||
|
promise_test(async t => {
|
||||||
|
function CustomError(message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
CustomError.prototype = Object.create(Error.prototype);
|
||||||
|
|
||||||
|
let message = null;
|
||||||
|
let waitForError = new Promise(resolve => {
|
||||||
|
window.onerror = (errorMessage) => {
|
||||||
|
message = errorMessage;
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
throw new CustomError("An exceptional exception.");
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
await waitForError;
|
||||||
|
assert_equals(message, "uncaught exception: Error: An exceptional exception.");
|
||||||
|
}, "Exception is stringified properly.");
|
||||||
|
</script>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue