From efa9ba5892dd2ddc4953d456fa258bbb9aaab384 Mon Sep 17 00:00:00 2001 From: messense Date: Sun, 19 Jul 2020 16:54:10 +0800 Subject: [PATCH] Throw exception when Rust code panics --- napi-derive/src/lib.rs | 21 +++++++++++++++++++-- test_module/__test__/throw.spec.js | 4 ++++ test_module/src/lib.rs | 5 +++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/napi-derive/src/lib.rs b/napi-derive/src/lib.rs index 69b7d475..963f92bc 100644 --- a/napi-derive/src/lib.rs +++ b/napi-derive/src/lib.rs @@ -89,8 +89,9 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream { use std::mem; use std::os::raw::c_char; use std::ptr; + use std::panic::{self, AssertUnwindSafe}; 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 raw_args = 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 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.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(); match result { diff --git a/test_module/__test__/throw.spec.js b/test_module/__test__/throw.spec.js index 76a03546..461fbed4 100644 --- a/test_module/__test__/throw.spec.js +++ b/test_module/__test__/throw.spec.js @@ -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) => { t.throws(() => bindings.testThrowWithReason(2)) }) + +test('should throw if Rust code panic', (t) => { + t.throws(() => bindings.testThrowWithPanic()) +}) diff --git a/test_module/src/lib.rs b/test_module/src/lib.rs index 88297dd5..88c9a900 100644 --- a/test_module/src/lib.rs +++ b/test_module/src/lib.rs @@ -86,3 +86,8 @@ fn test_throw_with_reason(ctx: CallContext) -> Result { reason.as_str()?.to_owned(), )) } + +#[js_function] +pub fn test_throw_with_panic(_ctx: CallContext) -> Result { + panic!("don't panic."); +}