C functions occasionally take function pointers as arguments, which are generally used as callbacks. In these cases, js-ctypes allows you to pass a regular JavaScript function as the callback. This is very powerful, since it allows native code to transparently call into JavaScript.
Prerequiste Understanding
Declaring Callbacks
A callback is declared by using ctypes.FunctionType
. The first argument is the calling convention, the second argument is the return type, and the third is an array of arguments the callback expects.
The return type of the javascript callback must match the return type declared, otherwise js-ctypes will throw an error saying "unexpected return type".
Examples
Example 1
This callback that returns bool
and has two arguments.
var myFuncTypeDeclaration = ctypes.FunctionType(ctypes.default_abi, ctypes.bool, [ctypes.int, ctypes.voidptr_t]); function myJSCallback(cInt, cPtr) { return true; // as the return of the FunctionType was ctypes.bool we must make our javascript callback return bool otherwise js-ctypes will throw error saying unexpected type return } var myCCallback = myFuncTypeDeclaration.ptr(myJSCallback);
Example 2
This callback that returns void
and no arguments. When void is the return type, the javascript callback must not return, or it should return undefined
.
var myFuncTypeDeclaration = ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, []); function myJSCallback() { return undefined; // as the return of the FunctionType was ctypes.void_t we must return undefined OR dont return at all otherwise js-ctypes will throw an error saying unexpected type } var myCCallback = myFuncTypeDeclaration.ptr(myJSCallback);
Using Callbacks
Since callbacks are function pointers in C, js-ctypes has special handling for function pointer types. Consider the following code:
function myJSCallback(foo, bar) { .... }; var funcType = ctypes.FunctionType(...); var funcPtrType = funcType.ptr; var regularFuncPtr = funcPtrType(); var callback = funcPtrType(myJSCallback);
js-ctypes detects that funcPtrType
is a type of function pointer, and adds a special case to its constructor. In the ordinary case, invoking the type object creates a regular CData
object containing a pointer to the data. However, if the first argument is a function, js-ctypes creates a special object (known internally as a CClosure) that wraps the JavaScript function within an ordinary C function. This can all be done in a single line of code, like so:
var callback = ctypes.FunctionType(...).ptr(function(...) {...});
.ptr()
here isn't a method call; we're accessing a property that dynamically creates a callable object, and then invoking the result.If two arguments are passed to the callback constructor, the second is used as the this
parameter:
function myJSCallback() { alert(this.message); }; var receiver = { message: 'hi there!' }; var callback = funcPtrType(myJSCallback, receiver); // alerts with 'hi there' when the callback is invoked
If three arguments are passed to the callback constructor, the third argument is used as a sentinel value which the callback returns if an exception is thrown. The sentinel value must be convertible to the return type of the callback:
function myJSCallback() { throw "uh oh"; }; var callback1 = funcPtrType(myJSCallback, null, -1); // Returns -1 to the native caller when the exception is thrown.
Arguments and return values
js-ctypes automatically handles the conversion between JavaScript and C value representations. Before the JavaScript callback function is invoked, js-ctypes converts the arguments passed by the caller to JavaScript values. Where possible, js-ctypes will convert arguments to primitive types. For arguments of complex types, temporary CData
objects will be created.
The return value is converted in a similar manner.