neon/sys/
fun.rs

1//! Facilities for working with JS functions.
2
3use std::{mem::MaybeUninit, os::raw::c_void, ptr};
4
5use super::{
6    bindings as napi,
7    raw::{Env, Local},
8};
9
10pub unsafe fn new<F>(env: Env, name: &str, callback: F) -> Result<Local, napi::Status>
11where
12    F: Fn(Env, napi::CallbackInfo) -> Local + 'static,
13{
14    let mut out = MaybeUninit::uninit();
15    let data = Box::into_raw(Box::new(callback));
16    let status = napi::create_function(
17        env,
18        name.as_ptr().cast(),
19        name.len(),
20        Some(call_boxed::<F>),
21        data.cast(),
22        out.as_mut_ptr(),
23    );
24
25    match status {
26        Err(err @ napi::Status::PendingException) => {
27            drop(Box::from_raw(data));
28
29            return Err(err);
30        }
31        status => status.unwrap(),
32    };
33
34    let out = out.assume_init();
35
36    #[cfg(feature = "napi-5")]
37    {
38        unsafe extern "C" fn drop_function<F>(
39            _env: Env,
40            _finalize_data: *mut c_void,
41            finalize_hint: *mut c_void,
42        ) {
43            drop(Box::from_raw(finalize_hint.cast::<F>()));
44        }
45
46        let status = napi::add_finalizer(
47            env,
48            out,
49            ptr::null_mut(),
50            Some(drop_function::<F>),
51            data.cast(),
52            ptr::null_mut(),
53        );
54
55        // If adding the finalizer fails the closure will leak, but it would
56        // be unsafe to drop it because there's no guarantee V8 won't use the
57        // pointer.
58        status.unwrap();
59    }
60
61    Ok(out)
62}
63
64// C ABI compatible function for invoking a boxed closure from the data field
65// of a Node-API JavaScript function
66unsafe extern "C" fn call_boxed<F>(env: Env, info: napi::CallbackInfo) -> Local
67where
68    F: Fn(Env, napi::CallbackInfo) -> Local + 'static,
69{
70    let mut data = MaybeUninit::uninit();
71    napi::get_cb_info(
72        env,
73        info,
74        ptr::null_mut(),
75        ptr::null_mut(),
76        ptr::null_mut(),
77        data.as_mut_ptr(),
78    )
79    .unwrap();
80
81    let callback = &*data.assume_init().cast::<F>();
82
83    callback(env, info)
84}
85
86pub unsafe fn construct(
87    out: &mut Local,
88    env: Env,
89    fun: Local,
90    argc: usize,
91    argv: *const c_void,
92) -> bool {
93    let status = napi::new_instance(env, fun, argc, argv as *const _, out as *mut _);
94
95    status.is_ok()
96}