Skip to main content

napi_sys/
lib.rs

1// borrowed from https://github.com/neon-bindings/neon/tree/main/crates/neon/src/sys/bindings
2
3#![allow(ambiguous_glob_reexports)]
4
5#[cfg(any(
6  target_env = "msvc",
7  all(not(target_family = "wasm"), feature = "dyn-symbols")
8))]
9#[inline(never)]
10unsafe fn load_symbol(
11  host: &libloading::Library,
12  name: &'static [u8],
13) -> Option<*mut std::ffi::c_void> {
14  match unsafe { host.get::<unsafe extern "C" fn()>(name) } {
15    Ok(symbol) => unsafe { symbol.try_as_raw_ptr() },
16    Err(_) => None,
17  }
18}
19
20#[cfg(any(
21  target_env = "msvc",
22  all(not(target_family = "wasm"), feature = "dyn-symbols")
23))]
24macro_rules! generate {
25  (@stub_fn $name:ident($($param:ident: $ptype:ty,)*) -> napi_status) => {
26    unsafe extern "C" fn $name($(_: $ptype,)*) -> napi_status {
27      eprintln!("Node-API symbol {} has not been loaded", stringify!($name));
28      1
29    }
30  };
31  (@stub_fn $name:ident($($param:ident: $ptype:ty,)*) -> $rtype:ty) => {
32    unsafe extern "C" fn $name($(_: $ptype,)*) -> $rtype {
33      eprintln!("Node-API symbol {} has not been loaded", stringify!($name));
34      unsafe { std::mem::zeroed() }
35    }
36  };
37  (@stub_fn $name:ident($($param:ident: $ptype:ty,)*)) => {
38    unsafe extern "C" fn $name($(_: $ptype,)*) {
39      eprintln!("Node-API symbol {} has not been loaded", stringify!($name));
40    }
41  };
42  (extern "C" {
43    $(fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+
44  }) => {
45    struct Napi {
46      $(
47        $name: unsafe extern "C" fn(
48          $($param: $ptype,)*
49        )$( -> $rtype)*,
50      )*
51    }
52
53    static mut NAPI: Napi = {
54      $(
55        generate!(@stub_fn $name($($param: $ptype,)*) $( -> $rtype)?);
56      )*
57
58      Napi {
59        $(
60          $name,
61        )*
62      }
63    };
64
65    #[allow(clippy::missing_safety_doc)]
66    pub unsafe fn load(
67      host: &libloading::Library,
68    ) -> Result<(), libloading::Error> {
69      NAPI = Napi {
70        $(
71          $name: match unsafe {
72            // SAFETY: every generated name identifies a Node-API function, and `setup` keeps the
73            // host library alive for as long as the loaded function pointers can be called.
74            $crate::load_symbol(
75              host,
76              concat!(stringify!($name), "\0").as_bytes(),
77            )
78          } {
79            Some(symbol) => {
80              // SAFETY: Node-API exports use the signature declared for this field. The host
81              // library remains alive for as long as these function pointers can be called.
82              unsafe {
83                std::mem::transmute::<
84                  *mut std::ffi::c_void,
85                  unsafe extern "C" fn($(_: $ptype,)*)$( -> $rtype)*,
86                >(symbol)
87              }
88            }
89            // Ignore the lookup error and preserve the existing stub function.
90            None => NAPI.$name,
91          },
92        )*
93      };
94
95      Ok(())
96    }
97
98    $(
99      #[inline]
100      #[allow(clippy::missing_safety_doc)]
101      pub unsafe fn $name($($param: $ptype,)*)$( -> $rtype)* {
102        (NAPI.$name)($($param,)*)
103      }
104    )*
105  };
106}
107
108#[cfg(any(
109  target_family = "wasm",
110  all(not(target_env = "msvc"), not(feature = "dyn-symbols"))
111))]
112macro_rules! generate {
113  (extern "C" {
114    $(fn $name:ident($($param:ident: $ptype:ty$(,)?)*)$( -> $rtype:ty)?;)+
115  }) => {
116    extern "C" {
117      $(
118        pub fn $name($($param: $ptype,)*)$( -> $rtype)*;
119      ) *
120    }
121  };
122}
123
124mod functions;
125mod types;
126
127pub use functions::*;
128pub use types::*;
129
130#[cfg(any(
131  target_env = "msvc",
132  all(not(target_family = "wasm"), feature = "dyn-symbols")
133))]
134/// Loads N-API symbols from host process.
135/// Must be called at least once before using any functions in bindings or
136/// they will panic
137///
138/// # Safety
139///
140/// The returned Library must be kept alive as long as any N-API
141pub unsafe fn setup() -> libloading::Library {
142  match load_all() {
143    Err(err) => panic!("{}", err),
144    Ok(l) => l,
145  }
146}