1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
use crate::{JSContext, JSContextImpl, JSValue, JSValueImpl};
use std::fmt;
use std::string::String;
/// Represents a JavaScript error (best-effort) with message and stack trace.
#[derive(Debug, PartialEq, Eq)]
pub struct JSError {
pub message: Option<String>,
pub stack: Option<String>,
}
impl fmt::Display for JSError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
match (&self.message, &self.stack) {
(Some(msg), Some(stack)) => write!(f, "{}\n{}", msg, stack),
(Some(msg), None) => write!(f, "{}", msg),
(None, Some(stack)) => write!(f, "{}", stack),
(None, None) => write!(f, "Unknown JavaScript Error"),
}
}
}
impl std::error::Error for JSError {}
/// Creates `Error` objects as normal JS values.
///
/// This is **not** the exception channel. To enter `catch`/reject, callers must still use
/// `JSExceptionThrower::throw(...)`.
pub trait JSErrorFactory: JSContextImpl {
/// Creates an error object with `name`, `message`, and optional `code`.
///
/// - For built-ins (`Error`, `TypeError`, `RangeError`, ...), implementations should create
/// the correct prototype chain (best-effort) so `instanceof` works.
/// - For non-built-in names (e.g. `AbortError`), implementations should create an `Error`
/// object and set `.name = <name>`.
/// - If `code` is provided, it should be set as a non-enumerable string property.
fn new_error(&self, name: &str, message: impl AsRef<str>, code: Option<&str>) -> Self::Value;
}
impl<C> JSContext<C>
where
C: JSContextImpl + JSErrorFactory,
C::Value: JSValueImpl,
{
/// Creates an `Error` object as a normal return value (does not throw).
pub fn new_error_value(
&self,
name: &str,
message: impl AsRef<str>,
code: Option<&str>,
) -> JSValue<C::Value> {
JSValue::from_raw(self, self.as_ref().new_error(name, message, code))
}
}