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