From 5ffb14729de5d36b26df0a6603dc7de48bff15af Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 2 Dec 2020 14:10:48 +0800 Subject: [PATCH] refactor(napi): js error --- napi-derive/src/lib.rs | 7 +- napi/src/async_work.rs | 6 +- napi/src/env.rs | 110 ++++++++++++++++-- napi/src/error.rs | 56 +++++---- napi/src/js_values/error.rs | 89 ++++++++++++++ napi/src/js_values/mod.rs | 6 +- napi/src/lib.rs | 2 +- napi/src/promise.rs | 5 +- napi/src/status.rs | 30 +++++ napi/src/threadsafe_function.rs | 4 +- .../__test__/napi4/tokio_rt-isolate.spec.ts | 2 +- .../napi4/tokio_rt-isolate.spec.ts.md | 14 +++ .../napi4/tokio_rt-isolate.spec.ts.snap | Bin 0 -> 209 bytes test_module/__test__/napi4/tokio_rt.spec.ts | 2 +- .../__test__/napi4/tokio_rt.spec.ts.md | 14 +++ .../__test__/napi4/tokio_rt.spec.ts.snap | Bin 0 -> 208 bytes test_module/__test__/object.spec.ts.md | 26 ++--- test_module/src/error.rs | 2 +- 18 files changed, 311 insertions(+), 64 deletions(-) create mode 100644 napi/src/js_values/error.rs create mode 100644 test_module/__test__/napi4/tokio_rt-isolate.spec.ts.md create mode 100644 test_module/__test__/napi4/tokio_rt-isolate.spec.ts.snap create mode 100644 test_module/__test__/napi4/tokio_rt.spec.ts.md create mode 100644 test_module/__test__/napi4/tokio_rt.spec.ts.snap diff --git a/napi-derive/src/lib.rs b/napi-derive/src/lib.rs index bb194dfa..dbecfb63 100644 --- a/napi-derive/src/lib.rs +++ b/napi-derive/src/lib.rs @@ -86,6 +86,7 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream { let new_fn_name = signature.ident.clone(); let execute_js_function = get_execute_js_code(new_fn_name, FunctionKind::JsFunction); let expanded = quote! { + #[inline(always)] #signature #(#fn_block)* #visibility extern "C" fn #fn_name( @@ -134,6 +135,7 @@ pub fn contextless_function(_attr: TokenStream, input: TokenStream) -> TokenStre let execute_js_function = get_execute_js_code(new_fn_name, FunctionKind::Contextless); let expanded = quote! { + #[inline(always)] #signature #(#fn_block)* #visibility extern "C" fn #fn_name( @@ -191,10 +193,7 @@ fn get_execute_js_code( }).and_then(|v| v) { #return_token_stream Err(e) => { - let message = format!("{}", e); - unsafe { - napi::sys::napi_throw_error(raw_env, ptr::null(), CString::from_vec_unchecked(message.into()).as_ptr()); - } + unsafe { napi::JsError::from(e).throw_into(raw_env) }; ptr::null_mut() } } diff --git a/napi/src/async_work.rs b/napi/src/async_work.rs index ff7bb4c4..231f7bfb 100644 --- a/napi/src/async_work.rs +++ b/napi/src/async_work.rs @@ -2,9 +2,7 @@ use std::mem; use std::os::raw::{c_char, c_void}; use std::ptr; -use crate::check_status; -use crate::js_values::NapiValue; -use crate::{sys, Env, JsObject, Result, Task}; +use crate::{check_status, js_values::NapiValue, sys, Env, JsError, JsObject, Result, Task}; struct AsyncWork { inner_task: T, @@ -110,7 +108,7 @@ unsafe extern "C" fn complete( debug_assert!(status == sys::Status::napi_ok, "Reject promise failed"); } Err(e) => { - let status = sys::napi_reject_deferred(env, deferred, e.into_raw(env)); + let status = sys::napi_reject_deferred(env, deferred, JsError::from(e).into_value(env)); debug_assert!(status == sys::Status::napi_ok, "Reject promise failed"); } }; diff --git a/napi/src/env.rs b/napi/src/env.rs index 6e7b7677..5ab7c334 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -4,11 +4,14 @@ use std::ffi::CString; use std::os::raw::{c_char, c_void}; use std::ptr; -use crate::async_work::{self, AsyncWorkPromise}; -use crate::check_status; -use crate::js_values::*; -use crate::task::Task; -use crate::{sys, Error, NodeVersion, Result, Status}; +use crate::{ + async_work::{self, AsyncWorkPromise}, + check_status, + js_values::*, + sys, + task::Task, + Error, ExtendedErrorInfo, NodeVersion, Result, Status, +}; #[cfg(feature = "napi3")] use super::cleanup_env::{CleanupEnvHook, CleanupEnvHookData}; @@ -255,6 +258,7 @@ impl Env { } #[inline] + /// This API allocates a node::Buffer object. While this is still a fully-supported data structure, in most cases using a TypedArray will suffice. pub fn create_buffer(&self, length: usize) -> Result { let mut raw_value = ptr::null_mut(); let mut data: Vec = Vec::with_capacity(length); @@ -274,6 +278,8 @@ impl Env { } #[inline] + /// This API allocates a node::Buffer object and initializes it with data backed by the passed in buffer. + /// While this is still a fully-supported data structure, in most cases using a TypedArray will suffice. pub fn create_buffer_with_data(&self, mut data: Vec) -> Result { let mut length = data.len(); let mut raw_value = ptr::null_mut(); @@ -303,6 +309,8 @@ impl Env { } #[inline] + /// This API allocates a node::Buffer object and initializes it with data copied from the passed-in buffer. + /// While this is still a fully-supported data structure, in most cases using a TypedArray will suffice. pub fn create_buffer_copy(&self, data_to_copy: D) -> Result where D: AsRef<[u8]>, @@ -376,6 +384,10 @@ impl Env { } #[inline] + /// This API allows an add-on author to create a function object in native code. + /// This is the primary mechanism to allow calling into the add-on's native code from JavaScript. + /// The newly created function is not automatically visible from script after this call. + /// Instead, a property must be explicitly set on any object that is visible to JavaScript, in order for the function to be accessible from script. pub fn create_function(&self, name: &str, callback: Callback) -> Result { let mut raw_result = ptr::null_mut(); let len = name.len(); @@ -395,19 +407,94 @@ impl Env { } #[inline] - pub fn throw(&self, error: Error) -> Result<()> { - let err_value = self.create_error(error)?.0.value; - check_status!(unsafe { sys::napi_throw(self.0, err_value) })?; - Ok(()) + /// This API retrieves a napi_extended_error_info structure with information about the last error that occurred. + /// The content of the napi_extended_error_info returned is only valid up until an n-api function is called on the same env. + /// Do not rely on the content or format of any of the extended information as it is not subject to SemVer and may change at any time. It is intended only for logging purposes. + /// This API can be called even if there is a pending JavaScript exception. + pub fn get_last_error_info(&self) -> Result { + let mut raw_extended_error = ptr::null(); + check_status!(unsafe { sys::napi_get_last_error_info(self.0, &mut raw_extended_error) })?; + unsafe { ptr::read(raw_extended_error) }.try_into() } #[inline] - pub fn throw_error(&self, msg: &str) -> Result<()> { + /// This API throws a JavaScript Error with the text provided. + pub fn throw_error(&self, msg: &str, code: Option<&str>) -> Result<()> { check_status!(unsafe { - sys::napi_throw_error(self.0, ptr::null(), CString::new(msg)?.as_ptr()) + sys::napi_throw_error( + self.0, + match code { + Some(s) => CString::new(s)?.as_ptr(), + None => ptr::null_mut(), + }, + CString::new(msg)?.as_ptr(), + ) }) } + #[inline] + /// This API throws a JavaScript RangeError with the text provided. + pub fn throw_range_error(&self, msg: &str, code: Option<&str>) -> Result<()> { + check_status!(unsafe { + sys::napi_throw_range_error( + self.0, + match code { + Some(s) => CString::new(s)?.as_ptr(), + None => ptr::null_mut(), + }, + CString::new(msg)?.as_ptr(), + ) + }) + } + + #[inline] + /// This API throws a JavaScript TypeError with the text provided. + pub fn throw_type_error(&self, msg: &str, code: Option<&str>) -> Result<()> { + check_status!(unsafe { + sys::napi_throw_type_error( + self.0, + match code { + Some(s) => CString::new(s)?.as_ptr(), + None => ptr::null_mut(), + }, + CString::new(msg)?.as_ptr(), + ) + }) + } + + #[inline] + #[allow(clippy::expect_fun_call)] + /// In the event of an unrecoverable error in a native module + /// A fatal error can be thrown to immediately terminate the process. + pub fn fatal_error(self, location: &str, message: &str) { + let location_len = location.len(); + let message_len = message.len(); + let location = + CString::new(location).expect(format!("Convert [{}] to CString failed", location).as_str()); + let message = + CString::new(message).expect(format!("Convert [{}] to CString failed", message).as_str()); + + unsafe { + sys::napi_fatal_error( + location.as_ptr(), + location_len, + message.as_ptr(), + message_len, + ) + } + } + + #[cfg(feature = "napi3")] + #[inline] + /// Trigger an 'uncaughtException' in JavaScript. + /// Useful if an async callback throws an exception with no way to recover. + pub fn fatal_exception(&self, err: Error) { + unsafe { + let js_error = JsError::from(err).into_value(self.0); + debug_assert!(sys::napi_fatal_exception(self.0, js_error) == sys::Status::napi_ok); + }; + } + #[inline] pub fn define_class( &self, @@ -757,6 +844,7 @@ impl Env { } #[inline] + /// This API represents the invocation of the Strict Equality algorithm as defined in [Section 7.2.14](https://tc39.es/ecma262/#sec-strict-equality-comparison) of the ECMAScript Language Specification. pub fn strict_equals(&self, a: A, b: B) -> Result { let mut result = false; check_status!(unsafe { sys::napi_strict_equals(self.0, a.raw(), b.raw(), &mut result) })?; diff --git a/napi/src/error.rs b/napi/src/error.rs index ad715294..acb86fb1 100644 --- a/napi/src/error.rs +++ b/napi/src/error.rs @@ -1,10 +1,10 @@ -use std::convert::From; +use std::convert::{From, TryFrom}; use std::error; +use std::ffi::CString; use std::fmt; #[cfg(feature = "serde-json")] use std::fmt::Display; -use std::os::raw::c_char; -use std::ptr; +use std::os::raw::{c_char, c_void}; #[cfg(feature = "serde-json")] use serde::{de, ser}; @@ -37,7 +37,11 @@ impl de::Error for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}: {}", self.status, self.reason) + if !self.reason.is_empty() { + write!(f, "{:?}, {}", self.status, self.reason) + } else { + write!(f, "{:?}", self.status) + } } } @@ -62,25 +66,6 @@ impl Error { reason, } } - - #[inline] - pub(crate) fn into_raw(self, env: sys::napi_env) -> sys::napi_value { - let mut err = ptr::null_mut(); - let s = self.reason; - unsafe { - let mut err_reason = ptr::null_mut(); - let status = sys::napi_create_string_utf8( - env, - s.as_ptr() as *const c_char, - s.len() as _, - &mut err_reason, - ); - debug_assert!(status == sys::Status::napi_ok, "Create error reason failed"); - let status = sys::napi_create_error(env, ptr::null_mut(), err_reason, &mut err); - debug_assert!(status == sys::Status::napi_ok, "Create error failed"); - }; - err - } } impl From for Error { @@ -101,6 +86,31 @@ impl From for Error { } } +#[derive(Clone, Debug)] +pub struct ExtendedErrorInfo { + pub message: String, + pub engine_reserved: *mut c_void, + pub engine_error_code: u32, + pub error_code: Status, +} + +impl TryFrom for ExtendedErrorInfo { + type Error = Error; + + fn try_from(value: sys::napi_extended_error_info) -> Result { + Ok(Self { + message: unsafe { + CString::from_raw(value.error_message as *mut c_char) + .into_string() + .map_err(|e| Error::new(Status::GenericFailure, format!("{}", e)))? + }, + engine_error_code: value.engine_error_code, + engine_reserved: value.engine_reserved, + error_code: Status::from(value.error_code), + }) + } +} + #[doc(hidden)] #[macro_export(local_inner_macros)] macro_rules! check_status { diff --git a/napi/src/js_values/error.rs b/napi/src/js_values/error.rs new file mode 100644 index 00000000..da18f0de --- /dev/null +++ b/napi/src/js_values/error.rs @@ -0,0 +1,89 @@ +use std::ffi::CString; +use std::ptr; + +use crate::{check_status, sys, Env, Error}; + +pub struct JsError(Error); + +pub struct JsTypeError(Error); + +pub struct JsRangeError(Error); + +macro_rules! impl_object_methods { + ($js_value:ident, $kind:expr) => { + impl $js_value { + #[inline(always)] + /// # Safety + /// + /// This function is safety if env is not null ptr. + pub unsafe fn into_value(self, env: sys::napi_env) -> sys::napi_value { + let error_status = format!("{:?}", self.0.status); + let status_len = error_status.len(); + let error_code_string = CString::new(error_status).unwrap(); + let reason_len = self.0.reason.len(); + let reason = CString::new(self.0.reason).unwrap(); + let mut error_code = ptr::null_mut(); + let mut reason_string = ptr::null_mut(); + let mut js_error = ptr::null_mut(); + debug_assert!( + sys::napi_create_string_utf8( + env, + error_code_string.as_ptr(), + status_len, + &mut error_code + ) == sys::Status::napi_ok + ); + debug_assert!( + sys::napi_create_string_utf8(env, reason.as_ptr(), reason_len, &mut reason_string) + == sys::Status::napi_ok + ); + debug_assert!($kind(env, error_code, reason_string, &mut js_error) == sys::Status::napi_ok); + js_error + } + + #[inline(always)] + /// # Safety + /// + /// This function is safety if env is not null ptr. + pub unsafe fn throw_into(self, env: sys::napi_env) { + let js_error = self.into_value(env); + debug_assert!(sys::napi_throw(env, js_error) == sys::Status::napi_ok); + } + + #[inline(always)] + pub fn throw(&self, env: &Env) -> Result<(), Error> { + let error_status = format!("{:?}", self.0.status); + let status_len = error_status.len(); + let error_code_string = CString::new(error_status).unwrap(); + let reason_len = self.0.reason.len(); + let reason = CString::new(self.0.reason.clone()).unwrap(); + let mut error_code = ptr::null_mut(); + let mut reason_string = ptr::null_mut(); + let mut js_error = ptr::null_mut(); + check_status!(unsafe { + sys::napi_create_string_utf8( + env.0, + error_code_string.as_ptr(), + status_len, + &mut error_code, + ) + })?; + check_status!(unsafe { + sys::napi_create_string_utf8(env.0, reason.as_ptr(), reason_len, &mut reason_string) + })?; + check_status!(unsafe { $kind(env.0, error_code, reason_string, &mut js_error) })?; + check_status!(unsafe { sys::napi_throw(env.0, js_error) }) + } + } + + impl From for $js_value { + fn from(err: Error) -> Self { + Self(err) + } + } + }; +} + +impl_object_methods!(JsError, sys::napi_create_error); +impl_object_methods!(JsTypeError, sys::napi_create_type_error); +impl_object_methods!(JsRangeError, sys::napi_create_range_error); diff --git a/napi/src/js_values/mod.rs b/napi/src/js_values/mod.rs index 1120be69..56c7f386 100644 --- a/napi/src/js_values/mod.rs +++ b/napi/src/js_values/mod.rs @@ -18,6 +18,7 @@ mod buffer; #[cfg(feature = "napi5")] mod date; mod either; +mod error; mod escapable_handle_scope; mod function; mod global; @@ -41,6 +42,7 @@ pub use date::*; #[cfg(feature = "serde-json")] pub(crate) use de::De; pub use either::Either; +pub use error::*; pub use escapable_handle_scope::EscapableHandleScope; pub use function::JsFunction; pub use global::*; @@ -508,7 +510,9 @@ macro_rules! impl_object_methods { #[inline] pub fn get_array_length_unchecked(&self) -> Result { let mut length: u32 = 0; - check_status!(unsafe { sys::napi_get_array_length(self.0.env, self.raw(), &mut length) })?; + check_status!(unsafe { + sys::napi_get_array_length(self.0.env, self.0.value, &mut length) + })?; Ok(length) } } diff --git a/napi/src/lib.rs b/napi/src/lib.rs index f9e441ad..79e3a36e 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -118,7 +118,7 @@ pub use napi_sys as sys; pub use call_context::CallContext; pub use env::*; -pub use error::{Error, Result}; +pub use error::{Error, ExtendedErrorInfo, Result}; pub use js_values::*; pub use module::Module; pub use status::Status; diff --git a/napi/src/promise.rs b/napi/src/promise.rs index 10bbbd51..a7ad507c 100644 --- a/napi/src/promise.rs +++ b/napi/src/promise.rs @@ -2,7 +2,7 @@ use futures::prelude::*; use std::os::raw::{c_char, c_void}; use std::ptr; -use crate::{check_status, sys, Env, NapiValue, Result}; +use crate::{check_status, sys, Env, JsError, NapiValue, Result}; pub struct FuturePromise { deferred: sys::napi_deferred, @@ -109,7 +109,8 @@ unsafe extern "C" fn call_js_cb( debug_assert!(status == sys::Status::napi_ok, "Resolve promise failed"); } Err(e) => { - let status = sys::napi_reject_deferred(raw_env, deferred, e.into_raw(raw_env)); + let status = + sys::napi_reject_deferred(raw_env, deferred, JsError::from(e).into_value(raw_env)); debug_assert!(status == sys::Status::napi_ok, "Reject promise failed"); } }; diff --git a/napi/src/status.rs b/napi/src/status.rs index bf9c5933..d5560954 100644 --- a/napi/src/status.rs +++ b/napi/src/status.rs @@ -68,3 +68,33 @@ impl From for Status { } } } + +impl From for i32 { + fn from(code: Status) -> Self { + match code { + Status::Ok => sys::Status::napi_ok, + Status::InvalidArg => sys::Status::napi_invalid_arg, + Status::ObjectExpected => sys::Status::napi_object_expected, + Status::StringExpected => sys::Status::napi_string_expected, + Status::NameExpected => sys::Status::napi_name_expected, + Status::FunctionExpected => sys::Status::napi_function_expected, + Status::NumberExpected => sys::Status::napi_number_expected, + Status::BooleanExpected => sys::Status::napi_boolean_expected, + Status::ArrayExpected => sys::Status::napi_array_expected, + Status::GenericFailure => sys::Status::napi_generic_failure, + Status::PendingException => sys::Status::napi_pending_exception, + Status::Cancelled => sys::Status::napi_cancelled, + Status::EscapeCalledTwice => sys::Status::napi_escape_called_twice, + Status::HandleScopeMismatch => sys::Status::napi_handle_scope_mismatch, + Status::CallbackScopeMismatch => sys::Status::napi_callback_scope_mismatch, + Status::QueueFull => sys::Status::napi_queue_full, + Status::Closing => sys::Status::napi_closing, + Status::BigintExpected => sys::Status::napi_bigint_expected, + Status::DateExpected => sys::Status::napi_date_expected, + Status::ArrayBufferExpected => sys::Status::napi_arraybuffer_expected, + Status::DetachableArraybufferExpected => sys::Status::napi_detachable_arraybuffer_expected, + Status::WouldDeadlock => sys::Status::napi_would_deadlock, + Status::Unknown => sys::Status::napi_generic_failure, + } + } +} diff --git a/napi/src/threadsafe_function.rs b/napi/src/threadsafe_function.rs index 0d2a4184..cb94e88d 100644 --- a/napi/src/threadsafe_function.rs +++ b/napi/src/threadsafe_function.rs @@ -5,7 +5,7 @@ use std::ptr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use crate::{check_status, sys, Env, Error, JsFunction, NapiValue, Result, Status}; +use crate::{check_status, sys, Env, Error, JsError, JsFunction, NapiValue, Result, Status}; use sys::napi_threadsafe_function_call_mode; @@ -291,7 +291,7 @@ unsafe extern "C" fn call_js_cb( recv, js_callback, 1, - [e.into_raw(raw_env)].as_mut_ptr(), + [JsError::from(e).into_value(raw_env)].as_mut_ptr(), ptr::null_mut(), ); } diff --git a/test_module/__test__/napi4/tokio_rt-isolate.spec.ts b/test_module/__test__/napi4/tokio_rt-isolate.spec.ts index 2373288f..7dc63c2e 100644 --- a/test_module/__test__/napi4/tokio_rt-isolate.spec.ts +++ b/test_module/__test__/napi4/tokio_rt-isolate.spec.ts @@ -23,6 +23,6 @@ test('should be able adjust queue size via process.env', async (t) => { ) throw new TypeError('Unreachable') } catch (e) { - t.is(e.message, 'QueueFull: Failed to run future: no available capacity') + t.snapshot({ code: e.code, message: e.message }) } }) diff --git a/test_module/__test__/napi4/tokio_rt-isolate.spec.ts.md b/test_module/__test__/napi4/tokio_rt-isolate.spec.ts.md new file mode 100644 index 00000000..3c6eb0f0 --- /dev/null +++ b/test_module/__test__/napi4/tokio_rt-isolate.spec.ts.md @@ -0,0 +1,14 @@ +# Snapshot report for `test_module/__test__/napi4/tokio_rt-isolate.spec.ts` + +The actual snapshot is saved in `tokio_rt-isolate.spec.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## should be able adjust queue size via process.env + +> Snapshot 1 + + { + code: 'QueueFull', + message: 'Failed to run future: no available capacity', + } diff --git a/test_module/__test__/napi4/tokio_rt-isolate.spec.ts.snap b/test_module/__test__/napi4/tokio_rt-isolate.spec.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..78ee14ca63dbe14c76a4b351c735a3270fa425a7 GIT binary patch literal 209 zcmZ<^b5sbi(_+BV{@xpAf+bgmd555=IA!<$)719679wtOtzX_U2CGw?kifImsMDp zWEHb;TXa&IUsjis=RT*v%y~{xnVy+}tEM`|?Q=?!3{W%FR1a0O+^Aq^xp|2YLuW9n I;7Oo60V { ) throw new TypeError('Unreachable') } catch (e) { - t.is(e.message, 'QueueFull: Failed to run future: no available capacity') + t.snapshot({ code: e.code, message: e.message }) } }) diff --git a/test_module/__test__/napi4/tokio_rt.spec.ts.md b/test_module/__test__/napi4/tokio_rt.spec.ts.md new file mode 100644 index 00000000..a16cfa66 --- /dev/null +++ b/test_module/__test__/napi4/tokio_rt.spec.ts.md @@ -0,0 +1,14 @@ +# Snapshot report for `test_module/__test__/napi4/tokio_rt.spec.ts` + +The actual snapshot is saved in `tokio_rt.spec.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## should reject if task queue is full + +> Snapshot 1 + + { + code: 'QueueFull', + message: 'Failed to run future: no available capacity', + } diff --git a/test_module/__test__/napi4/tokio_rt.spec.ts.snap b/test_module/__test__/napi4/tokio_rt.spec.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..6409d3a2127ac6092a9bbb4d26563363766db8b5 GIT binary patch literal 208 zcmV;>05AVRRzVhGc&M**^C^F%tk=k0*K>)SeTJbkde(l zDJwO(gpm=>6c7+#VB}?BWMW|C2dZHbVPs}tWEEs&NzPA6g>g6oOH)fz-AZ$E&{VPK zrWO|`ro$C#yCr7kq^2m8 Snapshot 1 + + 'RustPropertyKey' + ## setProperty > Snapshot 1 'Rust object property' -## setNamedProperty +## testDeleteElement > Snapshot 1 - 'RustPropertyKey' + [ + 0, + undefined, + undefined, + 3, + ] ## testGetPropertyNames @@ -37,14 +48,3 @@ Generated by [AVA](https://avajs.dev). undefined, 'foo', ] - -## testDeleteElement - -> Snapshot 1 - - [ - 0, - undefined, - undefined, - 3, - ] diff --git a/test_module/src/error.rs b/test_module/src/error.rs index 7bc86183..a4423995 100644 --- a/test_module/src/error.rs +++ b/test_module/src/error.rs @@ -10,7 +10,7 @@ fn test_throw_with_reason(ctx: CallContext) -> Result { let reason = ctx.get::(0)?; Err(Error::new( Status::GenericFailure, - reason.into_utf8()?.as_str()?.to_owned(), + reason.into_utf8()?.into_owned()?, )) }