From f2e5094345d34e9b225a3bd8f5809d39d11c56cb Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 27 Mar 2024 14:35:16 +0800 Subject: [PATCH] feat(napi-sys): support load Node-API symbols dynamically (#2014) --- .github/workflows/test-release.yaml | 2 +- crates/napi/Cargo.toml | 1 + .../src/bindgen_runtime/module_register.rs | 2 +- crates/sys/Cargo.toml | 8 +- crates/sys/src/functions.rs | 14 +-- crates/sys/src/lib.rs | 113 +++++++++--------- examples/napi-compat-mode/Cargo.toml | 1 + examples/napi/Cargo.toml | 1 + 8 files changed, 74 insertions(+), 68 deletions(-) diff --git a/.github/workflows/test-release.yaml b/.github/workflows/test-release.yaml index 8695e35c..a9477221 100644 --- a/.github/workflows/test-release.yaml +++ b/.github/workflows/test-release.yaml @@ -548,7 +548,7 @@ jobs: - name: Build run: | bun run build - bun run build:test + yarn workspace @examples/napi build --features dyn-symbols - name: Test continue-on-error: true run: bun run test:bun diff --git a/crates/napi/Cargo.toml b/crates/napi/Cargo.toml index d0ac5440..355f10d0 100644 --- a/crates/napi/Cargo.toml +++ b/crates/napi/Cargo.toml @@ -55,6 +55,7 @@ tokio_stats = ["tokio/stats"] tokio_sync = ["tokio/sync"] tokio_test_util = ["tokio/test-util"] tokio_time = ["tokio/time"] +dyn-symbols = ["napi-sys/dyn-symbols"] [dependencies] bitflags = "2" diff --git a/crates/napi/src/bindgen_runtime/module_register.rs b/crates/napi/src/bindgen_runtime/module_register.rs index c7a1ece2..2ea26580 100644 --- a/crates/napi/src/bindgen_runtime/module_register.rs +++ b/crates/napi/src/bindgen_runtime/module_register.rs @@ -243,7 +243,7 @@ pub fn get_c_callback(raw_fn: ExportRegisterCallback) -> Result }) } -#[cfg(all(windows, not(feature = "noop")))] +#[cfg(all(any(windows, feature = "dyn-symbols"), not(feature = "noop")))] #[ctor::ctor] fn load_host() { unsafe { diff --git a/crates/sys/Cargo.toml b/crates/sys/Cargo.toml index 96f0c7b6..8a11ec5c 100644 --- a/crates/sys/Cargo.toml +++ b/crates/sys/Cargo.toml @@ -12,6 +12,7 @@ rust-version = "1.65" version = "2.3.0" [features] +dyn-symbols = ["libloading"] experimental = [] napi1 = [] napi2 = ["napi1"] @@ -26,5 +27,8 @@ napi9 = ["napi8"] [package.metadata.workspaces] independent = true -[target.'cfg(windows)'.dependencies.libloading] -version = "0.8" +[dependencies] +libloading = { version = "0.8", optional = true } + +[target.'cfg(windows)'.dependencies] +libloading = "0.8" diff --git a/crates/sys/src/functions.rs b/crates/sys/src/functions.rs index d4a39f83..9408891f 100644 --- a/crates/sys/src/functions.rs +++ b/crates/sys/src/functions.rs @@ -780,15 +780,13 @@ pub use napi8::*; #[cfg(feature = "napi9")] pub use napi9::*; -#[cfg(windows)] +#[cfg(any(windows, feature = "dyn-symbols"))] pub(super) unsafe fn load_all() -> Result { - let host = match libloading::os::windows::Library::this() { - Ok(lib) => lib.into(), - Err(err) => { - eprintln!("Initialize libloading failed {}", err); - return Err(err); - } - }; + #[cfg(windows)] + let host = libloading::os::windows::Library::this()?.into(); + + #[cfg(unix)] + let host = libloading::os::unix::Library::this().into(); napi1::load(&host)?; #[cfg(feature = "napi2")] diff --git a/crates/sys/src/lib.rs b/crates/sys/src/lib.rs index 20ae54fe..5334de44 100644 --- a/crates/sys/src/lib.rs +++ b/crates/sys/src/lib.rs @@ -1,73 +1,74 @@ +// borrowed from https://github.com/neon-bindings/neon/tree/main/crates/neon/src/sys/bindings + #![allow(ambiguous_glob_reexports)] -#[cfg(windows)] +#[cfg(any(windows, feature = "dyn-symbols"))] macro_rules! generate { (extern "C" { - $(fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+ + $(fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+ }) => { - struct Napi { - $( - $name: unsafe extern "C" fn( - $($param: $ptype,)* - )$( -> $rtype)*, - )* - } + struct Napi { + $( + $name: unsafe extern "C" fn( + $($param: $ptype,)* + )$( -> $rtype)*, + )* + } - #[inline(never)] - fn panic_load() -> T { - panic!("Must load N-API bindings") - } + #[inline(never)] + fn panic_load() -> T { + panic!("Node-API symbol has not been loaded") + } - static mut NAPI: Napi = { - $( - unsafe extern "C" fn $name($(_: $ptype,)*)$( -> $rtype)* { - panic_load() + static mut NAPI: Napi = { + $( + unsafe extern "C" fn $name($(_: $ptype,)*)$( -> $rtype)* { + panic_load() + } + )* + + Napi { + $( + $name, + )* + } + }; + + #[allow(clippy::missing_safety_doc)] + pub unsafe fn load( + host: &libloading::Library, + ) -> Result<(), libloading::Error> { + NAPI = Napi { + $( + $name: { + let symbol: Result $rtype)*>, libloading::Error> = host.get(stringify!($name).as_bytes()); + match symbol { + Ok(f) => *f, + Err(e) => { + #[cfg(debug_assertions)] { + eprintln!("Load Node-API [{}] from host runtime failed: {}", stringify!($name), e); + } + NAPI.$name } - )* - - Napi { - $( - $name, - )* - } + } + }, + )* }; + Ok(()) + } + + $( + #[inline] #[allow(clippy::missing_safety_doc)] - pub unsafe fn load( - host: &libloading::Library, - ) -> Result<(), libloading::Error> { - NAPI = Napi { - $( - $name: { - let symbol: Result $rtype)*>, libloading::Error> = host.get(stringify!($name).as_bytes()); - match symbol { - Ok(f) => *f, - Err(e) => { - debug_assert!({ - println!("Load Node-API [{}] from host runtime failed: {}", stringify!($name), e); - true - }); - return Ok(()); - } - } - }, - )* - }; - - Ok(()) + pub unsafe fn $name($($param: $ptype,)*)$( -> $rtype)* { + (NAPI.$name)($($param,)*) } - - $( - #[inline] - #[allow(clippy::missing_safety_doc)] - pub unsafe fn $name($($param: $ptype,)*)$( -> $rtype)* { - (NAPI.$name)($($param,)*) - } - )* + )* }; } -#[cfg(not(windows))] +#[cfg(not(any(windows, feature = "dyn-symbols")))] macro_rules! generate { (extern "C" { $(fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+ @@ -90,7 +91,7 @@ pub use types::*; /// Must be called at least once before using any functions in bindings or /// they will panic. /// Safety: `env` must be a valid `napi_env` for the current thread -#[cfg(windows)] +#[cfg(any(windows, feature = "dyn-symbols"))] #[allow(clippy::missing_safety_doc)] pub unsafe fn setup() -> libloading::Library { match load_all() { diff --git a/examples/napi-compat-mode/Cargo.toml b/examples/napi-compat-mode/Cargo.toml index 9111dadb..9f756def 100644 --- a/examples/napi-compat-mode/Cargo.toml +++ b/examples/napi-compat-mode/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["cdylib"] [features] latest = ["napi/napi9"] napi3 = ["napi/napi3"] +dyn-symbols = ["napi/dyn-symbols"] [dependencies] futures = "0.3" diff --git a/examples/napi/Cargo.toml b/examples/napi/Cargo.toml index 2a359cf4..269485f2 100644 --- a/examples/napi/Cargo.toml +++ b/examples/napi/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib"] [features] snmalloc = ["snmalloc-rs"] +dyn-symbols = ["napi/dyn-symbols"] [dependencies] chrono = "0.4"