fix(napi): propagation error in function call (#1315)
This commit is contained in:
parent
5ba70b0e1a
commit
ea18170779
9 changed files with 79 additions and 8 deletions
|
@ -343,7 +343,8 @@ impl NapiFn {
|
|||
|
||||
let mut ret_ptr = std::ptr::null_mut();
|
||||
|
||||
napi::bindgen_prelude::check_status!(
|
||||
napi::bindgen_prelude::check_pending_exception!(
|
||||
env,
|
||||
napi::bindgen_prelude::sys::napi_call_function(
|
||||
env,
|
||||
cb.this(),
|
||||
|
@ -351,8 +352,7 @@ impl NapiFn {
|
|||
args.len(),
|
||||
args.as_ptr(),
|
||||
&mut ret_ptr
|
||||
),
|
||||
"Failed to call napi callback",
|
||||
)
|
||||
)?;
|
||||
|
||||
#ret
|
||||
|
|
|
@ -148,6 +148,10 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("Buffer", "Buffer"),
|
||||
("Vec", "Array<{}>"),
|
||||
("Result", "Error | {}"),
|
||||
("Error", "Error"),
|
||||
("JsError", "Error"),
|
||||
("JsTypeError", "TypeError"),
|
||||
("JsRangeError", "RangeError"),
|
||||
("ClassInstance", "{}"),
|
||||
("Either", "{} | {}"),
|
||||
("Either3", "{} | {} | {}"),
|
||||
|
|
|
@ -12,6 +12,7 @@ use serde::{de, ser};
|
|||
#[cfg(feature = "serde-json")]
|
||||
use serde_json::Error as SerdeJSONError;
|
||||
|
||||
use crate::bindgen_runtime::ToNapiValue;
|
||||
use crate::{check_status, sys, Env, JsUnknown, NapiValue, Status};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
@ -28,6 +29,21 @@ pub struct Error {
|
|||
maybe_env: sys::napi_env,
|
||||
}
|
||||
|
||||
impl ToNapiValue for Error {
|
||||
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
|
||||
if val.maybe_raw.is_null() {
|
||||
let err = unsafe { JsError::from(val).into_value(env) };
|
||||
Ok(err)
|
||||
} else {
|
||||
let mut value = std::ptr::null_mut();
|
||||
check_status!(unsafe {
|
||||
sys::napi_get_reference_value(val.maybe_env, val.maybe_raw, &mut value)
|
||||
})?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Error {}
|
||||
unsafe impl Sync for Error {}
|
||||
|
||||
|
@ -294,6 +310,12 @@ macro_rules! impl_object_methods {
|
|||
Self(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::bindgen_prelude::ToNapiValue for $js_value {
|
||||
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
|
||||
unsafe { ToNapiValue::to_napi_value(env, val.0) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -334,7 +356,8 @@ macro_rules! check_status {
|
|||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! check_pending_exception {
|
||||
($env: expr, $code:expr) => {{
|
||||
($env:expr, $code:expr) => {{
|
||||
use $crate::NapiValue;
|
||||
let c = $code;
|
||||
match c {
|
||||
$crate::sys::Status::napi_ok => Ok(()),
|
||||
|
@ -344,11 +367,30 @@ macro_rules! check_pending_exception {
|
|||
unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
|
||||
$crate::sys::Status::napi_ok
|
||||
);
|
||||
return Err(Error::from(unsafe {
|
||||
JsUnknown::from_raw_unchecked($env, error_result)
|
||||
return Err($crate::Error::from(unsafe {
|
||||
$crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
|
||||
}));
|
||||
}
|
||||
_ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
|
||||
}
|
||||
}};
|
||||
|
||||
($env:expr, $code:expr, $($msg:tt)*) => {{
|
||||
use $crate::NapiValue;
|
||||
let c = $code;
|
||||
match c {
|
||||
$crate::sys::Status::napi_ok => Ok(()),
|
||||
$crate::sys::Status::napi_pending_exception => {
|
||||
let mut error_result = std::ptr::null_mut();
|
||||
assert_eq!(
|
||||
unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
|
||||
$crate::sys::Status::napi_ok
|
||||
);
|
||||
return Err($crate::Error::from(unsafe {
|
||||
$crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
|
||||
}));
|
||||
}
|
||||
_ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -160,8 +160,9 @@ pub mod bindgen_prelude {
|
|||
#[cfg(feature = "tokio_rt")]
|
||||
pub use crate::tokio_runtime::*;
|
||||
pub use crate::{
|
||||
assert_type_of, bindgen_runtime::*, check_status, check_status_or_throw, error, error::*, sys,
|
||||
type_of, JsError, Property, PropertyAttributes, Result, Status, Task, ValueType,
|
||||
assert_type_of, bindgen_runtime::*, check_pending_exception, check_status,
|
||||
check_status_or_throw, error, error::*, sys, type_of, JsError, Property, PropertyAttributes,
|
||||
Result, Status, Task, ValueType,
|
||||
};
|
||||
|
||||
// This function's signature must be kept in sync with the one in tokio_runtime.rs, otherwise napi
|
||||
|
|
|
@ -43,6 +43,7 @@ Generated by [AVA](https://avajs.dev).
|
|||
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void␊
|
||||
export function returnJsFunction(): (...args: any[]) => any␊
|
||||
export function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>␊
|
||||
export function captureErrorInCallback(cb1: () => void, cb2: (arg0: Error) => void): void␊
|
||||
export interface ObjectFieldClassInstance {␊
|
||||
bird: Bird␊
|
||||
}␊
|
||||
|
|
Binary file not shown.
|
@ -112,6 +112,7 @@ import {
|
|||
CustomFinalize,
|
||||
plusOne,
|
||||
Width,
|
||||
captureErrorInCallback,
|
||||
} from '../'
|
||||
|
||||
test('export const', (t) => {
|
||||
|
@ -290,6 +291,15 @@ test('callback', (t) => {
|
|||
t.is(err, undefined)
|
||||
t.is(content, 'hello world')
|
||||
})
|
||||
|
||||
captureErrorInCallback(
|
||||
() => {
|
||||
throw new Error('Testing')
|
||||
},
|
||||
(err) => {
|
||||
t.is((err as Error).message, 'Testing')
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
test('return function', (t) => {
|
||||
|
|
1
examples/napi/index.d.ts
vendored
1
examples/napi/index.d.ts
vendored
|
@ -33,6 +33,7 @@ export function optionOnly(callback: (arg0?: string | undefined | null) => void)
|
|||
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void
|
||||
export function returnJsFunction(): (...args: any[]) => any
|
||||
export function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>
|
||||
export function captureErrorInCallback(cb1: () => void, cb2: (arg0: Error) => void): void
|
||||
export interface ObjectFieldClassInstance {
|
||||
bird: Bird
|
||||
}
|
||||
|
|
|
@ -80,3 +80,15 @@ fn callback_return_promise<T: Fn() -> Result<JsUnknown>>(
|
|||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn capture_error_in_callback<C: Fn() -> Result<()>, E: Fn(Error) -> Result<()>>(
|
||||
cb1: C,
|
||||
cb2: E,
|
||||
) -> Result<()> {
|
||||
if let Err(e) = cb1() {
|
||||
cb2(e)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue