Declaring and Using Callbacks

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.

Warning: Callbacks must be invoked on the same thread with which they were registered. There is no concurrency logic in js-ctypes, so invoking callbacks on other threads will cause things to crash.

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(...) {...});
Note: The use of .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.
Warning: You must store a reference to the callback object as long as the native code might call it. If you don't, the GC might collect the relevant data structures, and the browser will crash when native code attempts to invoke your callback.

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.