diff --git a/cli/napi/env.rs b/cli/napi/env.rs index 558b9b0127..922c641402 100644 --- a/cli/napi/env.rs +++ b/cli/napi/env.rs @@ -158,8 +158,8 @@ fn napi_get_node_version( env: *mut Env, result: *mut *const napi_node_version, ) -> Result { - let _: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - crate::check_arg!(result); + crate::check_env!(env); + crate::check_arg!(env, result); *result = &NODE_VERSION as *const napi_node_version; Ok(()) diff --git a/cli/napi/js_native_api.rs b/cli/napi/js_native_api.rs index c2f3a11228..103a5f06a6 100644 --- a/cli/napi/js_native_api.rs +++ b/cli/napi/js_native_api.rs @@ -13,30 +13,126 @@ use deno_runtime::deno_napi::function::create_function_template; use deno_runtime::deno_napi::function::CallbackInfo; use std::ptr::NonNull; -// Macro to check napi arguments. -// If nullptr, return Err(Error::InvalidArg). #[macro_export] -macro_rules! check_arg { - ($ptr: expr) => { - if $ptr.is_null() { +macro_rules! check_env { + ($env: expr) => { + if $env.is_null() { return Err(Error::InvalidArg); } }; } -macro_rules! check_arg_option { - ($ptr: expr) => { - if $ptr.is_none() { - return Err(Error::InvalidArg); +#[inline] +unsafe fn napi_value_unchecked(val: napi_value) -> v8::Local { + transmute::>(val) +} + +#[macro_export] +macro_rules! return_status_if_false { + ($env: expr, $condition: expr, $status: ident) => { + if !$condition { + return Err( + $crate::napi::js_native_api::napi_set_last_error( + $env, + $status, + 0, + std::ptr::null_mut(), + ) + .into(), + ); } }; } +fn check_new_from_utf8_len<'s>( + env: *mut Env, + str_: *const c_char, + len: usize, +) -> std::result::Result, Error> { + return_status_if_false!( + env, + (len == NAPI_AUTO_LENGTH) || len <= INT_MAX as _, + napi_invalid_arg + ); + return_status_if_false!(env, !str_.is_null(), napi_invalid_arg); + let string = if len == NAPI_AUTO_LENGTH { + let result = unsafe { std::ffi::CStr::from_ptr(str_ as *const _) }.to_str(); + return_status_if_false!(env, result.is_ok(), napi_generic_failure); + result.unwrap() + } else { + let string = unsafe { std::slice::from_raw_parts(str_ as *const u8, len) }; + let result = std::str::from_utf8(string); + return_status_if_false!(env, result.is_ok(), napi_generic_failure); + result.unwrap() + }; + let result = { + let env = unsafe { &mut *env }; + v8::String::new(&mut env.scope(), string) + }; + return_status_if_false!(env, result.is_some(), napi_generic_failure); + Ok(result.unwrap()) +} + +#[inline] +fn check_new_from_utf8<'s>( + env: *mut Env, + str_: *const c_char, +) -> std::result::Result, Error> { + check_new_from_utf8_len(env, str_, NAPI_AUTO_LENGTH) +} + +#[macro_export] +macro_rules! status_call { + ($call: expr) => { + let status = $call; + if status != napi_ok { + return Err(status.into()); + } + }; +} + +// Macro to check napi arguments. +// If nullptr, return Err(Error::InvalidArg). +#[macro_export] +macro_rules! check_arg { + ($env: expr, $ptr: expr) => { + $crate::return_status_if_false!($env, !$ptr.is_null(), napi_invalid_arg); + }; +} + +macro_rules! check_arg_option { + ($env: expr, $opt: expr) => { + $crate::return_status_if_false!($env, $opt.is_some(), napi_invalid_arg); + }; +} + +fn napi_clear_last_error(env: *mut Env) { + let env = unsafe { &mut *env }; + env.last_error.error_code = napi_ok; + env.last_error.engine_error_code = 0; + env.last_error.engine_reserved = std::ptr::null_mut(); + env.last_error.error_message = std::ptr::null_mut(); +} + +pub(crate) fn napi_set_last_error( + env: *mut Env, + error_code: napi_status, + engine_error_code: i32, + engine_reserved: *mut c_void, +) -> napi_status { + let env = unsafe { &mut *env }; + env.last_error.error_code = error_code; + env.last_error.engine_error_code = engine_error_code; + env.last_error.engine_reserved = engine_reserved; + error_code +} + /// Returns napi_value that represents a new JavaScript Array. #[napi_sym::napi_sym] fn napi_create_array(env: *mut Env, result: *mut napi_value) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::Array::new(&mut env.scope(), 0).into(); Ok(()) } @@ -47,8 +143,9 @@ fn napi_create_array_with_length( len: i32, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::Array::new(&mut env.scope(), len).into(); Ok(()) } @@ -60,8 +157,9 @@ fn napi_create_arraybuffer( data: *mut *mut u8, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; let value = v8::ArrayBuffer::new(&mut env.scope(), len); if !data.is_null() { @@ -78,8 +176,9 @@ fn napi_create_bigint_int64( value: i64, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::BigInt::new_from_i64(&mut env.scope(), value).into(); Ok(()) } @@ -90,8 +189,9 @@ fn napi_create_bigint_uint64( value: u64, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::BigInt::new_from_u64(&mut env.scope(), value).into(); Ok(()) } @@ -104,9 +204,10 @@ fn napi_create_bigint_words( word_count: usize, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(words); - check_arg!(result); + check_env!(env); + check_arg!(env, words); + let env = unsafe { &mut *env }; + check_arg!(env, result); if word_count > INT_MAX as _ { return Err(Error::InvalidArg); @@ -135,7 +236,8 @@ fn napi_create_buffer( data: *mut *mut u8, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let value = v8::ArrayBuffer::new(&mut env.scope(), len); if !data.is_null() { *data = get_array_buffer_ptr(value); @@ -154,7 +256,8 @@ fn napi_create_buffer_copy( result_data: *mut *mut u8, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let value = v8::ArrayBuffer::new(&mut env.scope(), len); let ptr = get_array_buffer_ptr(value); std::ptr::copy(data, ptr, len); @@ -173,8 +276,9 @@ fn napi_coerce_to_bool( value: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let coerced = value.to_boolean(&mut env.scope()); let value: v8::Local = coerced.into(); *result = value.into(); @@ -187,8 +291,9 @@ fn napi_coerce_to_number( value: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let coerced = value .to_number(&mut env.scope()) .ok_or(Error::NumberExpected)?; @@ -203,8 +308,9 @@ fn napi_coerce_to_object( value: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let coerced = value.to_object(&mut env.scope()).unwrap(); let value: v8::Local = coerced.into(); *result = value.into(); @@ -217,8 +323,9 @@ fn napi_coerce_to_string( value: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let coerced = value.to_string(&mut env.scope()).unwrap(); let value: v8::Local = coerced.into(); *result = value.into(); @@ -233,9 +340,10 @@ fn napi_create_dataview( byte_offset: usize, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(data); - check_arg!(result); + check_env!(env); + check_arg!(env, data); + let env = unsafe { &mut *env }; + check_arg!(env, result); let value = v8::ArrayBuffer::new(&mut env.scope(), len); if !data.is_null() { *data = get_array_buffer_ptr(value); @@ -264,7 +372,8 @@ fn napi_create_date( time: f64, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let value: v8::Local = v8::Date::new(&mut env.scope(), time).unwrap().into(); *result = value.into(); @@ -277,12 +386,49 @@ fn napi_create_double( value: f64, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::Number::new(&mut env.scope(), value).into(); Ok(()) } +fn set_error_code( + env: *mut Env, + error: v8::Local, + code: napi_value, + code_cstring: *const c_char, +) -> Result { + if code.is_some() || !code_cstring.is_null() { + let err_object: v8::Local = error.try_into().unwrap(); + + let code_value: v8::Local = if code.is_some() { + let mut code_value = unsafe { napi_value_unchecked(code) }; + return_status_if_false!( + env, + code_value.is_string(), + napi_string_expected + ); + code_value + } else { + let name = check_new_from_utf8(env, code_cstring)?; + name.into() + }; + + let mut scope = unsafe { &mut *env }.scope(); + let code_key = v8::String::new(&mut scope, "code").unwrap(); + + if err_object + .set(&mut scope, code_key.into(), code_value) + .is_none() + { + return Err(napi_generic_failure.into()); + } + } + + Ok(()) +} + #[napi_sym::napi_sym] fn napi_create_error( env: *mut Env, @@ -290,15 +436,62 @@ fn napi_create_error( msg: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + check_arg_option!(env, msg); + check_arg!(env, result); + let mut message_value = napi_value_unchecked(msg); + return_status_if_false!(env, message_value.is_string(), napi_string_expected); + let error_obj = v8::Exception::error( + &mut unsafe { &mut *env }.scope(), + message_value.try_into().unwrap(), + ); + set_error_code(env, error_obj, code, std::ptr::null())?; + *result = error_obj.into(); + napi_clear_last_error(env); + Ok(()) +} - let _code = transmute::>(code); - let msg = transmute::>(msg); - - let msg = msg.to_string(&mut env.scope()).unwrap(); - let error = v8::Exception::error(&mut env.scope(), msg); - *result = error.into(); +#[napi_sym::napi_sym] +fn napi_create_type_error( + env: *mut Env, + code: napi_value, + msg: napi_value, + result: *mut napi_value, +) -> Result { + check_env!(env); + check_arg_option!(env, msg); + check_arg!(env, result); + let mut message_value = napi_value_unchecked(msg); + return_status_if_false!(env, message_value.is_string(), napi_string_expected); + let error_obj = v8::Exception::type_error( + &mut unsafe { &mut *env }.scope(), + message_value.try_into().unwrap(), + ); + set_error_code(env, error_obj, code, std::ptr::null())?; + *result = error_obj.into(); + napi_clear_last_error(env); + Ok(()) +} +#[napi_sym::napi_sym] +fn napi_create_range_error( + env: *mut Env, + code: napi_value, + msg: napi_value, + result: *mut napi_value, +) -> Result { + check_env!(env); + check_arg_option!(env, msg); + check_arg!(env, result); + let mut message_value = napi_value_unchecked(msg); + return_status_if_false!(env, message_value.is_string(), napi_string_expected); + let error_obj = v8::Exception::range_error( + &mut unsafe { &mut *env }.scope(), + message_value.try_into().unwrap(), + ); + set_error_code(env, error_obj, code, std::ptr::null())?; + *result = error_obj.into(); + napi_clear_last_error(env); Ok(()) } @@ -310,7 +503,8 @@ fn napi_create_external( _finalize_hint: *mut c_void, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let value: v8::Local = v8::External::new(&mut env.scope(), value).into(); // TODO: finalization @@ -351,7 +545,8 @@ fn napi_create_external_arraybuffer( finalize_hint: *mut c_void, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let _slice = std::slice::from_raw_parts(data as *mut u8, byte_length); // TODO: finalization let store: UniqueRef = @@ -378,7 +573,8 @@ fn napi_create_external_buffer( _finalize_hint: *mut c_void, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let slice = if byte_length == -1 { std::ffi::CStr::from_ptr(data as *const _).to_bytes() } else { @@ -400,17 +596,17 @@ fn napi_create_external_buffer( #[napi_sym::napi_sym] fn napi_create_function( - env_ptr: *mut Env, + env: *mut Env, name: *const u8, length: usize, cb: napi_callback, cb_info: napi_callback_info, result: *mut napi_value, ) -> Result { - let _: &mut Env = env_ptr.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); - check_arg_option!(cb); - check_arg!(name); + check_env!(env); + check_arg!(env, result); + check_arg_option!(env, cb); + check_arg!(env, name); if length > INT_MAX as _ { return Err(Error::InvalidArg); @@ -424,7 +620,7 @@ fn napi_create_function( std::str::from_utf8_unchecked(name) }; - *result = create_function(env_ptr, Some(name), cb, cb_info).into(); + *result = create_function(env, Some(name), cb, cb_info).into(); Ok(()) } @@ -434,8 +630,9 @@ fn napi_create_int32( value: i32, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::Integer::new(&mut env.scope(), value).into(); Ok(()) } @@ -446,8 +643,9 @@ fn napi_create_uint32( value: u32, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::Integer::new_from_unsigned(&mut env.scope(), value).into(); Ok(()) } @@ -458,15 +656,17 @@ fn napi_create_int64( value: i64, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::Number::new(&mut env.scope(), value as f64).into(); Ok(()) } #[napi_sym::napi_sym] fn napi_create_object(env: *mut Env, result: *mut napi_value) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let object = v8::Object::new(&mut env.scope()); *result = object.into(); Ok(()) @@ -478,7 +678,8 @@ fn napi_create_promise( deferred: *mut napi_deferred, promise_out: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let resolver = v8::PromiseResolver::new(&mut env.scope()).unwrap(); let mut global = v8::Global::new(&mut env.scope(), resolver); let mut global_ptr = global.into_raw(); @@ -489,26 +690,6 @@ fn napi_create_promise( Ok(()) } -#[napi_sym::napi_sym] -fn napi_create_range_error( - env: *mut Env, - _code: napi_value, - msg: napi_value, - result: *mut napi_value, -) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - - // let code = transmute::>(code); - let msg = transmute::>(msg); - - let msg = msg.to_string(&mut env.scope()).unwrap(); - - let error = v8::Exception::range_error(&mut env.scope(), msg); - *result = error.into(); - - Ok(()) -} - #[napi_sym::napi_sym] fn napi_create_reference( env: *mut Env, @@ -516,9 +697,10 @@ fn napi_create_reference( _initial_refcount: u32, result: *mut napi_ref, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; - let value = transmute::>(value); + let value = napi_value_unchecked(value); let global = v8::Global::new(&mut env.scope(), value); let mut global_ptr = global.into_raw(); *result = transmute::, napi_ref>(global_ptr); @@ -532,15 +714,17 @@ fn napi_create_string_latin1( length: usize, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; if length > 0 { - check_arg!(string); - } - check_arg!(result); - let safe_len = (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _; - if !safe_len { - return Err(Error::InvalidArg); + check_arg!(env, string); } + check_arg!(env, result); + return_status_if_false!( + env, + (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _, + napi_invalid_arg + ); let string = if length == NAPI_AUTO_LENGTH { std::ffi::CStr::from_ptr(string as *const _) @@ -571,15 +755,17 @@ fn napi_create_string_utf16( length: usize, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; if length > 0 { - check_arg!(string); - } - check_arg!(result); - let safe_len = (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _; - if !safe_len { - return Err(Error::InvalidArg); + check_arg!(env, string); } + check_arg!(env, result); + return_status_if_false!( + env, + (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _, + napi_invalid_arg + ); let string = if length == NAPI_AUTO_LENGTH { let s = std::ffi::CStr::from_ptr(string as *const _) @@ -610,15 +796,17 @@ fn napi_create_string_utf8( length: usize, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; if length > 0 { - check_arg!(string); - } - check_arg!(result); - let safe_len = (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _; - if !safe_len { - return Err(Error::InvalidArg); + check_arg!(env, string); } + check_arg!(env, result); + return_status_if_false!( + env, + (length == NAPI_AUTO_LENGTH) || length <= INT_MAX as _, + napi_invalid_arg + ); let string = if length == NAPI_AUTO_LENGTH { std::ffi::CStr::from_ptr(string as *const _) @@ -640,8 +828,9 @@ fn napi_create_symbol( description: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; let scope = &mut env.scope(); let description = description @@ -654,26 +843,6 @@ fn napi_create_symbol( Ok(()) } -#[napi_sym::napi_sym] -fn napi_create_type_error( - env: *mut Env, - _code: napi_value, - msg: napi_value, - result: *mut napi_value, -) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - - // let code = transmute::>(code); - let msg = transmute::>(msg); - - let msg = msg.to_string(&mut env.scope()).unwrap(); - - let error = v8::Exception::type_error(&mut env.scope(), msg); - *result = error.into(); - - Ok(()) -} - #[napi_sym::napi_sym] fn napi_create_typedarray( env: *mut Env, @@ -683,8 +852,9 @@ fn napi_create_typedarray( byte_offset: usize, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let ab = transmute::>(arraybuffer); + check_env!(env); + let env = unsafe { &mut *env }; + let ab = napi_value_unchecked(arraybuffer); let ab = v8::Local::::try_from(ab).unwrap(); let typedarray: v8::Local = match ty { napi_uint8_array => { @@ -760,18 +930,19 @@ fn napi_make_callback( argv: *const napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg_option!(recv); + check_env!(env); + let env = unsafe { &mut *env }; + check_arg_option!(env, recv); if argc > 0 { - check_arg!(argv); + check_arg!(env, argv); } if !async_context.is_null() { log::info!("napi_make_callback: async_context is not supported"); } - let recv = transmute::>(recv); - let func = transmute::>(func); + let recv = napi_value_unchecked(recv); + let func = napi_value_unchecked(func); let func = v8::Local::::try_from(func) .map_err(|_| Error::FunctionExpected)?; @@ -788,8 +959,9 @@ fn napi_get_value_bigint_int64( value: napi_value, result: *mut i64, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let bigint = value.to_big_int(&mut env.scope()).unwrap(); *result = bigint.i64_value().0; Ok(()) @@ -801,8 +973,9 @@ fn napi_get_value_bigint_uint64( value: napi_value, result: *mut u64, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let bigint = value.to_big_int(&mut env.scope()).unwrap(); *result = bigint.u64_value().0; Ok(()) @@ -816,9 +989,10 @@ fn napi_get_value_bigint_words( size: *mut usize, out_words: *mut u64, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; - let value = transmute::>(value); + let value = napi_value_unchecked(value); let bigint = value.to_big_int(&mut env.scope()).unwrap(); let out_words = std::slice::from_raw_parts_mut(out_words, *size); @@ -839,8 +1013,9 @@ fn napi_get_value_bool( value: napi_value, result: *mut bool, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); *result = value.boolean_value(&mut env.scope()); Ok(()) } @@ -851,8 +1026,10 @@ fn napi_get_value_double( value: napi_value, result: *mut f64, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); + return_status_if_false!(env, value.is_number(), napi_number_expected); *result = value.number_value(&mut env.scope()).unwrap(); Ok(()) } @@ -863,7 +1040,7 @@ fn napi_get_value_external( value: napi_value, result: *mut *mut c_void, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); let ext = v8::Local::::try_from(value).unwrap(); *result = ext.value(); Ok(()) @@ -875,8 +1052,9 @@ fn napi_get_value_int32( value: napi_value, result: *mut i32, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); *result = value.int32_value(&mut env.scope()).unwrap(); Ok(()) } @@ -887,8 +1065,9 @@ fn napi_get_value_int64( value: napi_value, result: *mut i64, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); *result = value.integer_value(&mut env.scope()).unwrap(); Ok(()) } @@ -901,9 +1080,10 @@ fn napi_get_value_string_latin1( bufsize: usize, result: *mut usize, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; - let value = transmute::>(value); + let value = napi_value_unchecked(value); if !value.is_string() && !value.is_string_object() { return Err(Error::StringExpected); @@ -941,9 +1121,10 @@ fn napi_get_value_string_utf8( bufsize: usize, result: *mut usize, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; - let value = transmute::>(value); + let value = napi_value_unchecked(value); if !value.is_string() && !value.is_string_object() { return Err(Error::StringExpected); @@ -982,9 +1163,10 @@ fn napi_get_value_string_utf16( bufsize: usize, result: *mut usize, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; - let value = transmute::>(value); + let value = napi_value_unchecked(value); if !value.is_string() && !value.is_string_object() { return Err(Error::StringExpected); @@ -1020,8 +1202,9 @@ fn napi_get_value_uint32( value: napi_value, result: *mut u32, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); *result = value.uint32_value(&mut env.scope()).unwrap(); Ok(()) } @@ -1046,7 +1229,8 @@ fn napi_adjust_external_memory( change_in_bytes: i64, adjusted_value: &mut i64, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let isolate = &mut *env.isolate_ptr; *adjusted_value = isolate.adjust_amount_of_external_allocated_memory(change_in_bytes); @@ -1062,9 +1246,10 @@ fn napi_call_function( argv: *const napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let recv = transmute::>(recv); - let func = transmute::>(func); + check_env!(env); + let env = unsafe { &mut *env }; + let recv = napi_value_unchecked(recv); + let func = napi_value_unchecked(func); let func = v8::Local::::try_from(func) .map_err(|_| Error::FunctionExpected)?; @@ -1111,11 +1296,11 @@ fn napi_define_class( result: *mut napi_value, ) -> Result { let env: &mut Env = env_ptr.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); - check_arg_option!(constructor); + check_arg!(env, result); + check_arg_option!(env, constructor); if property_count > 0 { - check_arg!(properties); + check_arg!(env, properties); } let name = if length == -1 { @@ -1276,8 +1461,9 @@ fn napi_delete_element( index: u32, result: *mut bool, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let obj = value.to_object(&mut env.scope()).unwrap(); *result = obj.delete_index(&mut env.scope(), index).unwrap_or(false); Ok(()) @@ -1290,9 +1476,10 @@ fn napi_delete_property( key: napi_value, result: *mut bool, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg_option!(key); - check_arg!(result); + check_env!(env); + let env = unsafe { &mut *env }; + check_arg_option!(env, key); + check_arg!(env, result); let scope = &mut env.scope(); let object = object @@ -1315,7 +1502,7 @@ fn napi_delete_reference(env: *mut Env, _nref: napi_ref) -> Result { #[napi_sym::napi_sym] fn napi_detach_arraybuffer(_env: *mut Env, value: napi_value) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); let ab = v8::Local::::try_from(value).unwrap(); ab.detach(None); Ok(()) @@ -1344,7 +1531,8 @@ fn napi_get_and_clear_last_exception( env: *mut Env, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; // TODO: just return undefined for now we don't cache // exceptions in env. let value: v8::Local = v8::undefined(&mut env.scope()).into(); @@ -1358,7 +1546,7 @@ fn napi_get_array_length( value: napi_value, result: *mut u32, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); *result = v8::Local::::try_from(value).unwrap().length(); Ok(()) } @@ -1370,7 +1558,7 @@ fn napi_get_arraybuffer_info( data: *mut *mut u8, length: *mut usize, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); let buf = v8::Local::::try_from(value).unwrap(); if !data.is_null() { *data = get_array_buffer_ptr(buf); @@ -1387,8 +1575,9 @@ fn napi_get_boolean( value: bool, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::Boolean::new(env.isolate(), value).into(); Ok(()) } @@ -1400,8 +1589,9 @@ fn napi_get_buffer_info( data: *mut *mut u8, length: *mut usize, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let buf = v8::Local::::try_from(value).unwrap(); let buffer_name = v8::String::new(&mut env.scope(), "buffer").unwrap(); let abuf = v8::Local::::try_from( @@ -1427,7 +1617,7 @@ fn napi_get_cb_info( cb_data: *mut *mut c_void, ) -> Result { let _: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(cbinfo); + check_arg!(env, cbinfo); let cbinfo: &CallbackInfo = &*(cbinfo as *const CallbackInfo); let args = &*(cbinfo.args as *const v8::FunctionCallbackArguments); @@ -1465,8 +1655,9 @@ fn napi_get_dataview_info( data: *mut *mut u8, length: *mut usize, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let buf = v8::Local::::try_from(value).unwrap(); let buffer_name = v8::String::new(&mut env.scope(), "buffer").unwrap(); let abuf = v8::Local::::try_from( @@ -1486,14 +1677,12 @@ fn napi_get_date_value( value: napi_value, result: *mut f64, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); - - if !value.is_date() { - return Err(Error::DateExpected); - } - + check_env!(env); + let value = napi_value_unchecked(value); + return_status_if_false!(env, value.is_date(), napi_date_expected); + let env = unsafe { &mut *env }; let date = v8::Local::::try_from(value).unwrap(); + // TODO: should be value of *result = date.number_value(&mut env.scope()).unwrap(); Ok(()) } @@ -1505,8 +1694,9 @@ fn napi_get_element( index: u32, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let object = transmute::>(object); + check_env!(env); + let env = unsafe { &mut *env }; + let object = napi_value_unchecked(object); let array = v8::Local::::try_from(object).unwrap(); let value: v8::Local = array.get_index(&mut env.scope(), index).unwrap(); @@ -1516,7 +1706,8 @@ fn napi_get_element( #[napi_sym::napi_sym] fn napi_get_global(env: *mut Env, result: *mut napi_value) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let context = &mut env.scope().get_current_context(); let global = context.global(&mut env.scope()); @@ -1533,6 +1724,7 @@ fn napi_get_instance_data(env: *mut Env, result: *mut *mut c_void) -> Result { Ok(()) } +// TODO(bartlomieju): this function is broken #[napi_sym::napi_sym] fn napi_get_last_error_info( _env: *mut Env, @@ -1542,7 +1734,7 @@ fn napi_get_last_error_info( error_message: std::ptr::null(), engine_reserved: std::ptr::null_mut(), engine_error_code: 0, - status_code: napi_ok, + error_code: napi_ok, }); *error_code = Box::into_raw(err_info); @@ -1556,8 +1748,9 @@ fn napi_get_named_property( utf8_name: *const c_char, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let object = transmute::>(object); + check_env!(env); + let env = unsafe { &mut *env }; + let object = napi_value_unchecked(object); let utf8_name = std::ffi::CStr::from_ptr(utf8_name); let name = v8::String::new(&mut env.scope(), &utf8_name.to_string_lossy()).unwrap(); @@ -1583,8 +1776,9 @@ fn napi_get_new_target( #[napi_sym::napi_sym] fn napi_get_null(env: *mut Env, result: *mut napi_value) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::null(env.isolate()).into(); Ok(()) } @@ -1596,9 +1790,10 @@ fn napi_get_property( key: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let object = transmute::>(object); - let key = transmute::>(key); + let key = napi_value_unchecked(key); let value: v8::Local = object.get(&mut env.scope(), key).unwrap(); *result = value.into(); Ok(()) @@ -1610,8 +1805,9 @@ fn napi_get_property_names( object: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let object = transmute::>(object); + check_env!(env); + let env = unsafe { &mut *env }; + let object = napi_value_unchecked(object); let array: v8::Local = object .to_object(&mut env.scope()) .unwrap() @@ -1628,8 +1824,9 @@ fn napi_get_prototype( value: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let obj = value.to_object(&mut env.scope()).unwrap(); let proto = obj.get_prototype(&mut env.scope()).unwrap(); *result = proto.into(); @@ -1660,8 +1857,9 @@ fn napi_get_typedarray_info( arraybuffer: *mut napi_value, byte_offset: *mut usize, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(typedarray); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(typedarray); let array = v8::Local::::try_from(value) .ok() .ok_or(Error::InvalidArg)?; @@ -1711,8 +1909,9 @@ fn napi_get_typedarray_info( #[napi_sym::napi_sym] fn napi_get_undefined(env: *mut Env, result: *mut napi_value) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg!(result); + check_env!(env); + check_arg!(env, result); + let env = unsafe { &mut *env }; *result = v8::undefined(env.isolate()).into(); Ok(()) } @@ -1732,8 +1931,9 @@ fn napi_has_element( index: u32, result: *mut bool, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let obj = value.to_object(&mut env.scope()).unwrap(); *result = obj.has_index(&mut env.scope(), index).unwrap_or(false); Ok(()) @@ -1746,8 +1946,9 @@ fn napi_has_named_property( key: *const c_char, result: *mut bool, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let obj = value.to_object(&mut env.scope()).unwrap(); let key = CStr::from_ptr(key).to_str().unwrap(); let key = v8::String::new(&mut env.scope(), key).unwrap(); @@ -1762,9 +1963,10 @@ fn napi_has_own_property( key: napi_value, result: *mut bool, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg_option!(key); - check_arg!(result); + check_env!(env); + let env = unsafe { &mut *env }; + check_arg_option!(env, key); + check_arg!(env, result); let scope = &mut env.scope(); let object = object @@ -1791,9 +1993,10 @@ fn napi_has_property( key: napi_value, result: *mut bool, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg_option!(key); - check_arg!(result); + check_env!(env); + let env = unsafe { &mut *env }; + check_arg_option!(env, key); + check_arg!(env, result); let scope = &mut env.scope(); let object = object @@ -1814,12 +2017,13 @@ fn napi_instanceof( constructor: napi_value, result: *mut bool, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg_option!(constructor); - check_arg_option!(value); + check_env!(env); + let env = unsafe { &mut *env }; + check_arg_option!(env, constructor); + check_arg_option!(env, value); - let value = transmute::>(value); - let constructor = transmute::>(constructor); + let value = napi_value_unchecked(value); + let constructor = napi_value_unchecked(constructor); let ctor = constructor .to_object(&mut env.scope()) .ok_or(Error::ObjectExpected)?; @@ -1842,7 +2046,7 @@ fn napi_is_array( value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); *result = value.is_array(); Ok(()) } @@ -1853,7 +2057,7 @@ fn napi_is_arraybuffer( value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); *result = value.is_array_buffer(); Ok(()) } @@ -1864,7 +2068,7 @@ fn napi_is_buffer( value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); // TODO: should we assume Buffer as Uint8Array in Deno? // or use std/node polyfill? *result = value.is_typed_array(); @@ -1877,7 +2081,7 @@ fn napi_is_dataview( value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); *result = value.is_data_view(); Ok(()) } @@ -1888,7 +2092,7 @@ fn napi_is_date( value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); *result = value.is_date(); Ok(()) } @@ -1899,7 +2103,7 @@ fn napi_is_detached_arraybuffer( value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); let _ab = v8::Local::::try_from(value).unwrap(); *result = _ab.was_detached(); Ok(()) @@ -1907,13 +2111,20 @@ fn napi_is_detached_arraybuffer( #[napi_sym::napi_sym] fn napi_is_error( - _env: *mut Env, + env: *mut Env, value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); - // TODO - *result = value.is_object(); + { + // TODO(bartlomieju): add `check_env!` macro? + let _env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + value.ok_or(Error::InvalidArg)?; + check_arg!(env, result); + + let value = napi_value_unchecked(value); + *result = value.is_native_error(); + } + napi_clear_last_error(env); Ok(()) } @@ -1931,7 +2142,7 @@ fn napi_is_promise( value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); *result = value.is_promise(); Ok(()) } @@ -1942,7 +2153,7 @@ fn napi_is_typedarray( value: napi_value, result: *mut bool, ) -> Result { - let value = transmute::>(value); + let value = napi_value_unchecked(value); *result = value.is_typed_array(); Ok(()) } @@ -1955,8 +2166,9 @@ fn napi_new_instance( argv: *const napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let constructor = transmute::>(constructor); + check_env!(env); + let env = unsafe { &mut *env }; + let constructor = napi_value_unchecked(constructor); let constructor = v8::Local::::try_from(constructor).unwrap(); let args: &[v8::Local] = transmute(std::slice::from_raw_parts(argv, argc)); @@ -2029,7 +2241,8 @@ fn napi_reject_deferred( deferred: napi_deferred, error: napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let deferred_ptr = NonNull::new_unchecked(deferred as *mut v8::PromiseResolver); @@ -2042,18 +2255,16 @@ fn napi_reject_deferred( v8::Local, >(deferred_ptr); resolver - .reject( - &mut env.scope(), - transmute::>(error), - ) + .reject(&mut env.scope(), napi_value_unchecked(error)) .unwrap(); Ok(()) } #[napi_sym::napi_sym] fn napi_remove_wrap(env: *mut Env, value: napi_value) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let obj = value.to_object(&mut env.scope()).unwrap(); let shared = &*(env.shared as *const EnvShared); let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap); @@ -2067,7 +2278,8 @@ fn napi_resolve_deferred( deferred: napi_deferred, result: napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let deferred_ptr = NonNull::new_unchecked(deferred as *mut v8::PromiseResolver); // TODO(@littledivy): Use Global::from_raw instead casting to local. @@ -2079,10 +2291,7 @@ fn napi_resolve_deferred( v8::Local, >(deferred_ptr); resolver - .resolve( - &mut env.scope(), - transmute::>(result), - ) + .resolve(&mut env.scope(), napi_value_unchecked(result)) .unwrap(); Ok(()) } @@ -2093,10 +2302,13 @@ fn napi_run_script( script: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; - let script = transmute::>(script); + let script = napi_value_unchecked(script); if !script.is_string() { + // TODO: + // napi_set_last_error return Err(Error::StringExpected); } let script = script.to_string(&mut env.scope()).unwrap(); @@ -2124,10 +2336,11 @@ fn napi_set_element( index: u32, value: napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let object = transmute::>(object); + check_env!(env); + let env = unsafe { &mut *env }; + let object = napi_value_unchecked(object); let array = v8::Local::::try_from(object).unwrap(); - let value = transmute::>(value); + let value = napi_value_unchecked(value); array.set_index(&mut env.scope(), index, value).unwrap(); Ok(()) } @@ -2158,10 +2371,11 @@ fn napi_set_named_property( name: *const c_char, value: napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; let name = CStr::from_ptr(name).to_str().unwrap(); let object = transmute::>(object); - let value = transmute::>(value); + let value = napi_value_unchecked(value); let name = v8::String::new(&mut env.scope(), name).unwrap(); object.set(&mut env.scope(), name.into(), value).unwrap(); Ok(()) @@ -2174,9 +2388,10 @@ fn napi_set_property( key: napi_value, value: napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg_option!(key); - check_arg_option!(value); + check_env!(env); + let env = unsafe { &mut *env }; + check_arg_option!(env, key); + check_arg_option!(env, value); let scope = &mut env.scope(); let object = object @@ -2199,8 +2414,8 @@ fn napi_strict_equals( result: *mut bool, ) -> Result { let _: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg_option!(lhs); - check_arg_option!(rhs); + check_arg_option!(env, lhs); + check_arg_option!(env, rhs); *result = lhs.unwrap_unchecked().strict_equals(rhs.unwrap_unchecked()); Ok(()) @@ -2208,8 +2423,9 @@ fn napi_strict_equals( #[napi_sym::napi_sym] fn napi_throw(env: *mut Env, error: napi_value) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let error = transmute::>(error); + check_env!(env); + let env = unsafe { &mut *env }; + let error = napi_value_unchecked(error); env.scope().throw_exception(error); Ok(()) } @@ -2217,60 +2433,86 @@ fn napi_throw(env: *mut Env, error: napi_value) -> Result { #[napi_sym::napi_sym] fn napi_throw_error( env: *mut Env, - _code: *const c_char, + code: *const c_char, msg: *const c_char, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + // TODO: add preamble here - // let code = CStr::from_ptr(code).to_str().unwrap(); - let msg = CStr::from_ptr(msg).to_str().unwrap(); + { + check_env!(env); + let str_ = check_new_from_utf8(env, msg)?; - // let code = v8::String::new(&mut env.scope(), code).unwrap(); - let msg = v8::String::new(&mut env.scope(), msg).unwrap(); - - let error = v8::Exception::error(&mut env.scope(), msg); - env.scope().throw_exception(error); + let error = { + let env = unsafe { &mut *env }; + let scope = &mut env.scope(); + v8::Exception::error(scope, str_) + }; + set_error_code( + env, + error, + transmute::<*mut (), napi_value>(std::ptr::null_mut()), + code, + )?; + unsafe { &mut *env }.scope().throw_exception(error); + } + napi_clear_last_error(env); Ok(()) } #[napi_sym::napi_sym] fn napi_throw_range_error( env: *mut Env, - _code: *const c_char, + code: *const c_char, msg: *const c_char, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - - // let code = CStr::from_ptr(code).to_str().unwrap(); - let msg = CStr::from_ptr(msg).to_str().unwrap(); - - // let code = v8::String::new(&mut env.scope(), code).unwrap(); - let msg = v8::String::new(&mut env.scope(), msg).unwrap(); - - let error = v8::Exception::range_error(&mut env.scope(), msg); - env.scope().throw_exception(error); + // TODO: add preamble here + { + check_env!(env); + let str_ = check_new_from_utf8(env, msg)?; + let error = { + let env = unsafe { &mut *env }; + let scope = &mut env.scope(); + v8::Exception::range_error(scope, str_) + }; + set_error_code( + env, + error, + transmute::<*mut (), napi_value>(std::ptr::null_mut()), + code, + )?; + unsafe { &mut *env }.scope().throw_exception(error); + } + napi_clear_last_error(env); Ok(()) } #[napi_sym::napi_sym] fn napi_throw_type_error( env: *mut Env, - _code: *const c_char, + code: *const c_char, msg: *const c_char, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - - // let code = CStr::from_ptr(code).to_str().unwrap(); - let msg = CStr::from_ptr(msg).to_str().unwrap(); - - // let code = v8::String::new(&mut env.scope(), code).unwrap(); - let msg = v8::String::new(&mut env.scope(), msg).unwrap(); - - let error = v8::Exception::type_error(&mut env.scope(), msg); - env.scope().throw_exception(error); + // TODO: add preamble here + { + check_env!(env); + let str_ = check_new_from_utf8(env, msg)?; + let error = { + let env = unsafe { &mut *env }; + let scope = &mut env.scope(); + v8::Exception::type_error(scope, str_) + }; + set_error_code( + env, + error, + transmute::<*mut (), napi_value>(std::ptr::null_mut()), + code, + )?; + unsafe { &mut *env }.scope().throw_exception(error); + } + napi_clear_last_error(env); Ok(()) } @@ -2306,9 +2548,9 @@ fn napi_typeof( value: napi_value, result: *mut napi_valuetype, ) -> Result { - let _: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - check_arg_option!(value); - check_arg!(result); + check_env!(env); + check_arg_option!(env, value); + check_arg!(env, result); match get_value_type(value.unwrap()) { Some(ty) => { *result = ty; @@ -2327,8 +2569,9 @@ fn napi_unwrap( value: napi_value, result: *mut *mut c_void, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let obj = value.to_object(&mut env.scope()).unwrap(); let shared = &*(env.shared as *const EnvShared); let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap); @@ -2346,8 +2589,9 @@ fn napi_wrap( value: napi_value, native_object: *mut c_void, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; - let value = transmute::>(value); + check_env!(env); + let env = unsafe { &mut *env }; + let value = napi_value_unchecked(value); let obj = value.to_object(&mut env.scope()).unwrap(); let shared = &*(env.shared as *const EnvShared); let napi_wrap = v8::Local::new(&mut env.scope(), &shared.napi_wrap); @@ -2362,7 +2606,8 @@ fn node_api_throw_syntax_error( _code: *const c_char, msg: *const c_char, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; // let code = CStr::from_ptr(code).to_str().unwrap(); let msg = CStr::from_ptr(msg).to_str().unwrap(); @@ -2383,10 +2628,11 @@ fn node_api_create_syntax_error( msg: napi_value, result: *mut napi_value, ) -> Result { - let env: &mut Env = env.as_mut().ok_or(Error::InvalidArg)?; + check_env!(env); + let env = unsafe { &mut *env }; - // let code = transmute::>(code); - let msg = transmute::>(msg); + // let code = napi_value_unchecked(code); + let msg = napi_value_unchecked(msg); let msg = msg.to_string(&mut env.scope()).unwrap(); diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index 22b0e3a088..1cb1b7c26b 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -311,7 +311,7 @@ pub struct napi_extended_error_info { pub error_message: *const c_char, pub engine_reserved: *mut c_void, pub engine_error_code: i32, - pub status_code: napi_status, + pub error_code: napi_status, } #[repr(C)] @@ -416,6 +416,7 @@ pub struct Env { pub cleanup_hooks: Rc>>, pub tsfn_ref_counters: Rc>, + pub last_error: napi_extended_error_info, } unsafe impl Send for Env {} @@ -450,6 +451,12 @@ impl Env { threadsafe_function_sender, cleanup_hooks, tsfn_ref_counters, + last_error: napi_extended_error_info { + error_message: std::ptr::null(), + engine_reserved: std::ptr::null_mut(), + engine_error_code: 0, + error_code: napi_ok, + }, } } diff --git a/test_napi/common.js b/test_napi/common.js index d1db4e9bfa..23f58c60c3 100644 --- a/test_napi/common.js +++ b/test_napi/common.js @@ -4,6 +4,7 @@ export { assert, assertEquals, assertRejects, + assertThrows, } from "../test_util/std/testing/asserts.ts"; export { fromFileUrl } from "../test_util/std/path/mod.ts"; diff --git a/test_napi/error_test.js b/test_napi/error_test.js new file mode 100644 index 0000000000..884a226a47 --- /dev/null +++ b/test_napi/error_test.js @@ -0,0 +1,215 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { + assert, + assertEquals, + assertThrows, + loadTestLibrary, +} from "./common.js"; + +const testError = loadTestLibrary(); + +const theError = new Error("Some error"); +const theTypeError = new TypeError("Some type error"); +const theSyntaxError = new SyntaxError("Some syntax error"); +const theRangeError = new RangeError("Some type error"); +const theReferenceError = new ReferenceError("Some reference error"); +const theURIError = new URIError("Some URI error"); +const theEvalError = new EvalError("Some eval error"); + +function assertThrowsWithCode(fn, value) { + let thrown = false; + + try { + fn(); + } catch (e) { + thrown = true; + assertEquals(e.message, value.message); + assertEquals(e.code, value.code); + } finally { + assert(thrown); + } +} + +Deno.test("napi error", function () { + class MyError extends Error {} + const myError = new MyError("Some MyError"); + + // Test that native error object is correctly classed + assertEquals(testError.checkError(theError), true); + + // Test that native type error object is correctly classed + assertEquals(testError.checkError(theTypeError), true); + + // Test that native syntax error object is correctly classed + assertEquals(testError.checkError(theSyntaxError), true); + + // Test that native range error object is correctly classed + assertEquals(testError.checkError(theRangeError), true); + + // Test that native reference error object is correctly classed + assertEquals(testError.checkError(theReferenceError), true); + + // Test that native URI error object is correctly classed + assertEquals(testError.checkError(theURIError), true); + + // Test that native eval error object is correctly classed + assertEquals(testError.checkError(theEvalError), true); + + // Test that class derived from native error is correctly classed + assertEquals(testError.checkError(myError), true); + + // Test that non-error object is correctly classed + assertEquals(testError.checkError({}), false); + + // Test that non-error primitive is correctly classed + assertEquals(testError.checkError("non-object"), false); + + assertThrows( + () => { + testError.throwExistingError(); + }, + Error, + "existing error", + ); + + assertThrows( + () => { + testError.throwError(); + }, + Error, + "error", + ); + + assertThrows( + () => { + testError.throwRangeError(); + }, + RangeError, + "range error", + ); + + assertThrows( + () => { + testError.throwTypeError(); + }, + TypeError, + "type error", + ); + + // assertThrows(() => { + // testError.throwSyntaxError(); + // }, "SyntaxError: syntax error"); + + [42, {}, [], Symbol("xyzzy"), true, "ball", undefined, null, NaN] + .forEach((value) => { + let thrown = false; + + try { + testError.throwArbitrary(value); + } catch (e) { + thrown = true; + assertEquals(e, value); + } finally { + assert(thrown); + } + }); + + assertThrowsWithCode( + () => testError.throwErrorCode(), + { + code: "ERR_TEST_CODE", + message: "Error [error]", + }, + ); + + assertThrowsWithCode( + () => testError.throwRangeErrorCode(), + { + code: "ERR_TEST_CODE", + message: "RangeError [range error]", + }, + ); + + assertThrowsWithCode( + () => testError.throwTypeErrorCode(), + { + code: "ERR_TEST_CODE", + message: "TypeError [type error]", + }, + ); + + // assertThrowsWithCode( + // () => testError.throwSyntaxErrorCode(), + // { + // code: "ERR_TEST_CODE", + // message: "SyntaxError [syntax error]", + // }, + // ); + + let error = testError.createError(); + assert( + error instanceof Error, + "expected error to be an instance of Error", + ); + assertEquals(error.message, "error"); + + error = testError.createRangeError(); + assert( + error instanceof RangeError, + "expected error to be an instance of RangeError", + ); + assertEquals(error.message, "range error"); + + error = testError.createTypeError(); + assert( + error instanceof TypeError, + "expected error to be an instance of TypeError", + ); + assertEquals(error.message, "type error"); + + // TODO(bartlomieju): this is experimental API + // error = testError.createSyntaxError(); + // assert( + // error instanceof SyntaxError, + // "expected error to be an instance of SyntaxError", + // ); + // assertEquals(error.message, "syntax error"); + + error = testError.createErrorCode(); + assert( + error instanceof Error, + "expected error to be an instance of Error", + ); + assertEquals(error.code, "ERR_TEST_CODE"); + assertEquals(error.message, "Error [error]"); + assertEquals(error.name, "Error"); + + error = testError.createRangeErrorCode(); + assert( + error instanceof RangeError, + "expected error to be an instance of RangeError", + ); + assertEquals(error.message, "RangeError [range error]"); + assertEquals(error.code, "ERR_TEST_CODE"); + assertEquals(error.name, "RangeError"); + + error = testError.createTypeErrorCode(); + assert( + error instanceof TypeError, + "expected error to be an instance of TypeError", + ); + assertEquals(error.message, "TypeError [type error]"); + assertEquals(error.code, "ERR_TEST_CODE"); + assertEquals(error.name, "TypeError"); + + // TODO(bartlomieju): this is experimental API + // error = testError.createSyntaxErrorCode(); + // assert( + // error instanceof SyntaxError, + // "expected error to be an instance of SyntaxError", + // ); + // assertEquals(error.message, "SyntaxError [syntax error]"); + // assertEquals(error.code, "ERR_TEST_CODE"); + // assertEquals(error.name, "SyntaxError"); +}); diff --git a/test_napi/src/error.rs b/test_napi/src/error.rs new file mode 100644 index 0000000000..bbbbab46f2 --- /dev/null +++ b/test_napi/src/error.rs @@ -0,0 +1,288 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use crate::assert_napi_ok; +use crate::cstr; +use crate::napi_get_callback_info; +use crate::napi_new_property; +use napi_sys::*; +use std::ptr; + +extern "C" fn check_error( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + let mut r = false; + assert_napi_ok!(napi_is_error(env, args[0], &mut r)); + let mut result: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_get_boolean(env, r, &mut result)); + result +} + +extern "C" fn create_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("error"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_error( + env, + ptr::null_mut(), + message, + &mut result + )); + result +} + +extern "C" fn create_range_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("range error"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_range_error( + env, + ptr::null_mut(), + message, + &mut result + )); + result +} + +extern "C" fn create_type_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("type error"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_type_error( + env, + ptr::null_mut(), + message, + &mut result + )); + result +} + +extern "C" fn create_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + let mut code: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("Error [error]"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("ERR_TEST_CODE"), + usize::MAX, + &mut code + )); + assert_napi_ok!(napi_create_error(env, code, message, &mut result)); + result +} + +extern "C" fn create_range_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + let mut code: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("RangeError [range error]"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("ERR_TEST_CODE"), + usize::MAX, + &mut code + )); + assert_napi_ok!(napi_create_range_error(env, code, message, &mut result)); + result +} + +extern "C" fn create_type_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut result: napi_value = ptr::null_mut(); + let mut message: napi_value = ptr::null_mut(); + let mut code: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("TypeError [type error]"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("ERR_TEST_CODE"), + usize::MAX, + &mut code + )); + assert_napi_ok!(napi_create_type_error(env, code, message, &mut result)); + result +} + +extern "C" fn throw_existing_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut message: napi_value = ptr::null_mut(); + let mut error: napi_value = ptr::null_mut(); + assert_napi_ok!(napi_create_string_utf8( + env, + cstr!("existing error"), + usize::MAX, + &mut message + )); + assert_napi_ok!(napi_create_error( + env, + std::ptr::null_mut(), + message, + &mut error + )); + assert_napi_ok!(napi_throw(env, error)); + std::ptr::null_mut() +} + +extern "C" fn throw_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_error(env, std::ptr::null_mut(), cstr!("error"),)); + std::ptr::null_mut() +} + +extern "C" fn throw_range_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_range_error( + env, + std::ptr::null_mut(), + cstr!("range error"), + )); + std::ptr::null_mut() +} + +extern "C" fn throw_type_error( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_type_error( + env, + std::ptr::null_mut(), + cstr!("type error"), + )); + std::ptr::null_mut() +} + +extern "C" fn throw_arbitrary( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = napi_get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + assert_napi_ok!(napi_throw(env, args[0])); + std::ptr::null_mut() +} + +extern "C" fn throw_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_error( + env, + cstr!("ERR_TEST_CODE"), + cstr!("Error [error]"), + )); + std::ptr::null_mut() +} + +extern "C" fn throw_range_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_range_error( + env, + cstr!("ERR_TEST_CODE"), + cstr!("RangeError [range error]"), + )); + std::ptr::null_mut() +} + +extern "C" fn throw_type_error_code( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + assert_napi_ok!(napi_throw_type_error( + env, + cstr!("ERR_TEST_CODE"), + cstr!("TypeError [type error]"), + )); + std::ptr::null_mut() +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + napi_new_property!(env, "checkError", check_error), + napi_new_property!(env, "throwExistingError", throw_existing_error), + napi_new_property!(env, "throwError", throw_error), + napi_new_property!(env, "throwRangeError", throw_range_error), + napi_new_property!(env, "throwTypeError", throw_type_error), + // NOTE(bartlomieju): currently experimental api + // napi_new_property!(env, "throwSyntaxError", throw_syntax_error), + napi_new_property!(env, "throwErrorCode", throw_error_code), + napi_new_property!(env, "throwRangeErrorCode", throw_range_error_code), + napi_new_property!(env, "throwTypeErrorCode", throw_type_error_code), + // NOTE(bartlomieju): currently experimental api + // napi_new_property!(env, "throwSyntaxErrorCode", throw_syntax_error_code), + napi_new_property!(env, "throwArbitrary", throw_arbitrary), + napi_new_property!(env, "createError", create_error), + napi_new_property!(env, "createRangeError", create_range_error), + napi_new_property!(env, "createTypeError", create_type_error), + // NOTE(bartlomieju): currently experimental api + // napi_new_property!(env, "createSyntaxError", create_syntax_error), + napi_new_property!(env, "createErrorCode", create_error_code), + napi_new_property!(env, "createRangeErrorCode", create_range_error_code), + napi_new_property!(env, "createTypeErrorCode", create_type_error_code), + // NOTE(bartlomieju): currently experimental api + // napi_new_property!(env, "createSyntaxErrorCode", create_syntax_error_code), + ]; + + assert_napi_ok!(napi_define_properties( + env, + exports, + properties.len(), + properties.as_ptr() + )); +} diff --git a/test_napi/src/lib.rs b/test_napi/src/lib.rs index b54b3886b8..c02e53da46 100644 --- a/test_napi/src/lib.rs +++ b/test_napi/src/lib.rs @@ -12,6 +12,7 @@ pub mod r#async; pub mod callback; pub mod coerce; pub mod date; +pub mod error; pub mod numbers; pub mod object_wrap; pub mod primitives; @@ -21,6 +22,13 @@ pub mod strings; pub mod tsfn; pub mod typedarray; +#[macro_export] +macro_rules! cstr { + ($s: literal) => {{ + std::ffi::CString::new($s).unwrap().as_ptr() + }}; +} + #[macro_export] macro_rules! assert_napi_ok { ($call: expr) => {{ @@ -127,6 +135,7 @@ unsafe extern "C" fn napi_register_module_v1( typedarray::init(env, exports); arraybuffer::init(env, exports); array::init(env, exports); + error::init(env, exports); primitives::init(env, exports); properties::init(env, exports); promise::init(env, exports);