Hi!
I am writing node.js bindings for a C library. Some of the C API takes callback function pointers, so my corresponding JS API will accept JS callbacks. I intend to use a hybrid JS/C++ solution to achieve running the JS callback whenever the C callback gets called. Let's say the C API has the following signature:
void c_api_function( int flags, void (*callback)() );
Let's further say that the name of my module is cApiBindings (i.e., you will load the module by doing require( "bindings" )( "cApiBindings" ) ), and that I intend to expose the above function as cApiBindings.c_api_function(). I was thinking about achieving this as follows:
------------------------------------------------------------------------------------------------------
1. The JS side:
var cApiBindings = require( "bindings" )( "cApiBindings" );
cApiBindings.callbacks = {};
cApiBindings.callbackUuid = 0;
cApiBindings.c_api_function( flags, callback ) {
var uuid = cApiBindings.callbackUuid++;
cApiBindings.callbacks[ uuid ] = callback;
return cApiBindings._partial_c_api_function( flags, uuid );
};
IOW, store the callback in an array of currently active callbacks, and pass the location of the JS callback to the C++ side.
2. The C++ side, containing the crux of the problem:
void default_c_api_callback( int uuid ) {
Local<Function> callback = Local<Function>::Cast(
?????->Get( String::NewFromUtf8( "callbacks" ) )
->Get( uuid ) );
/* Execute the JS callback thus retrieved */
}
void bind__partial_c_api_function( const FunctionCallbackInfo<Value>& args ) {
int flags = args[ 0 ]->ToUint32(),
uuid = args[ 1 ]->ToUint32();
/* Create a closure using libffi so that uuid ends up getting passed to default_c_api_callback() as defined above */
void (*closure)() = create_closure( default_c_api_callback, uuid );
c_api_function( flags, closure );
}
void Init( Handle<Object> exports, Handle<Object> module ) {
NODE_SET_METHOD( exports, "_partial_c_api_function", bind__partial_c_api_function );
}
NODE_MODULE( cApiBindings, Init )
------------------------------------------------------------------------------------------------------
As you can see, in the C callback I need a way of retrieving the module onto which I've defined all the bindings, and onto which the JS side has defined the callbacks hash. Where does node keep this reference? For JS modules, it's in a parameter passed into the outermost IIFE, so it's always within the scope of the module.
I've tried to simply define a global variable
Handle<Object> module_exports;
and set it inside Init() as
module_exports = exports;
but that is useless, because attempting to access members of module_exports from the C callback via module_exports->Get(...) results in a segault. So, is there a correct way of storing a reference to module and/or exports that gets passed into Init() in a global variable for later use by the C callback?
Can I somehow work my way from Isolate::GetCurrent()?
I have successfully assigned the module object to a key on the global object inside Init():
Isolate::GetCurrent()->GetCurrentContext()->Global()
->ForceSet( String::NewFromUtf8( Isolate::GetCurrent(), "cApiBindings" ), module,
( PropertyAttribute )( ReadOnly | DontEnum | DontDelete ) );
I can then retrieve the module off the global object later on. Great, but I'd rather not pollute the global object. I'd much rather store module in a global variable in the source file instead. Is there a correct way of doing that?
TIA for your help,
Gabriel