From 279107629834ec19b61a81c1674139405099e4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 26 Nov 2020 10:56:32 +0100 Subject: [PATCH 1/7] Upgrade neon-runtime to 2018 edition --- crates/neon-runtime/Cargo.toml | 1 + crates/neon-runtime/src/lib.rs | 6 +----- crates/neon-runtime/src/nan/error.rs | 2 +- crates/neon-runtime/src/nan/scope.rs | 6 +++--- crates/neon-runtime/src/napi/array.rs | 2 +- crates/neon-runtime/src/napi/arraybuffer.rs | 2 +- crates/neon-runtime/src/napi/buffer.rs | 2 +- crates/neon-runtime/src/napi/call.rs | 2 +- crates/neon-runtime/src/napi/class.rs | 4 ++-- crates/neon-runtime/src/napi/convert.rs | 2 +- crates/neon-runtime/src/napi/error.rs | 2 +- crates/neon-runtime/src/napi/external.rs | 2 +- crates/neon-runtime/src/napi/fun.rs | 4 ++-- crates/neon-runtime/src/napi/handler.rs | 2 +- crates/neon-runtime/src/napi/mem.rs | 2 +- crates/neon-runtime/src/napi/object.rs | 2 +- crates/neon-runtime/src/napi/primitive.rs | 2 +- crates/neon-runtime/src/napi/scope.rs | 2 +- crates/neon-runtime/src/napi/string.rs | 2 +- crates/neon-runtime/src/napi/tag.rs | 2 +- crates/neon-runtime/src/napi/task.rs | 2 +- 21 files changed, 25 insertions(+), 28 deletions(-) diff --git a/crates/neon-runtime/Cargo.toml b/crates/neon-runtime/Cargo.toml index bab462595..f03422317 100644 --- a/crates/neon-runtime/Cargo.toml +++ b/crates/neon-runtime/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Dave Herman "] description = "Bindings to the Node.js native addon API, used by the Neon implementation." repository = "https://github.com/neon-bindings/neon" license = "MIT/Apache-2.0" +edition = "2018" [dependencies] cfg-if = "0.1.9" diff --git a/crates/neon-runtime/src/lib.rs b/crates/neon-runtime/src/lib.rs index 49d6d2cc2..428cb4a9c 100644 --- a/crates/neon-runtime/src/lib.rs +++ b/crates/neon-runtime/src/lib.rs @@ -1,6 +1,3 @@ -extern crate cfg_if; -extern crate smallvec; - #[cfg(all(not(feature = "neon-sys"), not(feature = "nodejs-sys")))] compile_error!("The Neon runtime must have at least one of the `neon-sys` or `nodejs-sys` backends enabled."); @@ -8,14 +5,13 @@ use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "nodejs-sys")] { - pub extern crate nodejs_sys; + pub use nodejs_sys; pub mod napi; } } cfg_if! { if #[cfg(feature = "neon-sys")] { - extern crate neon_sys; pub mod nan; // The legacy variant is the default API as long as it's present. pub use nan::*; diff --git a/crates/neon-runtime/src/nan/error.rs b/crates/neon-runtime/src/nan/error.rs index 5113ea1a6..2127c6d1e 100644 --- a/crates/neon-runtime/src/nan/error.rs +++ b/crates/neon-runtime/src/nan/error.rs @@ -1,6 +1,6 @@ //! Facilities for creating and throwing JS errors. -use raw::{Isolate, Local}; +use crate::raw::{Isolate, Local}; /// Throws an `Error` object in the current context. pub unsafe fn throw(_: Isolate, val: Local) { diff --git a/crates/neon-runtime/src/nan/scope.rs b/crates/neon-runtime/src/nan/scope.rs index 3c4c20a1b..44e5e182f 100644 --- a/crates/neon-runtime/src/nan/scope.rs +++ b/crates/neon-runtime/src/nan/scope.rs @@ -1,11 +1,11 @@ //! Facilities for working with `v8::HandleScope`s and `v8::EscapableHandleScope`s. -use raw::{HandleScope, EscapableHandleScope, InheritedHandleScope, Isolate}; +use crate::raw::{HandleScope, EscapableHandleScope, InheritedHandleScope, Isolate}; pub trait Root { unsafe fn allocate() -> Self; - unsafe fn enter(&mut self, Isolate); - unsafe fn exit(&mut self, Isolate); + unsafe fn enter(&mut self, isolate: Isolate); + unsafe fn exit(&mut self, isolate: Isolate); } impl Root for HandleScope { diff --git a/crates/neon-runtime/src/napi/array.rs b/crates/neon-runtime/src/napi/array.rs index 59cac11ef..f2411f95e 100644 --- a/crates/neon-runtime/src/napi/array.rs +++ b/crates/neon-runtime/src/napi/array.rs @@ -1,6 +1,6 @@ //! Facilities for working with Array `napi_value`s. -use raw::{Env, Local}; +use crate::raw::{Env, Local}; use nodejs_sys as napi; diff --git a/crates/neon-runtime/src/napi/arraybuffer.rs b/crates/neon-runtime/src/napi/arraybuffer.rs index 09c53b14a..b09352cbb 100644 --- a/crates/neon-runtime/src/napi/arraybuffer.rs +++ b/crates/neon-runtime/src/napi/arraybuffer.rs @@ -1,4 +1,4 @@ -use raw::{Env, Local}; +use crate::raw::{Env, Local}; use std::os::raw::c_void; use std::ptr::null_mut; diff --git a/crates/neon-runtime/src/napi/buffer.rs b/crates/neon-runtime/src/napi/buffer.rs index 60b6d7e96..29f660789 100644 --- a/crates/neon-runtime/src/napi/buffer.rs +++ b/crates/neon-runtime/src/napi/buffer.rs @@ -1,4 +1,4 @@ -use raw::{Env, Local}; +use crate::raw::{Env, Local}; use std::os::raw::c_void; use std::ptr::null_mut; diff --git a/crates/neon-runtime/src/napi/call.rs b/crates/neon-runtime/src/napi/call.rs index b21484d43..8007178b6 100644 --- a/crates/neon-runtime/src/napi/call.rs +++ b/crates/neon-runtime/src/napi/call.rs @@ -1,7 +1,7 @@ use std::mem::MaybeUninit; use std::os::raw::c_void; use std::ptr::null_mut; -use raw::{FunctionCallbackInfo, Env, Local}; +use crate::raw::{FunctionCallbackInfo, Env, Local}; use smallvec::{smallvec, SmallVec}; use nodejs_sys as napi; diff --git a/crates/neon-runtime/src/napi/class.rs b/crates/neon-runtime/src/napi/class.rs index e522a6041..9e7244075 100644 --- a/crates/neon-runtime/src/napi/class.rs +++ b/crates/neon-runtime/src/napi/class.rs @@ -1,6 +1,6 @@ use std::os::raw::c_void; -use call::CCallback; -use raw::{Env, Local}; +use crate::call::CCallback; +use crate::raw::{Env, Local}; pub unsafe extern "C" fn get_class_map(_isolate: Env) -> *mut c_void { unimplemented!() } diff --git a/crates/neon-runtime/src/napi/convert.rs b/crates/neon-runtime/src/napi/convert.rs index 17c35dd0f..585a1ee42 100644 --- a/crates/neon-runtime/src/napi/convert.rs +++ b/crates/neon-runtime/src/napi/convert.rs @@ -1,6 +1,6 @@ use nodejs_sys as napi; -use raw::{Env, Local}; +use crate::raw::{Env, Local}; /// This API is currently unused, see https://github.com/neon-bindings/neon/issues/572 pub unsafe extern "C" fn to_object(out: &mut Local, env: Env, value: Local) -> bool { diff --git a/crates/neon-runtime/src/napi/error.rs b/crates/neon-runtime/src/napi/error.rs index 92b65bb10..dd95a592d 100644 --- a/crates/neon-runtime/src/napi/error.rs +++ b/crates/neon-runtime/src/napi/error.rs @@ -3,7 +3,7 @@ use std::ptr; use nodejs_sys as napi; -use raw::{Env, Local}; +use crate::raw::{Env, Local}; pub unsafe fn is_throwing(env: Env) -> bool { let mut b: MaybeUninit = MaybeUninit::zeroed(); diff --git a/crates/neon-runtime/src/napi/external.rs b/crates/neon-runtime/src/napi/external.rs index 736e6bee7..1882a3771 100644 --- a/crates/neon-runtime/src/napi/external.rs +++ b/crates/neon-runtime/src/napi/external.rs @@ -1,6 +1,6 @@ use std::mem::MaybeUninit; -use raw::{Env, Local}; +use crate::raw::{Env, Local}; use nodejs_sys as napi; diff --git a/crates/neon-runtime/src/napi/fun.rs b/crates/neon-runtime/src/napi/fun.rs index 9317ed862..bdce0c012 100644 --- a/crates/neon-runtime/src/napi/fun.rs +++ b/crates/neon-runtime/src/napi/fun.rs @@ -1,7 +1,7 @@ //! Facilities for working with JS functions. -use call::CCallback; -use raw::{Env, Local}; +use crate::call::CCallback; +use crate::raw::{Env, Local}; use std::os::raw::c_void; use std::ptr::null; diff --git a/crates/neon-runtime/src/napi/handler.rs b/crates/neon-runtime/src/napi/handler.rs index 13b108c86..dc2f9546a 100644 --- a/crates/neon-runtime/src/napi/handler.rs +++ b/crates/neon-runtime/src/napi/handler.rs @@ -1,4 +1,4 @@ -use raw::Local; +use crate::raw::Local; use std::os::raw::c_void; pub unsafe extern "C" fn new(_isolate: *mut c_void, _this: Local, _callback: Local) -> *mut c_void { unimplemented!() } diff --git a/crates/neon-runtime/src/napi/mem.rs b/crates/neon-runtime/src/napi/mem.rs index 6d48b7923..95ae6a386 100644 --- a/crates/neon-runtime/src/napi/mem.rs +++ b/crates/neon-runtime/src/napi/mem.rs @@ -1,3 +1,3 @@ -use raw::Local; +use crate::raw::Local; pub unsafe extern "C" fn same_handle(_h1: Local, _h2: Local) -> bool { unimplemented!() } diff --git a/crates/neon-runtime/src/napi/object.rs b/crates/neon-runtime/src/napi/object.rs index a8874b600..171a5493d 100644 --- a/crates/neon-runtime/src/napi/object.rs +++ b/crates/neon-runtime/src/napi/object.rs @@ -2,7 +2,7 @@ use std::mem::MaybeUninit; use nodejs_sys as napi; -use raw::{Env, Local}; +use crate::raw::{Env, Local}; /// Mutates the `out` argument to refer to a `napi_value` containing a newly created JavaScript Object. pub unsafe extern "C" fn new(out: &mut Local, env: Env) { diff --git a/crates/neon-runtime/src/napi/primitive.rs b/crates/neon-runtime/src/napi/primitive.rs index c951096ce..904d47ac5 100644 --- a/crates/neon-runtime/src/napi/primitive.rs +++ b/crates/neon-runtime/src/napi/primitive.rs @@ -1,4 +1,4 @@ -use raw::{Local, Env}; +use crate::raw::{Local, Env}; use nodejs_sys as napi; diff --git a/crates/neon-runtime/src/napi/scope.rs b/crates/neon-runtime/src/napi/scope.rs index ae29f76d0..c3ca58d50 100644 --- a/crates/neon-runtime/src/napi/scope.rs +++ b/crates/neon-runtime/src/napi/scope.rs @@ -3,7 +3,7 @@ use std::mem::MaybeUninit; use nodejs_sys as napi; -use raw::{Env, HandleScope, EscapableHandleScope, InheritedHandleScope}; +use crate::raw::{Env, HandleScope, EscapableHandleScope, InheritedHandleScope}; type Local = napi::napi_value; diff --git a/crates/neon-runtime/src/napi/string.rs b/crates/neon-runtime/src/napi/string.rs index 56059489a..2f496c4a4 100644 --- a/crates/neon-runtime/src/napi/string.rs +++ b/crates/neon-runtime/src/napi/string.rs @@ -3,7 +3,7 @@ use std::ptr; use nodejs_sys as napi; -use raw::{Env, Local}; +use crate::raw::{Env, Local}; pub unsafe fn new(out: &mut Local, env: Env, data: *const u8, len: i32) -> bool { let status = napi::napi_create_string_utf8( diff --git a/crates/neon-runtime/src/napi/tag.rs b/crates/neon-runtime/src/napi/tag.rs index a2babaa90..fd2e6bd41 100644 --- a/crates/neon-runtime/src/napi/tag.rs +++ b/crates/neon-runtime/src/napi/tag.rs @@ -1,4 +1,4 @@ -use raw::{Env, Local}; +use crate::raw::{Env, Local}; use nodejs_sys as napi; diff --git a/crates/neon-runtime/src/napi/task.rs b/crates/neon-runtime/src/napi/task.rs index daea51998..a784df4a0 100644 --- a/crates/neon-runtime/src/napi/task.rs +++ b/crates/neon-runtime/src/napi/task.rs @@ -1,4 +1,4 @@ -use raw::Local; +use crate::raw::Local; use std::os::raw::c_void; pub unsafe extern "C" fn schedule(_task: *mut c_void, From d7dd0fa12cb2cf016adf05ea64d41fad936bf9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 26 Nov 2020 11:29:12 +0100 Subject: [PATCH 2/7] Dynamically load N-API functions for primitives --- Cargo.toml | 2 +- crates/neon-runtime/Cargo.toml | 3 + crates/neon-runtime/src/napi/bindings.rs | 144 ++++++++++++++++++++++ crates/neon-runtime/src/napi/mod.rs | 9 ++ crates/neon-runtime/src/napi/primitive.rs | 33 +++-- src/context/internal.rs | 6 + 6 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 crates/neon-runtime/src/napi/bindings.rs diff --git a/Cargo.toml b/Cargo.toml index 791f3fd47..326552b3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ legacy-runtime = ["neon-runtime/neon-sys", "neon-build/neon-sys"] # Feature flag to enable the experimental N-API runtime. For now, this feature # is disabled by default. -napi-runtime = ["proc-macros", "neon-macros/napi", "neon-runtime/nodejs-sys"] +napi-runtime = ["proc-macros", "neon-macros/napi", "neon-runtime/napi"] # Feature flag to disable external dependencies on docs build docs-only = ["neon-runtime/docs-only"] diff --git a/crates/neon-runtime/Cargo.toml b/crates/neon-runtime/Cargo.toml index f03422317..f54750f23 100644 --- a/crates/neon-runtime/Cargo.toml +++ b/crates/neon-runtime/Cargo.toml @@ -9,6 +9,8 @@ edition = "2018" [dependencies] cfg-if = "0.1.9" +libloading = { version = "0.6.5", optional = true } +lazy_static = { version = "1.4.0", optional = true } neon-sys = { version = "=0.5.3", path = "../neon-sys", optional = true } nodejs-sys = { version = "0.7.0", optional = true } smallvec = "1.4.2" @@ -16,6 +18,7 @@ smallvec = "1.4.2" [features] default = [] docs-only = ["neon-sys/docs-only"] +napi = ["nodejs-sys", "lazy_static", "libloading"] [package.metadata.docs.rs] features = ["docs-only"] diff --git a/crates/neon-runtime/src/napi/bindings.rs b/crates/neon-runtime/src/napi/bindings.rs new file mode 100644 index 000000000..15e9c9759 --- /dev/null +++ b/crates/neon-runtime/src/napi/bindings.rs @@ -0,0 +1,144 @@ +use std::mem::MaybeUninit; +use lazy_static::lazy_static; +use libloading::{Library, Symbol}; + +/* Later we should do: +#[repr(C)] +struct NapiEnvStruct {} + +#[repr(C)] +struct NapiValueStruct {} + +pub(crate) type NapiEnv = *mut NapiEnvStruct; +pub(crate) type NapiValue = *mut NapiValueStruct; + +But in this sample we still rely on nodejs_sys's types +*/ + +pub(crate) type NapiEnv = nodejs_sys::napi_env; +pub(crate) type NapiValue = nodejs_sys::napi_value; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[allow(non_camel_case_types)] +#[allow(dead_code)] +pub(crate) enum NapiStatus { + napi_ok, + napi_invalid_arg, + napi_object_expected, + napi_string_expected, + napi_name_expected, + napi_function_expected, + napi_number_expected, + napi_boolean_expected, + napi_array_expected, + napi_generic_failure, + napi_pending_exception, + napi_cancelled, + napi_escape_called_twice, + napi_handle_scope_mismatch, + napi_callback_scope_mismatch, + napi_queue_full, + napi_closing, + napi_bigint_expected, + napi_date_expected, + napi_arraybuffer_expected, + napi_detachable_arraybuffer_expected, + napi_would_deadlock, +} + +/* Maybe we can make a macro like this: + * + * declare_napi_functions! { + * fn napi_get_undefined(env: NapiEnv, out: *mut NapiValue) -> NapiStatus; + * fn napi_get_null(env: NapiEnv, out: *mut NapiValue) -> NapiStatus; + * + * fn napi_get_boolean(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus; + * fn napi_get_value_bool(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus; + * + * fn napi_create_double(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus; + * fn napi_get_value_double(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus; + * } + * + * I think it would have to be a proc macro, since we need to output those declarations several + * times in different shapes. It would allow generating both the property declarations in the Napi + * struct, and the `library.get()` calls in the `from_host` constructor. It could also generate + * trampoline functions so we don't have to use an `napi!()` macro wrapper for napi function calls: + * + * pub unsafe fn napi_get_undefined(env: NapiEnv, out: *mut NapiValue) -> NapiStatus { + * ((*NAPI).napi_get_undefined)(env, out) + * } + * + * Then our code wouldn't look any different compared to using nodejs-sys. + */ + +pub(crate) struct Napi<'a> { + pub napi_get_undefined: Symbol<'a, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, + pub napi_get_null: Symbol<'a, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, + + pub napi_get_boolean: + Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus>, + pub napi_get_value_bool: + Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus>, + + pub napi_create_double: + Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus>, + pub napi_get_value_double: + Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus>, +} + +#[cfg(not(windows))] +fn get_host_library() -> Library { + use libloading::os::unix::Library; + Library::this().into() +} + +#[cfg(windows)] +fn get_host_library() -> Library { + use libloading::os::windows::Library; + Library::this().into() +} + +lazy_static! { + static ref HOST: Library = get_host_library(); +} + +impl Napi<'_> { + fn try_from_host() -> Result { + let host = &HOST; + + Ok(unsafe { + Self { + napi_get_undefined: host.get(b"napi_get_undefined")?, + napi_get_null: host.get(b"napi_get_null")?, + napi_get_boolean: host.get(b"napi_get_boolean")?, + napi_get_value_bool: host.get(b"napi_get_value_bool")?, + napi_create_double: host.get(b"napi_create_double")?, + napi_get_value_double: host.get(b"napi_get_value_double")?, + } + }) + } + + pub fn from_host() -> Self { + Self::try_from_host().unwrap() + } +} + +pub(crate) static mut NAPI: MaybeUninit = MaybeUninit::uninit(); + +/// Load the N-API symbols we need. +pub(crate) unsafe fn load() { + NAPI.as_mut_ptr().write(Napi::from_host()); +} + +macro_rules! napi { + ( $name:ident ( $($args:expr),* ) ) => { + { + let bindings = $crate::napi::bindings::NAPI.as_ptr(); + let result: $crate::napi::bindings::NapiStatus = ((*bindings).$name)( + $($args),* + ); + result + } + } +} diff --git a/crates/neon-runtime/src/napi/mod.rs b/crates/neon-runtime/src/napi/mod.rs index 70b42b931..67acde2c6 100644 --- a/crates/neon-runtime/src/napi/mod.rs +++ b/crates/neon-runtime/src/napi/mod.rs @@ -1,3 +1,6 @@ +#[macro_use] +pub(crate) mod bindings; + pub mod array; pub mod arraybuffer; pub mod buffer; @@ -16,3 +19,9 @@ pub mod string; pub mod tag; pub mod task; pub mod handler; + +/// # Safety +/// Must only be called once. +pub unsafe fn setup() { + bindings::load(); +} diff --git a/crates/neon-runtime/src/napi/primitive.rs b/crates/neon-runtime/src/napi/primitive.rs index 904d47ac5..aebb9c07b 100644 --- a/crates/neon-runtime/src/napi/primitive.rs +++ b/crates/neon-runtime/src/napi/primitive.rs @@ -1,27 +1,38 @@ use crate::raw::{Local, Env}; - -use nodejs_sys as napi; +use crate::bindings::NapiStatus; /// Mutates the `out` argument provided to refer to the global `undefined` object. pub unsafe extern "C" fn undefined(out: &mut Local, env: Env) { - napi::napi_get_undefined(env, out as *mut Local); + assert_eq!( + napi!(napi_get_undefined(env, out as *mut Local)), + NapiStatus::napi_ok, + ); } /// Mutates the `out` argument provided to refer to the global `null` object. pub unsafe extern "C" fn null(out: &mut Local, env: Env) { - napi::napi_get_null(env, out as *mut Local); + assert_eq!( + napi!(napi_get_null(env, out as *mut Local)), + NapiStatus::napi_ok, + ); } /// Mutates the `out` argument provided to refer to one of the global `true` or `false` objects. pub unsafe extern "C" fn boolean(out: &mut Local, env: Env, b: bool) { - napi::napi_get_boolean(env, b, out as *mut Local); + assert_eq!( + napi!(napi_get_boolean(env, b, out as *mut Local)), + NapiStatus::napi_ok, + ); } /// Get the boolean value out of a `Local` object. If the `Local` object does not contain a /// boolean, this function panics. pub unsafe extern "C" fn boolean_value(env: Env, p: Local) -> bool { let mut value = false; - assert_eq!(napi::napi_get_value_bool(env, p, &mut value as *mut bool), napi::napi_status::napi_ok); + assert_eq!( + napi!(napi_get_value_bool(env, p, &mut value as *mut bool)), + NapiStatus::napi_ok, + ); value } @@ -38,13 +49,19 @@ pub unsafe extern "C" fn integer_value(_p: Local) -> i64 { unimplemented!() } /// Mutates the `out` argument provided to refer to a newly created `Local` containing a /// JavaScript number. pub unsafe extern "C" fn number(out: &mut Local, env: Env, v: f64) { - napi::napi_create_double(env, v, out as *mut Local); + assert_eq!( + napi!(napi_create_double(env, v, out as *mut Local)), + NapiStatus::napi_ok, + ); } /// Gets the underlying value of an `Local` object containing a JavaScript number. Panics if /// the given `Local` is not a number. pub unsafe extern "C" fn number_value(env: Env, p: Local) -> f64 { let mut value = 0.0; - assert_eq!(napi::napi_get_value_double(env, p, &mut value as *mut f64), napi::napi_status::napi_ok); + assert_eq!( + napi!(napi_get_value_double(env, p, &mut value as *mut f64)), + NapiStatus::napi_ok, + ); return value; } diff --git a/src/context/internal.rs b/src/context/internal.rs index 602394357..e1ad1de20 100644 --- a/src/context/internal.rs +++ b/src/context/internal.rs @@ -233,6 +233,12 @@ pub fn initialize_module(exports: Handle, init: fn(ModuleContext) -> N #[cfg(feature = "napi-runtime")] pub fn initialize_module(env: raw::Env, exports: Handle, init: fn(ModuleContext) -> NeonResult<()>) { + // SAFETY: initialize_module() is only called once by Neon during startup, before any user code + // runs. + unsafe { + neon_runtime::setup(); + } + ModuleContext::with(Env(env), exports, |cx| { let _ = init(cx); }); From b3c7acae49a6cd129ef6bf2cf837b7ffcdef4aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 26 Nov 2020 12:00:42 +0100 Subject: [PATCH 3/7] move macro idea into PR description --- crates/neon-runtime/src/napi/bindings.rs | 25 ------------------------ 1 file changed, 25 deletions(-) diff --git a/crates/neon-runtime/src/napi/bindings.rs b/crates/neon-runtime/src/napi/bindings.rs index 15e9c9759..c58a09d4f 100644 --- a/crates/neon-runtime/src/napi/bindings.rs +++ b/crates/neon-runtime/src/napi/bindings.rs @@ -47,31 +47,6 @@ pub(crate) enum NapiStatus { napi_would_deadlock, } -/* Maybe we can make a macro like this: - * - * declare_napi_functions! { - * fn napi_get_undefined(env: NapiEnv, out: *mut NapiValue) -> NapiStatus; - * fn napi_get_null(env: NapiEnv, out: *mut NapiValue) -> NapiStatus; - * - * fn napi_get_boolean(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus; - * fn napi_get_value_bool(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus; - * - * fn napi_create_double(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus; - * fn napi_get_value_double(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus; - * } - * - * I think it would have to be a proc macro, since we need to output those declarations several - * times in different shapes. It would allow generating both the property declarations in the Napi - * struct, and the `library.get()` calls in the `from_host` constructor. It could also generate - * trampoline functions so we don't have to use an `napi!()` macro wrapper for napi function calls: - * - * pub unsafe fn napi_get_undefined(env: NapiEnv, out: *mut NapiValue) -> NapiStatus { - * ((*NAPI).napi_get_undefined)(env, out) - * } - * - * Then our code wouldn't look any different compared to using nodejs-sys. - */ - pub(crate) struct Napi<'a> { pub napi_get_undefined: Symbol<'a, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, pub napi_get_null: Symbol<'a, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, From 63358f5652674a7bc6c30b29f1436d0b3b269e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 27 Nov 2020 14:47:44 +0100 Subject: [PATCH 4/7] add trampoline functions --- crates/neon-runtime/src/napi/bindings.rs | 38 +++++++++++++++++------ crates/neon-runtime/src/napi/primitive.rs | 14 ++++----- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/crates/neon-runtime/src/napi/bindings.rs b/crates/neon-runtime/src/napi/bindings.rs index c58a09d4f..221ca1256 100644 --- a/crates/neon-runtime/src/napi/bindings.rs +++ b/crates/neon-runtime/src/napi/bindings.rs @@ -106,14 +106,32 @@ pub(crate) unsafe fn load() { NAPI.as_mut_ptr().write(Napi::from_host()); } -macro_rules! napi { - ( $name:ident ( $($args:expr),* ) ) => { - { - let bindings = $crate::napi::bindings::NAPI.as_ptr(); - let result: $crate::napi::bindings::NapiStatus = ((*bindings).$name)( - $($args),* - ); - result - } - } +#[inline(always)] +pub(crate) unsafe fn napi_get_undefined(env: NapiEnv, out: *mut NapiValue) -> NapiStatus { + ((*NAPI.as_ptr()).napi_get_undefined)(env, out) +} + +#[inline(always)] +pub(crate) unsafe fn napi_get_null(env: NapiEnv, out: *mut NapiValue) -> NapiStatus { + ((*NAPI.as_ptr()).napi_get_null)(env, out) +} + +#[inline(always)] +pub(crate) unsafe fn napi_get_boolean(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus { + ((*NAPI.as_ptr()).napi_get_boolean)(env, value, out) +} + +#[inline(always)] +pub(crate) unsafe fn napi_get_value_bool(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus { + ((*NAPI.as_ptr()).napi_get_value_bool)(env, value, out) +} + +#[inline(always)] +pub(crate) unsafe fn napi_create_double(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus { + ((*NAPI.as_ptr()).napi_create_double)(env, value, out) +} + +#[inline(always)] +pub(crate) unsafe fn napi_get_value_double(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus { + ((*NAPI.as_ptr()).napi_get_value_double)(env, value, out) } diff --git a/crates/neon-runtime/src/napi/primitive.rs b/crates/neon-runtime/src/napi/primitive.rs index aebb9c07b..e97457b33 100644 --- a/crates/neon-runtime/src/napi/primitive.rs +++ b/crates/neon-runtime/src/napi/primitive.rs @@ -1,10 +1,10 @@ use crate::raw::{Local, Env}; -use crate::bindings::NapiStatus; +use crate::bindings::{self as napi, NapiStatus}; /// Mutates the `out` argument provided to refer to the global `undefined` object. pub unsafe extern "C" fn undefined(out: &mut Local, env: Env) { assert_eq!( - napi!(napi_get_undefined(env, out as *mut Local)), + napi::napi_get_undefined(env, out as *mut Local), NapiStatus::napi_ok, ); } @@ -12,7 +12,7 @@ pub unsafe extern "C" fn undefined(out: &mut Local, env: Env) { /// Mutates the `out` argument provided to refer to the global `null` object. pub unsafe extern "C" fn null(out: &mut Local, env: Env) { assert_eq!( - napi!(napi_get_null(env, out as *mut Local)), + napi::napi_get_null(env, out as *mut Local), NapiStatus::napi_ok, ); } @@ -20,7 +20,7 @@ pub unsafe extern "C" fn null(out: &mut Local, env: Env) { /// Mutates the `out` argument provided to refer to one of the global `true` or `false` objects. pub unsafe extern "C" fn boolean(out: &mut Local, env: Env, b: bool) { assert_eq!( - napi!(napi_get_boolean(env, b, out as *mut Local)), + napi::napi_get_boolean(env, b, out as *mut Local), NapiStatus::napi_ok, ); } @@ -30,7 +30,7 @@ pub unsafe extern "C" fn boolean(out: &mut Local, env: Env, b: bool) { pub unsafe extern "C" fn boolean_value(env: Env, p: Local) -> bool { let mut value = false; assert_eq!( - napi!(napi_get_value_bool(env, p, &mut value as *mut bool)), + napi::napi_get_value_bool(env, p, &mut value as *mut bool), NapiStatus::napi_ok, ); value @@ -50,7 +50,7 @@ pub unsafe extern "C" fn integer_value(_p: Local) -> i64 { unimplemented!() } /// JavaScript number. pub unsafe extern "C" fn number(out: &mut Local, env: Env, v: f64) { assert_eq!( - napi!(napi_create_double(env, v, out as *mut Local)), + napi::napi_create_double(env, v, out as *mut Local), NapiStatus::napi_ok, ); } @@ -60,7 +60,7 @@ pub unsafe extern "C" fn number(out: &mut Local, env: Env, v: f64) { pub unsafe extern "C" fn number_value(env: Env, p: Local) -> f64 { let mut value = 0.0; assert_eq!( - napi!(napi_get_value_double(env, p, &mut value as *mut f64)), + napi::napi_get_value_double(env, p, &mut value as *mut f64), NapiStatus::napi_ok, ); return value; From 39a828ead0f4ac2320796a21326c051e9de998d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 27 Nov 2020 14:50:32 +0100 Subject: [PATCH 5/7] remove lifetime from Napi struct --- crates/neon-runtime/src/napi/bindings.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/neon-runtime/src/napi/bindings.rs b/crates/neon-runtime/src/napi/bindings.rs index 221ca1256..b28c2e1ff 100644 --- a/crates/neon-runtime/src/napi/bindings.rs +++ b/crates/neon-runtime/src/napi/bindings.rs @@ -47,19 +47,19 @@ pub(crate) enum NapiStatus { napi_would_deadlock, } -pub(crate) struct Napi<'a> { - pub napi_get_undefined: Symbol<'a, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, - pub napi_get_null: Symbol<'a, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, +pub(crate) struct Napi { + pub napi_get_undefined: Symbol<'static, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, + pub napi_get_null: Symbol<'static, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, pub napi_get_boolean: - Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus>, + Symbol<'static, unsafe extern "C" fn(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus>, pub napi_get_value_bool: - Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus>, + Symbol<'static, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus>, pub napi_create_double: - Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus>, + Symbol<'static, unsafe extern "C" fn(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus>, pub napi_get_value_double: - Symbol<'a, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus>, + Symbol<'static, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus>, } #[cfg(not(windows))] @@ -78,7 +78,7 @@ lazy_static! { static ref HOST: Library = get_host_library(); } -impl Napi<'_> { +impl Napi { fn try_from_host() -> Result { let host = &HOST; From 6ce7cf0ba8803cbd1c09fb95ae90cbf925102aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 27 Nov 2020 15:04:51 +0100 Subject: [PATCH 6/7] unwrap function pointers --- crates/neon-runtime/Cargo.toml | 3 +- crates/neon-runtime/src/napi/bindings.rs | 38 ++++++++++++------------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/crates/neon-runtime/Cargo.toml b/crates/neon-runtime/Cargo.toml index f54750f23..5104c4ab1 100644 --- a/crates/neon-runtime/Cargo.toml +++ b/crates/neon-runtime/Cargo.toml @@ -10,7 +10,6 @@ edition = "2018" [dependencies] cfg-if = "0.1.9" libloading = { version = "0.6.5", optional = true } -lazy_static = { version = "1.4.0", optional = true } neon-sys = { version = "=0.5.3", path = "../neon-sys", optional = true } nodejs-sys = { version = "0.7.0", optional = true } smallvec = "1.4.2" @@ -18,7 +17,7 @@ smallvec = "1.4.2" [features] default = [] docs-only = ["neon-sys/docs-only"] -napi = ["nodejs-sys", "lazy_static", "libloading"] +napi = ["nodejs-sys", "libloading"] [package.metadata.docs.rs] features = ["docs-only"] diff --git a/crates/neon-runtime/src/napi/bindings.rs b/crates/neon-runtime/src/napi/bindings.rs index b28c2e1ff..0d30468f3 100644 --- a/crates/neon-runtime/src/napi/bindings.rs +++ b/crates/neon-runtime/src/napi/bindings.rs @@ -1,6 +1,5 @@ +use libloading::Library; use std::mem::MaybeUninit; -use lazy_static::lazy_static; -use libloading::{Library, Symbol}; /* Later we should do: #[repr(C)] @@ -48,18 +47,18 @@ pub(crate) enum NapiStatus { } pub(crate) struct Napi { - pub napi_get_undefined: Symbol<'static, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, - pub napi_get_null: Symbol<'static, unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus>, + pub napi_get_undefined: unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus, + pub napi_get_null: unsafe extern "C" fn(env: NapiEnv, out: *mut NapiValue) -> NapiStatus, pub napi_get_boolean: - Symbol<'static, unsafe extern "C" fn(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus>, + unsafe extern "C" fn(env: NapiEnv, value: bool, out: *mut NapiValue) -> NapiStatus, pub napi_get_value_bool: - Symbol<'static, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus>, + unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut bool) -> NapiStatus, pub napi_create_double: - Symbol<'static, unsafe extern "C" fn(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus>, + unsafe extern "C" fn(env: NapiEnv, value: f64, out: *mut NapiValue) -> NapiStatus, pub napi_get_value_double: - Symbol<'static, unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus>, + unsafe extern "C" fn(env: NapiEnv, value: NapiValue, out: *mut f64) -> NapiStatus, } #[cfg(not(windows))] @@ -74,22 +73,23 @@ fn get_host_library() -> Library { Library::this().into() } -lazy_static! { - static ref HOST: Library = get_host_library(); -} - impl Napi { fn try_from_host() -> Result { - let host = &HOST; + let host = get_host_library(); + // Load symbols, then deref to raw function pointers. + // + // SAFETY: The deref here discards a lifetime specifier. This is still safe because we are + // only keeping pointers to functions in the host executable: if they are ever to unload, + // the Neon module will be unloaded first. Ok(unsafe { Self { - napi_get_undefined: host.get(b"napi_get_undefined")?, - napi_get_null: host.get(b"napi_get_null")?, - napi_get_boolean: host.get(b"napi_get_boolean")?, - napi_get_value_bool: host.get(b"napi_get_value_bool")?, - napi_create_double: host.get(b"napi_create_double")?, - napi_get_value_double: host.get(b"napi_get_value_double")?, + napi_get_undefined: *(host.get(b"napi_get_undefined")?), + napi_get_null: *(host.get(b"napi_get_null")?), + napi_get_boolean: *(host.get(b"napi_get_boolean")?), + napi_get_value_bool: *(host.get(b"napi_get_value_bool")?), + napi_create_double: *(host.get(b"napi_create_double")?), + napi_get_value_double: *(host.get(b"napi_get_value_double")?), } }) } From 9485d9afb1e294add6eac47a1e871a417d9ab8f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 27 Nov 2020 15:14:11 +0100 Subject: [PATCH 7/7] Library::this() is fallible on windows --- crates/neon-runtime/src/napi/bindings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/neon-runtime/src/napi/bindings.rs b/crates/neon-runtime/src/napi/bindings.rs index 0d30468f3..d103e2cf0 100644 --- a/crates/neon-runtime/src/napi/bindings.rs +++ b/crates/neon-runtime/src/napi/bindings.rs @@ -70,7 +70,7 @@ fn get_host_library() -> Library { #[cfg(windows)] fn get_host_library() -> Library { use libloading::os::windows::Library; - Library::this().into() + Library::this().unwrap().into() } impl Napi {