edon/internal/
napi_module_register.rs1use std::collections::HashMap;
2use std::ffi::c_char;
3use std::ffi::c_int;
4use std::ffi::c_uint;
5use std::ffi::CString;
6use std::os::raw::c_void;
7use std::ptr;
8use std::sync::LazyLock;
9use std::sync::RwLock;
10
11use crate::napi::JsObject;
12use crate::napi::NapiValue;
13use crate::Env;
14
15type InitFn =
16 unsafe extern "C" fn(libnode_sys::napi_env, libnode_sys::napi_value) -> libnode_sys::napi_value;
17
18static NAPI_MODULE_NAMES: LazyLock<RwLock<HashMap<String, CString>>> =
19 LazyLock::new(Default::default);
20
21fn set_napi_module_register_name<S: AsRef<str>>(name: S) -> bool {
22 let mut napi_module_names = NAPI_MODULE_NAMES.write().unwrap();
23 let name = name.as_ref().to_string();
24 let cname = CString::new(name.clone()).unwrap();
25 if napi_module_names.contains_key(&name) {
26 return false;
27 }
28 napi_module_names.insert(name, cname);
29 true
30}
31
32fn get_napi_module_register_name<S: AsRef<str>>(name: S) -> Option<*const c_char> {
33 let napi_module_names = NAPI_MODULE_NAMES.read().unwrap();
34 let cname = napi_module_names.get(name.as_ref())?;
35 Some(cname.as_ptr())
36}
37
38pub fn napi_module_register<
39 S: AsRef<str>,
40 F: 'static + Fn(Env, JsObject) -> crate::Result<JsObject>,
41>(
42 module_name: S,
43 register_function: F,
44) -> crate::Result<()> {
45 if !set_napi_module_register_name(&module_name) {
46 return Err(crate::Error::NapiModuleAlreadyRegistered);
47 }
48
49 let wrapped_fn = move |napi_env: libnode_sys::napi_env,
50 napi_value: libnode_sys::napi_value|
51 -> libnode_sys::napi_value {
52 let env = unsafe { Env::from_raw(napi_env) };
53 let exports = unsafe { JsObject::from_raw_unchecked(napi_env, napi_value) };
54 register_function(env, exports).unwrap();
55 napi_value
56 };
57
58 let target_fn_leaked: &'static _ = Box::leak(Box::new(wrapped_fn));
59 let target_fn_closure = libffi::high::Closure2::new(target_fn_leaked);
60 let &target_fn_ptr = target_fn_closure.code_ptr();
61 let target_fn: InitFn = unsafe { std::mem::transmute(target_fn_ptr) };
62 std::mem::forget(target_fn_closure);
63
64 let nm = Box::into_raw(Box::new(libnode_sys::napi_module {
65 nm_version: 131 as c_int,
66 nm_flags: 0 as c_uint,
67 nm_filename: get_napi_module_register_name(&module_name).unwrap(),
68 nm_register_func: Some(target_fn),
69 nm_modname: get_napi_module_register_name(&module_name).unwrap(),
70 nm_priv: get_napi_module_register_name(&module_name).unwrap() as *mut c_void,
71 reserved: [
72 std::ptr::null_mut::<c_void>(),
73 ptr::null_mut(),
74 ptr::null_mut(),
75 ptr::null_mut(),
76 ],
77 }));
78
79 unsafe {
80 libnode_sys::napi_module_register(nm);
81 }
82
83 Ok(())
84}