feat(napi-sys): support load Node-API symbols dynamically (#2014)

This commit is contained in:
LongYinan 2024-03-27 14:35:16 +08:00 committed by GitHub
parent 0550c56fcf
commit f2e5094345
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 74 additions and 68 deletions

View file

@ -548,7 +548,7 @@ jobs:
- name: Build - name: Build
run: | run: |
bun run build bun run build
bun run build:test yarn workspace @examples/napi build --features dyn-symbols
- name: Test - name: Test
continue-on-error: true continue-on-error: true
run: bun run test:bun run: bun run test:bun

View file

@ -55,6 +55,7 @@ tokio_stats = ["tokio/stats"]
tokio_sync = ["tokio/sync"] tokio_sync = ["tokio/sync"]
tokio_test_util = ["tokio/test-util"] tokio_test_util = ["tokio/test-util"]
tokio_time = ["tokio/time"] tokio_time = ["tokio/time"]
dyn-symbols = ["napi-sys/dyn-symbols"]
[dependencies] [dependencies]
bitflags = "2" bitflags = "2"

View file

@ -243,7 +243,7 @@ pub fn get_c_callback(raw_fn: ExportRegisterCallback) -> Result<crate::Callback>
}) })
} }
#[cfg(all(windows, not(feature = "noop")))] #[cfg(all(any(windows, feature = "dyn-symbols"), not(feature = "noop")))]
#[ctor::ctor] #[ctor::ctor]
fn load_host() { fn load_host() {
unsafe { unsafe {

View file

@ -12,6 +12,7 @@ rust-version = "1.65"
version = "2.3.0" version = "2.3.0"
[features] [features]
dyn-symbols = ["libloading"]
experimental = [] experimental = []
napi1 = [] napi1 = []
napi2 = ["napi1"] napi2 = ["napi1"]
@ -26,5 +27,8 @@ napi9 = ["napi8"]
[package.metadata.workspaces] [package.metadata.workspaces]
independent = true independent = true
[target.'cfg(windows)'.dependencies.libloading] [dependencies]
version = "0.8" libloading = { version = "0.8", optional = true }
[target.'cfg(windows)'.dependencies]
libloading = "0.8"

View file

@ -780,15 +780,13 @@ pub use napi8::*;
#[cfg(feature = "napi9")] #[cfg(feature = "napi9")]
pub use napi9::*; pub use napi9::*;
#[cfg(windows)] #[cfg(any(windows, feature = "dyn-symbols"))]
pub(super) unsafe fn load_all() -> Result<libloading::Library, libloading::Error> { pub(super) unsafe fn load_all() -> Result<libloading::Library, libloading::Error> {
let host = match libloading::os::windows::Library::this() { #[cfg(windows)]
Ok(lib) => lib.into(), let host = libloading::os::windows::Library::this()?.into();
Err(err) => {
eprintln!("Initialize libloading failed {}", err); #[cfg(unix)]
return Err(err); let host = libloading::os::unix::Library::this().into();
}
};
napi1::load(&host)?; napi1::load(&host)?;
#[cfg(feature = "napi2")] #[cfg(feature = "napi2")]

View file

@ -1,73 +1,74 @@
// borrowed from https://github.com/neon-bindings/neon/tree/main/crates/neon/src/sys/bindings
#![allow(ambiguous_glob_reexports)] #![allow(ambiguous_glob_reexports)]
#[cfg(windows)] #[cfg(any(windows, feature = "dyn-symbols"))]
macro_rules! generate { macro_rules! generate {
(extern "C" { (extern "C" {
$(fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+ $(fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+
}) => { }) => {
struct Napi { struct Napi {
$( $(
$name: unsafe extern "C" fn( $name: unsafe extern "C" fn(
$($param: $ptype,)* $($param: $ptype,)*
)$( -> $rtype)*, )$( -> $rtype)*,
)* )*
} }
#[inline(never)] #[inline(never)]
fn panic_load<T>() -> T { fn panic_load<T>() -> T {
panic!("Must load N-API bindings") panic!("Node-API symbol has not been loaded")
} }
static mut NAPI: Napi = { static mut NAPI: Napi = {
$( $(
unsafe extern "C" fn $name($(_: $ptype,)*)$( -> $rtype)* { unsafe extern "C" fn $name($(_: $ptype,)*)$( -> $rtype)* {
panic_load() 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<libloading::Symbol<unsafe extern "C" fn ($(_: $ptype,)*)$( -> $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)] #[allow(clippy::missing_safety_doc)]
pub unsafe fn load( pub unsafe fn $name($($param: $ptype,)*)$( -> $rtype)* {
host: &libloading::Library, (NAPI.$name)($($param,)*)
) -> Result<(), libloading::Error> {
NAPI = Napi {
$(
$name: {
let symbol: Result<libloading::Symbol<unsafe extern "C" fn ($(_: $ptype,)*)$( -> $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(())
} }
)*
$(
#[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 { macro_rules! generate {
(extern "C" { (extern "C" {
$(fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+ $(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 /// Must be called at least once before using any functions in bindings or
/// they will panic. /// they will panic.
/// Safety: `env` must be a valid `napi_env` for the current thread /// 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)] #[allow(clippy::missing_safety_doc)]
pub unsafe fn setup() -> libloading::Library { pub unsafe fn setup() -> libloading::Library {
match load_all() { match load_all() {

View file

@ -11,6 +11,7 @@ crate-type = ["cdylib"]
[features] [features]
latest = ["napi/napi9"] latest = ["napi/napi9"]
napi3 = ["napi/napi3"] napi3 = ["napi/napi3"]
dyn-symbols = ["napi/dyn-symbols"]
[dependencies] [dependencies]
futures = "0.3" futures = "0.3"

View file

@ -10,6 +10,7 @@ crate-type = ["cdylib"]
[features] [features]
snmalloc = ["snmalloc-rs"] snmalloc = ["snmalloc-rs"]
dyn-symbols = ["napi/dyn-symbols"]
[dependencies] [dependencies]
chrono = "0.4" chrono = "0.4"