rquickjs_core/value/
exception.rs1use std::{error::Error as ErrorTrait, ffi::CStr, fmt};
2
3use crate::{atom::PredefinedAtom, convert::Coerced, qjs, Ctx, Error, Object, Result, Value};
4
5#[repr(transparent)]
9#[derive(Clone, Eq, PartialEq, Hash)]
10pub struct Exception<'js>(pub(crate) Object<'js>);
11
12impl<'js> ErrorTrait for Exception<'js> {}
13
14impl fmt::Debug for Exception<'_> {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 f.debug_struct("Exception")
17 .field("message", &self.message())
18 .field("stack", &self.stack())
19 .finish()
20 }
21}
22
23pub(crate) static ERROR_FORMAT_STR: &CStr =
24 unsafe { CStr::from_bytes_with_nul_unchecked("%s\0".as_bytes()) };
25
26fn truncate_str(mut max: usize, bytes: &[u8]) -> &[u8] {
27 if bytes.len() <= max {
28 return bytes;
29 }
30 while (bytes[max] & 0b1100_0000) == 0b1000_0000 {
32 max -= 1;
33 }
34 &bytes[..max]
35}
36
37impl<'js> Exception<'js> {
38 pub fn into_object(self) -> Object<'js> {
40 self.0
41 }
42
43 pub fn as_object(&self) -> &Object<'js> {
45 &self.0
46 }
47
48 pub fn from_object(obj: Object<'js>) -> Option<Self> {
50 if obj.is_error() {
51 Some(Self(obj))
52 } else {
53 None
54 }
55 }
56
57 pub fn from_message(ctx: Ctx<'js>, message: &str) -> Result<Self> {
59 let obj = unsafe {
60 let value = ctx.handle_exception(qjs::JS_NewError(ctx.as_ptr()))?;
61 Value::from_js_value(ctx, value)
62 .into_object()
63 .expect("`JS_NewError` did not return an object")
64 };
65 obj.set(PredefinedAtom::Message, message)?;
66 Ok(Exception(obj))
67 }
68
69 pub fn message(&self) -> Option<String> {
73 self.get::<_, Option<Coerced<String>>>(PredefinedAtom::Message)
74 .ok()
75 .and_then(|x| x)
76 .map(|x| x.0)
77 }
78
79 pub fn stack(&self) -> Option<String> {
83 self.get::<_, Option<Coerced<String>>>(PredefinedAtom::Stack)
84 .ok()
85 .and_then(|x| x)
86 .map(|x| x.0)
87 }
88
89 pub fn throw_message(ctx: &Ctx<'js>, message: &str) -> Error {
105 let (Ok(e) | Err(e)) = Self::from_message(ctx.clone(), message).map(|x| x.throw());
106 e
107 }
108
109 pub fn throw_syntax(ctx: &Ctx<'js>, message: &str) -> Error {
111 let mut buffer = std::mem::MaybeUninit::<[u8; 256]>::uninit();
115 let str = truncate_str(255, message.as_bytes());
116 unsafe {
117 std::ptr::copy_nonoverlapping(message.as_ptr(), buffer.as_mut_ptr().cast(), str.len());
118 buffer.as_mut_ptr().cast::<u8>().add(str.len()).write(b'\0');
119 let res = qjs::JS_ThrowSyntaxError(
120 ctx.as_ptr(),
121 ERROR_FORMAT_STR.as_ptr(),
122 buffer.as_ptr().cast::<*mut u8>(),
123 );
124 debug_assert_eq!(qjs::JS_VALUE_GET_NORM_TAG(res), qjs::JS_TAG_EXCEPTION);
125 }
126 Error::Exception
127 }
128
129 pub fn throw_type(ctx: &Ctx<'js>, message: &str) -> Error {
131 let mut buffer = std::mem::MaybeUninit::<[u8; 256]>::uninit();
135 let str = truncate_str(255, message.as_bytes());
136 unsafe {
137 std::ptr::copy_nonoverlapping(message.as_ptr(), buffer.as_mut_ptr().cast(), str.len());
138 buffer.as_mut_ptr().cast::<u8>().add(str.len()).write(b'\0');
139 let res = qjs::JS_ThrowTypeError(
140 ctx.as_ptr(),
141 ERROR_FORMAT_STR.as_ptr(),
142 buffer.as_ptr().cast::<*mut u8>(),
143 );
144 debug_assert_eq!(qjs::JS_VALUE_GET_NORM_TAG(res), qjs::JS_TAG_EXCEPTION);
145 }
146 Error::Exception
147 }
148
149 pub fn throw_reference(ctx: &Ctx<'js>, message: &str) -> Error {
151 let mut buffer = std::mem::MaybeUninit::<[u8; 256]>::uninit();
155 let str = truncate_str(255, message.as_bytes());
156 unsafe {
157 std::ptr::copy_nonoverlapping(message.as_ptr(), buffer.as_mut_ptr().cast(), str.len());
158 buffer.as_mut_ptr().cast::<u8>().add(str.len()).write(b'\0');
159 let res = qjs::JS_ThrowReferenceError(
160 ctx.as_ptr(),
161 ERROR_FORMAT_STR.as_ptr(),
162 buffer.as_ptr().cast::<*mut u8>(),
163 );
164 debug_assert_eq!(qjs::JS_VALUE_GET_NORM_TAG(res), qjs::JS_TAG_EXCEPTION);
165 }
166 Error::Exception
167 }
168
169 pub fn throw_range(ctx: &Ctx<'js>, message: &str) -> Error {
171 let mut buffer = std::mem::MaybeUninit::<[u8; 256]>::uninit();
175 let str = truncate_str(255, message.as_bytes());
176 unsafe {
177 std::ptr::copy_nonoverlapping(message.as_ptr(), buffer.as_mut_ptr().cast(), str.len());
178 buffer.as_mut_ptr().cast::<u8>().add(str.len()).write(b'\0');
179 let res = qjs::JS_ThrowRangeError(
180 ctx.as_ptr(),
181 ERROR_FORMAT_STR.as_ptr(),
182 buffer.as_ptr().cast::<*mut u8>(),
183 );
184 debug_assert_eq!(qjs::JS_VALUE_GET_NORM_TAG(res), qjs::JS_TAG_EXCEPTION);
185 }
186 Error::Exception
187 }
188
189 pub fn throw_internal(ctx: &Ctx<'js>, message: &str) -> Error {
191 let mut buffer = std::mem::MaybeUninit::<[u8; 256]>::uninit();
195 let str = truncate_str(255, message.as_bytes());
196 unsafe {
197 std::ptr::copy_nonoverlapping(message.as_ptr(), buffer.as_mut_ptr().cast(), str.len());
198 buffer.as_mut_ptr().cast::<u8>().add(str.len()).write(b'\0');
199 let res = qjs::JS_ThrowInternalError(
200 ctx.as_ptr(),
201 ERROR_FORMAT_STR.as_ptr(),
202 buffer.as_ptr().cast::<*mut u8>(),
203 );
204 debug_assert_eq!(qjs::JS_VALUE_GET_NORM_TAG(res), qjs::JS_TAG_EXCEPTION);
205 }
206 Error::Exception
207 }
208
209 pub fn throw(self) -> Error {
211 let ctx = self.ctx().clone();
212 ctx.throw(self.0.into_value())
213 }
214}
215
216impl fmt::Display for Exception<'_> {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 "Error:".fmt(f)?;
219 if let Some(message) = self.message() {
220 ' '.fmt(f)?;
221 message.fmt(f)?;
222 }
223 if let Some(stack) = self.stack() {
224 '\n'.fmt(f)?;
225 stack.fmt(f)?;
226 }
227 Ok(())
228 }
229}