napi/
error.rs

1use std::convert::{From, TryFrom};
2use std::error;
3use std::ffi::CString;
4use std::fmt;
5#[cfg(feature = "serde-json")]
6use std::fmt::Display;
7use std::os::raw::{c_char, c_void};
8use std::ptr;
9
10#[cfg(feature = "serde-json")]
11use serde::{de, ser};
12#[cfg(feature = "serde-json")]
13use serde_json::Error as SerdeJSONError;
14
15use crate::{bindgen_runtime::ToNapiValue, check_status, sys, Env, JsValue, Status, Unknown};
16
17pub type Result<T, S = Status> = std::result::Result<T, Error<S>>;
18
19/// Represent `JsError`.
20/// Return this Error in `js_function`, **napi-rs** will throw it as `JsError` for you.
21/// If you want throw it as `TypeError` or `RangeError`, you can use `JsTypeError/JsRangeError::from(Error).throw_into(env)`
22pub struct Error<S: AsRef<str> = Status> {
23  pub status: S,
24  pub reason: String,
25  // Convert raw `JsError` into Error
26  pub(crate) maybe_raw: sys::napi_ref,
27  pub(crate) maybe_env: sys::napi_env,
28}
29
30impl<S: AsRef<str>> Drop for Error<S> {
31  fn drop(&mut self) {
32    // @TODO: deal with Error created with reference and leave it to drop in `async fn`
33    if !self.maybe_raw.is_null() {
34      let mut ref_count = 0;
35      let status =
36        unsafe { sys::napi_reference_unref(self.maybe_env, self.maybe_raw, &mut ref_count) };
37      if status != sys::Status::napi_ok {
38        eprintln!("unref error reference failed: {}", Status::from(status));
39      }
40      if ref_count == 0 {
41        let status = unsafe { sys::napi_delete_reference(self.maybe_env, self.maybe_raw) };
42        if status != sys::Status::napi_ok {
43          eprintln!("delete error reference failed: {}", Status::from(status));
44        }
45      }
46    }
47  }
48}
49
50impl<S: AsRef<str>> std::fmt::Debug for Error<S> {
51  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52    write!(
53      f,
54      "Error {{ status: {:?}, reason: {:?} }}",
55      self.status.as_ref(),
56      self.reason
57    )
58  }
59}
60
61impl<S: AsRef<str>> ToNapiValue for Error<S> {
62  unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
63    if val.maybe_raw.is_null() {
64      let err = unsafe { JsError::from(val).into_value(env) };
65      Ok(err)
66    } else {
67      let mut value = std::ptr::null_mut();
68      check_status!(
69        unsafe { sys::napi_get_reference_value(env, val.maybe_raw, &mut value) },
70        "Get error reference in `to_napi_value` failed"
71      )?;
72      let mut ref_count = 0;
73      check_status!(
74        unsafe { sys::napi_reference_unref(env, val.maybe_raw, &mut ref_count) },
75        "Unref error reference in `to_napi_value` failed"
76      )?;
77      if ref_count == 0 {
78        check_status!(
79          unsafe { sys::napi_delete_reference(env, val.maybe_raw) },
80          "Delete error reference in `to_napi_value` failed"
81        )?;
82      }
83      // already unref, skip the logic in `Drop`
84      val.maybe_raw = ptr::null_mut();
85      val.maybe_env = ptr::null_mut();
86      Ok(value)
87    }
88  }
89}
90
91unsafe impl<S> Send for Error<S> where S: Send + AsRef<str> {}
92unsafe impl<S> Sync for Error<S> where S: Sync + AsRef<str> {}
93
94impl<S: AsRef<str> + std::fmt::Debug> error::Error for Error<S> {}
95
96impl<S: AsRef<str>> From<std::convert::Infallible> for Error<S> {
97  fn from(_: std::convert::Infallible) -> Self {
98    unreachable!()
99  }
100}
101
102#[cfg(feature = "serde-json")]
103impl ser::Error for Error {
104  fn custom<T: Display>(msg: T) -> Self {
105    Error::new(Status::InvalidArg, msg.to_string())
106  }
107}
108
109#[cfg(feature = "serde-json")]
110impl de::Error for Error {
111  fn custom<T: Display>(msg: T) -> Self {
112    Error::new(Status::InvalidArg, msg.to_string())
113  }
114}
115
116#[cfg(feature = "serde-json")]
117impl From<SerdeJSONError> for Error {
118  fn from(value: SerdeJSONError) -> Self {
119    Error::new(Status::InvalidArg, format!("{value}"))
120  }
121}
122
123impl From<Unknown<'_>> for Error {
124  fn from(value: Unknown) -> Self {
125    let mut result = std::ptr::null_mut();
126    let status = unsafe { sys::napi_create_reference(value.0.env, value.0.value, 1, &mut result) };
127    if status != sys::Status::napi_ok {
128      return Error::new(
129        Status::from(status),
130        "Create Error reference failed".to_owned(),
131      );
132    }
133    let maybe_env = value.0.env;
134    let maybe_error_message = value
135      .coerce_to_string()
136      .and_then(|a| a.into_utf8().and_then(|a| a.into_owned()));
137    if let Ok(error_message) = maybe_error_message {
138      return Self {
139        status: Status::GenericFailure,
140        reason: error_message,
141        maybe_raw: result,
142        maybe_env,
143      };
144    }
145
146    Self {
147      status: Status::GenericFailure,
148      reason: "".to_string(),
149      maybe_raw: result,
150      maybe_env,
151    }
152  }
153}
154
155#[cfg(feature = "anyhow")]
156impl From<anyhow::Error> for Error {
157  fn from(value: anyhow::Error) -> Self {
158    Error::new(Status::GenericFailure, format!("{:?}", value))
159  }
160}
161
162impl<S: AsRef<str> + std::fmt::Debug> fmt::Display for Error<S> {
163  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164    if !self.reason.is_empty() {
165      write!(f, "{:?}, {}", self.status, self.reason)
166    } else {
167      write!(f, "{:?}", self.status)
168    }
169  }
170}
171
172impl<S: AsRef<str>> Error<S> {
173  pub fn new<R: ToString>(status: S, reason: R) -> Self {
174    Error {
175      status,
176      reason: reason.to_string(),
177      maybe_raw: ptr::null_mut(),
178      maybe_env: ptr::null_mut(),
179    }
180  }
181
182  pub fn from_status(status: S) -> Self {
183    Error {
184      status,
185      reason: "".to_owned(),
186      maybe_raw: ptr::null_mut(),
187      maybe_env: ptr::null_mut(),
188    }
189  }
190}
191
192impl<S: AsRef<str> + Clone> Error<S> {
193  pub fn try_clone(&self) -> Result<Self> {
194    if !self.maybe_raw.is_null() {
195      check_status!(
196        unsafe { sys::napi_reference_ref(self.maybe_env, self.maybe_raw, &mut 0) },
197        "Failed to increase error reference count"
198      )?;
199    }
200    Ok(Self {
201      status: self.status.clone(),
202      reason: self.reason.to_string(),
203      maybe_raw: self.maybe_raw,
204      maybe_env: self.maybe_env,
205    })
206  }
207}
208
209impl Error {
210  pub fn from_reason<T: Into<String>>(reason: T) -> Self {
211    Error {
212      status: Status::GenericFailure,
213      reason: reason.into(),
214      maybe_raw: ptr::null_mut(),
215      maybe_env: ptr::null_mut(),
216    }
217  }
218}
219
220impl From<std::ffi::NulError> for Error {
221  fn from(error: std::ffi::NulError) -> Self {
222    Error {
223      status: Status::GenericFailure,
224      reason: format!("{error}"),
225      maybe_raw: ptr::null_mut(),
226      maybe_env: ptr::null_mut(),
227    }
228  }
229}
230
231impl From<std::io::Error> for Error {
232  fn from(error: std::io::Error) -> Self {
233    Error {
234      status: Status::GenericFailure,
235      reason: format!("{error}"),
236      maybe_raw: ptr::null_mut(),
237      maybe_env: ptr::null_mut(),
238    }
239  }
240}
241
242#[derive(Clone, Debug)]
243pub struct ExtendedErrorInfo {
244  pub message: String,
245  pub engine_reserved: *mut c_void,
246  pub engine_error_code: u32,
247  pub error_code: Status,
248}
249
250impl TryFrom<sys::napi_extended_error_info> for ExtendedErrorInfo {
251  type Error = Error;
252
253  fn try_from(value: sys::napi_extended_error_info) -> Result<Self> {
254    Ok(Self {
255      message: unsafe {
256        CString::from_raw(value.error_message as *mut c_char)
257          .into_string()
258          .map_err(|e| Error::new(Status::GenericFailure, format!("{e}")))?
259      },
260      engine_error_code: value.engine_error_code,
261      engine_reserved: value.engine_reserved,
262      error_code: Status::from(value.error_code),
263    })
264  }
265}
266
267pub struct JsError<S: AsRef<str> = Status>(Error<S>);
268
269#[cfg(feature = "anyhow")]
270impl From<anyhow::Error> for JsError {
271  fn from(value: anyhow::Error) -> Self {
272    JsError(Error::new(Status::GenericFailure, value.to_string()))
273  }
274}
275
276pub struct JsTypeError<S: AsRef<str> = Status>(Error<S>);
277
278pub struct JsRangeError<S: AsRef<str> = Status>(Error<S>);
279
280#[cfg(feature = "napi9")]
281pub struct JsSyntaxError<S: AsRef<str> = Status>(Error<S>);
282
283pub(crate) fn get_error_message_and_stack_trace(
284  env: sys::napi_env,
285  err: sys::napi_value,
286) -> Result<String> {
287  use crate::bindgen_runtime::FromNapiValue;
288
289  let mut error_string = ptr::null_mut();
290  check_status!(
291    unsafe { sys::napi_coerce_to_string(env, err, &mut error_string) },
292    "Get error message failed"
293  )?;
294  let mut result = unsafe { String::from_napi_value(env, error_string) }?;
295
296  let mut stack_trace = ptr::null_mut();
297  check_status!(
298    unsafe { sys::napi_get_named_property(env, err, c"stack".as_ptr().cast(), &mut stack_trace) },
299    "Get stack trace failed"
300  )?;
301  let mut stack_type = -1;
302  check_status!(
303    unsafe { sys::napi_typeof(env, stack_trace, &mut stack_type) },
304    "Get stack trace type failed"
305  )?;
306  if stack_type == sys::ValueType::napi_string {
307    let stack_trace = unsafe { String::from_napi_value(env, stack_trace) }?;
308    result.push('\n');
309    result.push_str(&stack_trace);
310  }
311
312  Ok(result)
313}
314
315macro_rules! impl_object_methods {
316  ($js_value:ident, $kind:expr) => {
317    impl<S: AsRef<str>> $js_value<S> {
318      /// # Safety
319      ///
320      /// This function is safety if env is not null ptr.
321      pub unsafe fn into_value(mut self, env: sys::napi_env) -> sys::napi_value {
322        if !self.0.maybe_raw.is_null() {
323          let mut err = ptr::null_mut();
324          let get_err_status =
325            unsafe { sys::napi_get_reference_value(env, self.0.maybe_raw, &mut err) };
326          debug_assert!(
327            get_err_status == sys::Status::napi_ok,
328            "Get Error from Reference failed"
329          );
330          let mut ref_count = 0;
331          let unref_status =
332            unsafe { sys::napi_reference_unref(env, self.0.maybe_raw, &mut ref_count) };
333          debug_assert!(
334            unref_status == sys::Status::napi_ok,
335            "Unref Error Reference failed"
336          );
337          if ref_count == 0 {
338            let delete_err_status = unsafe { sys::napi_delete_reference(env, self.0.maybe_raw) };
339            debug_assert!(
340              delete_err_status == sys::Status::napi_ok,
341              "Delete Error Reference failed"
342            );
343          }
344          // already unref, skip the logic in `Drop`
345          self.0.maybe_raw = ptr::null_mut();
346          self.0.maybe_env = ptr::null_mut();
347          let mut is_error = false;
348          let is_error_status = unsafe { sys::napi_is_error(env, err, &mut is_error) };
349          debug_assert!(
350            is_error_status == sys::Status::napi_ok,
351            "Check Error failed"
352          );
353          // make sure ref_value is a valid error at first and avoid throw error failed.
354          if is_error {
355            return err;
356          }
357        }
358
359        let error_status = self.0.status.as_ref();
360        let status_len = error_status.len();
361        let reason_len = self.0.reason.len();
362        let mut error_code = ptr::null_mut();
363        let mut reason_string = ptr::null_mut();
364        let mut js_error = ptr::null_mut();
365        let create_code_status = unsafe {
366          sys::napi_create_string_utf8(
367            env,
368            error_status.as_ptr().cast(),
369            status_len as isize,
370            &mut error_code,
371          )
372        };
373        debug_assert!(create_code_status == sys::Status::napi_ok);
374        let create_reason_status = unsafe {
375          sys::napi_create_string_utf8(
376            env,
377            self.0.reason.as_ptr().cast(),
378            reason_len as isize,
379            &mut reason_string,
380          )
381        };
382        debug_assert!(create_reason_status == sys::Status::napi_ok);
383        let create_error_status = unsafe { $kind(env, error_code, reason_string, &mut js_error) };
384        debug_assert!(create_error_status == sys::Status::napi_ok);
385        js_error
386      }
387
388      pub fn into_unknown<'env>(self, env: Env) -> Unknown<'env> {
389        let value = unsafe { self.into_value(env.raw()) };
390        unsafe { Unknown::from_raw_unchecked(env.raw(), value) }
391      }
392
393      /// # Safety
394      ///
395      /// This function is safety if env is not null ptr.
396      pub unsafe fn throw_into(self, env: sys::napi_env) {
397        #[cfg(debug_assertions)]
398        let reason = self.0.reason.clone();
399        let status = self.0.status.as_ref().to_string();
400        // just sure current error is pending_exception
401        if status == Status::PendingException.as_ref() {
402          return;
403        }
404        // make sure current env is not exception_pending status
405        let mut is_pending_exception = false;
406        assert_eq!(
407          unsafe { $crate::sys::napi_is_exception_pending(env, &mut is_pending_exception) },
408          $crate::sys::Status::napi_ok,
409          "Check exception status failed"
410        );
411        let js_error = match is_pending_exception {
412          true => {
413            let mut error_result = std::ptr::null_mut();
414            assert_eq!(
415              unsafe { $crate::sys::napi_get_and_clear_last_exception(env, &mut error_result) },
416              $crate::sys::Status::napi_ok,
417              "Get and clear last exception failed"
418            );
419            error_result
420          }
421          false => unsafe { self.into_value(env) },
422        };
423        #[cfg(debug_assertions)]
424        let throw_status = unsafe { sys::napi_throw(env, js_error) };
425        unsafe { sys::napi_throw(env, js_error) };
426        #[cfg(debug_assertions)]
427        assert!(
428          throw_status == sys::Status::napi_ok,
429          "Throw error failed, status: [{}], raw message: \"{}\", raw status: [{}]",
430          Status::from(throw_status),
431          reason,
432          status
433        );
434      }
435    }
436
437    impl<S: AsRef<str>> From<Error<S>> for $js_value<S> {
438      fn from(err: Error<S>) -> Self {
439        Self(err)
440      }
441    }
442
443    impl crate::bindgen_prelude::ToNapiValue for $js_value {
444      unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
445        unsafe { ToNapiValue::to_napi_value(env, val.0) }
446      }
447    }
448  };
449}
450
451impl_object_methods!(JsError, sys::napi_create_error);
452impl_object_methods!(JsTypeError, sys::napi_create_type_error);
453impl_object_methods!(JsRangeError, sys::napi_create_range_error);
454#[cfg(feature = "napi9")]
455impl_object_methods!(JsSyntaxError, sys::node_api_create_syntax_error);
456
457#[doc(hidden)]
458#[macro_export]
459macro_rules! error {
460  ($status:expr, $($msg:tt)*) => {
461    $crate::Error::new($status, format!($($msg)*))
462  };
463}
464
465#[doc(hidden)]
466#[macro_export]
467macro_rules! check_status {
468  ($code:expr) => {{
469    let c = $code;
470    match c {
471      $crate::sys::Status::napi_ok => Ok(()),
472      _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
473    }
474  }};
475
476  ($code:expr, $($msg:tt)*) => {{
477    let c = $code;
478    match c {
479      $crate::sys::Status::napi_ok => Ok(()),
480      _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
481    }
482  }};
483
484  ($code:expr, $msg:expr, $env:expr, $val:expr) => {{
485    let c = $code;
486    match c {
487      $crate::sys::Status::napi_ok => Ok(()),
488      _ => Err($crate::Error::new($crate::Status::from(c), format!($msg, $crate::type_of!($env, $val)?))),
489    }
490  }};
491}
492
493#[doc(hidden)]
494#[macro_export]
495macro_rules! check_status_and_type {
496  ($code:expr, $env:ident, $val:ident, $msg:expr) => {{
497    let c = $code;
498    match c {
499      $crate::sys::Status::napi_ok => Ok(()),
500      _ => {
501        use $crate::js_values::JsValue;
502        let value_type = $crate::type_of!($env, $val)?;
503        let error_msg = match value_type {
504          ValueType::Function => {
505            let function_name = unsafe {
506              $crate::bindgen_prelude::Function::<
507                $crate::bindgen_prelude::Unknown,
508                $crate::bindgen_prelude::Unknown,
509              >::from_napi_value($env, $val)?
510              .name()?
511            };
512            format!(
513              $msg,
514              format!(
515                "function {}(..) ",
516                if function_name.len() == 0 {
517                  "anonymous".to_owned()
518                } else {
519                  function_name
520                }
521              )
522            )
523          }
524          ValueType::Object => {
525            let env_ = $crate::Env::from($env);
526            let json: $crate::JSON = env_.get_global()?.get_named_property_unchecked("JSON")?;
527            let object = json.stringify($crate::bindgen_prelude::Object::from_raw($env, $val))?;
528            format!($msg, format!("Object {}", object))
529          }
530          ValueType::Boolean | ValueType::Number => {
531            let val = $crate::Unknown::from_raw_unchecked($env, $val);
532            let value = val.coerce_to_string()?.into_utf8()?;
533            format!($msg, format!("{} {} ", value_type, value.as_str()?))
534          }
535          #[cfg(feature = "napi6")]
536          ValueType::BigInt => {
537            let val = $crate::Unknown::from_raw_unchecked($env, $val);
538            let value = val.coerce_to_string()?.into_utf8()?;
539            format!($msg, format!("{} {} ", value_type, value.as_str()?))
540          }
541          _ => format!($msg, value_type),
542        };
543        Err($crate::Error::new($crate::Status::from(c), error_msg))
544      }
545    }
546  }};
547}
548
549#[doc(hidden)]
550#[macro_export]
551macro_rules! check_pending_exception {
552  ($env:expr, $code:expr) => {{
553    let c = $code;
554    match c {
555      $crate::sys::Status::napi_ok => Ok(()),
556      $crate::sys::Status::napi_pending_exception => {
557        let mut error_result = std::ptr::null_mut();
558        assert_eq!(
559          unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
560          $crate::sys::Status::napi_ok
561        );
562        return Err($crate::Error::from(unsafe {
563          $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
564        }));
565      }
566      _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
567    }
568  }};
569
570  ($env:expr, $code:expr, $($msg:tt)*) => {{
571    let c = $code;
572    match c {
573      $crate::sys::Status::napi_ok => Ok(()),
574      $crate::sys::Status::napi_pending_exception => {
575        let mut error_result = std::ptr::null_mut();
576        assert_eq!(
577          unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
578          $crate::sys::Status::napi_ok
579        );
580        return Err($crate::Error::from(unsafe {
581          $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
582        }));
583      }
584      _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
585    }
586  }};
587}