neon/sys/
fun.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//! Facilities for working with JS functions.

use std::{mem::MaybeUninit, os::raw::c_void, ptr};

use super::{
    bindings as napi,
    raw::{Env, Local},
};

pub unsafe fn new<F>(env: Env, name: &str, callback: F) -> Result<Local, napi::Status>
where
    F: Fn(Env, napi::CallbackInfo) -> Local + 'static,
{
    let mut out = MaybeUninit::uninit();
    let data = Box::into_raw(Box::new(callback));
    let status = napi::create_function(
        env,
        name.as_ptr().cast(),
        name.len(),
        Some(call_boxed::<F>),
        data.cast(),
        out.as_mut_ptr(),
    );

    match status {
        Err(err @ napi::Status::PendingException) => {
            drop(Box::from_raw(data));

            return Err(err);
        }
        status => status.unwrap(),
    };

    let out = out.assume_init();

    #[cfg(feature = "napi-5")]
    {
        unsafe extern "C" fn drop_function<F>(
            _env: Env,
            _finalize_data: *mut c_void,
            finalize_hint: *mut c_void,
        ) {
            drop(Box::from_raw(finalize_hint.cast::<F>()));
        }

        let status = napi::add_finalizer(
            env,
            out,
            ptr::null_mut(),
            Some(drop_function::<F>),
            data.cast(),
            ptr::null_mut(),
        );

        // If adding the finalizer fails the closure will leak, but it would
        // be unsafe to drop it because there's no guarantee V8 won't use the
        // pointer.
        status.unwrap();
    }

    Ok(out)
}

// C ABI compatible function for invoking a boxed closure from the data field
// of a Node-API JavaScript function
unsafe extern "C" fn call_boxed<F>(env: Env, info: napi::CallbackInfo) -> Local
where
    F: Fn(Env, napi::CallbackInfo) -> Local + 'static,
{
    let mut data = MaybeUninit::uninit();
    napi::get_cb_info(
        env,
        info,
        ptr::null_mut(),
        ptr::null_mut(),
        ptr::null_mut(),
        data.as_mut_ptr(),
    )
    .unwrap();

    let callback = &*data.assume_init().cast::<F>();

    callback(env, info)
}

pub unsafe fn construct(
    out: &mut Local,
    env: Env,
    fun: Local,
    argc: usize,
    argv: *const c_void,
) -> bool {
    let status = napi::new_instance(env, fun, argc, argv as *const _, out as *mut _);

    status.is_ok()
}