infinyon / node-bindgen

Easy way to write Node.js module using Rust
Apache License 2.0
531 stars 44 forks source link

Fail to invoke the cached JS Callback, due to the error "napi call failed InvalidArg" #224

Closed stuartZhang closed 8 months ago

stuartZhang commented 1 year ago

Hello,

I'd like to globally hold a reference of the callback function originating from the JS side in the Rust static variable so that the callback function can be invoked within any Rust functions.

At the moment, the callback function is able to be stored in a Rust static variable, by virtue of both the lifetime parameter 'static and the macro std::thread_local!. Nevertheless, the later invocation against the retained callback function brings about a runtime error "napi call failed InvalidArg".

My code snippet is as follows

/// static variable storing the JS Callback function
std::thread_local! {
    static LOGGER: RefCell<Option<Box<dyn Fn(String)>>> = RefCell::new(None);
}
/// the wrapper for invoking the cached js callback function
fn log(text: String) -> bool {
    LOGGER.with(|log| {
        let log = log.borrow();
        if let Some(log) = log.as_ref() {
            log(text);
            true
        } else {
            false
        }
    })
}
#[node_bindgen]
fn set_logger<F: Fn(String) + 'static>(logger: F) {
    let logger: Box<dyn Fn(String)> = Box::new(logger);
    LOGGER.with(|log| {
        log.borrow_mut().replace(logger); // Store the js callback function into the static variable
    });
    log("[set_logger]".to_string()); // Succeed to immediately invoke it.
}
#[node_bindgen]
fn get_edition() -> GitEdition {
    log("Failure".to_string()); // Fail! A runtime error "napi call failed InvalidArg" will be thrown.
    GitEdition::default()
}
const addon = require('./dist/index.node');
addon.setLogger(function(text){console.log(text)});
addon.getEdition(); // Fail here.

Are there any workarounds useful to solve the problem?

To my knowledge, there is a struct Closure in the wasm_bindgen crate for the same purpose. By means of wasm_bindgen::closure::Closure, a JS callback function conveyed into the WASM module is augmented and able to be invoked across the different exported function API.

Is there the counterpart of the struct wasm_bindgen::closure::Closure in the node_bindgen crate?

sehz commented 1 year ago

That's an interesting possibility. We haven't done anything in WASM integration but welcome any contribution in that area