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