Merge pull request #104 from messense/feature/catch-unwind

Throw exception when Rust code panics
This commit is contained in:
LongYinan 2020-07-19 21:44:39 +08:00 committed by GitHub
commit 5e0ec94db1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 7 deletions

View file

@ -4,9 +4,9 @@
```rust ```rust
#[macro_use] #[macro_use]
extern crate napi_rs as napi; extern crate napi;
#[macro_use] #[macro_use]
extern crate napi_rs_derive; extern crate napi_derive;
use napi::{CallContext, Error, JsNumber, JsUnknown, Module, Result, Status}; use napi::{CallContext, Error, JsNumber, JsUnknown, Module, Result, Status};
use std::convert::TryInto; use std::convert::TryInto;

View file

@ -89,8 +89,9 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream {
use std::mem; use std::mem;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::ptr; use std::ptr;
use std::panic::{self, AssertUnwindSafe};
use std::ffi::CString; use std::ffi::CString;
use napi::{JsUnknown, Env, Status, NapiValue, CallContext}; use napi::{JsUnknown, Env, Error, Status, NapiValue, CallContext};
let mut argc = #arg_len_span as usize; let mut argc = #arg_len_span as usize;
let mut raw_args = let mut raw_args =
unsafe { mem::MaybeUninit::<[napi::sys::napi_value; #arg_len_span as usize]>::uninit().assume_init() }; unsafe { mem::MaybeUninit::<[napi::sys::napi_value; #arg_len_span as usize]>::uninit().assume_init() };
@ -112,7 +113,23 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream {
let mut env = Env::from_raw(raw_env); let mut env = Env::from_raw(raw_env);
let call_ctx = CallContext::new(&mut env, raw_this, &raw_args, #arg_len_span, argc as usize); let call_ctx = CallContext::new(&mut env, raw_this, &raw_args, #arg_len_span, argc as usize);
let result = call_ctx.and_then(|ctx| #new_fn_name(ctx)); let result = call_ctx.and_then(|ctx| {
match panic::catch_unwind(AssertUnwindSafe(move || #new_fn_name(ctx))) {
Ok(result) => result,
Err(e) => {
let message = {
if let Some(string) = e.downcast_ref::<String>() {
string.clone()
} else if let Some(string) = e.downcast_ref::<&str>() {
string.to_string()
} else {
format!("panic from Rust code: {:?}", e)
}
};
Err(Error { status: Status::GenericFailure, reason: message })
}
}
});
has_error = has_error && result.is_err(); has_error = has_error && result.is_err();
match result { match result {

View file

@ -20,10 +20,10 @@ pub trait ToJs: Copy + Clone {
/// ///
/// ``` /// ```
/// #[macro_use] /// #[macro_use]
/// extern crate napi_rs_derive; /// extern crate napi_derive;
/// ///
/// use std::thread; /// use std::thread;
/// use napi_rs::{ /// use napi::{
/// Number, Result, Env, CallContext, JsUndefined, JsFunction, /// Number, Result, Env, CallContext, JsUndefined, JsFunction,
/// sys::{ /// sys::{
/// napi_threadsafe_function_call_mode::{ /// napi_threadsafe_function_call_mode::{
@ -34,7 +34,7 @@ pub trait ToJs: Copy + Clone {
/// } /// }
/// } /// }
/// }; /// };
/// use napi_rs::threadsafe_function::{ /// use napi::threadsafe_function::{
/// ToJs, ThreadsafeFunction, /// ToJs, ThreadsafeFunction,
/// }; /// };
/// ///

View file

@ -14,3 +14,7 @@ test('should be able to throw error from native with reason', (t) => {
test('should throw if argument type is not match', (t) => { test('should throw if argument type is not match', (t) => {
t.throws(() => bindings.testThrowWithReason(2)) t.throws(() => bindings.testThrowWithReason(2))
}) })
test('should throw if Rust code panic', (t) => {
t.throws(() => bindings.testThrowWithPanic())
})

View file

@ -86,3 +86,8 @@ fn test_throw_with_reason(ctx: CallContext) -> Result<JsUnknown> {
reason.as_str()?.to_owned(), reason.as_str()?.to_owned(),
)) ))
} }
#[js_function]
pub fn test_throw_with_panic(_ctx: CallContext) -> Result<JsUnknown> {
panic!("don't panic.");
}