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();
|
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(
|
napi::bindgen_prelude::sys::napi_call_function(
|
||||||
env,
|
env,
|
||||||
cb.this(),
|
cb.this(),
|
||||||
|
@ -351,8 +352,7 @@ impl NapiFn {
|
||||||
args.len(),
|
args.len(),
|
||||||
args.as_ptr(),
|
args.as_ptr(),
|
||||||
&mut ret_ptr
|
&mut ret_ptr
|
||||||
),
|
)
|
||||||
"Failed to call napi callback",
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
#ret
|
#ret
|
||||||
|
|
|
@ -148,6 +148,10 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||||
("Buffer", "Buffer"),
|
("Buffer", "Buffer"),
|
||||||
("Vec", "Array<{}>"),
|
("Vec", "Array<{}>"),
|
||||||
("Result", "Error | {}"),
|
("Result", "Error | {}"),
|
||||||
|
("Error", "Error"),
|
||||||
|
("JsError", "Error"),
|
||||||
|
("JsTypeError", "TypeError"),
|
||||||
|
("JsRangeError", "RangeError"),
|
||||||
("ClassInstance", "{}"),
|
("ClassInstance", "{}"),
|
||||||
("Either", "{} | {}"),
|
("Either", "{} | {}"),
|
||||||
("Either3", "{} | {} | {}"),
|
("Either3", "{} | {} | {}"),
|
||||||
|
|
|
@ -12,6 +12,7 @@ use serde::{de, ser};
|
||||||
#[cfg(feature = "serde-json")]
|
#[cfg(feature = "serde-json")]
|
||||||
use serde_json::Error as SerdeJSONError;
|
use serde_json::Error as SerdeJSONError;
|
||||||
|
|
||||||
|
use crate::bindgen_runtime::ToNapiValue;
|
||||||
use crate::{check_status, sys, Env, JsUnknown, NapiValue, Status};
|
use crate::{check_status, sys, Env, JsUnknown, NapiValue, Status};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -28,6 +29,21 @@ pub struct Error {
|
||||||
maybe_env: sys::napi_env,
|
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 Send for Error {}
|
||||||
unsafe impl Sync for Error {}
|
unsafe impl Sync for Error {}
|
||||||
|
|
||||||
|
@ -294,6 +310,12 @@ macro_rules! impl_object_methods {
|
||||||
Self(err)
|
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)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! check_pending_exception {
|
macro_rules! check_pending_exception {
|
||||||
($env: expr, $code:expr) => {{
|
($env:expr, $code:expr) => {{
|
||||||
|
use $crate::NapiValue;
|
||||||
let c = $code;
|
let c = $code;
|
||||||
match c {
|
match c {
|
||||||
$crate::sys::Status::napi_ok => Ok(()),
|
$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) },
|
unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
|
||||||
$crate::sys::Status::napi_ok
|
$crate::sys::Status::napi_ok
|
||||||
);
|
);
|
||||||
return Err(Error::from(unsafe {
|
return Err($crate::Error::from(unsafe {
|
||||||
JsUnknown::from_raw_unchecked($env, error_result)
|
$crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
_ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
|
_ => 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")]
|
#[cfg(feature = "tokio_rt")]
|
||||||
pub use crate::tokio_runtime::*;
|
pub use crate::tokio_runtime::*;
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
assert_type_of, bindgen_runtime::*, check_status, check_status_or_throw, error, error::*, sys,
|
assert_type_of, bindgen_runtime::*, check_pending_exception, check_status,
|
||||||
type_of, JsError, Property, PropertyAttributes, Result, Status, Task, ValueType,
|
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
|
// 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 readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void␊
|
||||||
export function returnJsFunction(): (...args: any[]) => any␊
|
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 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 {␊
|
export interface ObjectFieldClassInstance {␊
|
||||||
bird: Bird␊
|
bird: Bird␊
|
||||||
}␊
|
}␊
|
||||||
|
|
Binary file not shown.
|
@ -112,6 +112,7 @@ import {
|
||||||
CustomFinalize,
|
CustomFinalize,
|
||||||
plusOne,
|
plusOne,
|
||||||
Width,
|
Width,
|
||||||
|
captureErrorInCallback,
|
||||||
} from '../'
|
} from '../'
|
||||||
|
|
||||||
test('export const', (t) => {
|
test('export const', (t) => {
|
||||||
|
@ -290,6 +291,15 @@ test('callback', (t) => {
|
||||||
t.is(err, undefined)
|
t.is(err, undefined)
|
||||||
t.is(content, 'hello world')
|
t.is(content, 'hello world')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
captureErrorInCallback(
|
||||||
|
() => {
|
||||||
|
throw new Error('Testing')
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
t.is((err as Error).message, 'Testing')
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('return function', (t) => {
|
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 readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void
|
||||||
export function returnJsFunction(): (...args: any[]) => any
|
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 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 {
|
export interface ObjectFieldClassInstance {
|
||||||
bird: Bird
|
bird: Bird
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,3 +80,15 @@ fn callback_return_promise<T: Fn() -> Result<JsUnknown>>(
|
||||||
Ok(ret)
|
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