Heather's Paragraphs

Playing Around With js ctypes on Linux

I’m pretty elated about the ctypes module introduced in Firefox 3.6. ctypes.jsm is a module that lets chrome code call functions from shared libraries. This is a big win for a lot of extension developers. The baseline is that you no longer have to create an XPCOM component to call C++ code from javascript.

I had wanted to speed up some calculation-heavy js I was using in my extension by writing it in C++. Writing a C++ XPCOM component would be a painful process, so I decided to ditch the effort…then I found out about ctypes!

Ctypes can help with calling Win API functions and such, and there are some examples of this on the ctypes.jsm wiki page for this. My use case however was loading my own shared library, so I decided to put together a short end-to-end tutorial on how to call your own C code from your extension.

First we write a little C function and put it in add.c:

int add(int a, int b) {
  return a + b;
}

To get a shared library from this code, compile with these commands:

gcc -fPIC -c add.c
gcc -shared -o add.so add.o

Now you have a file called add.so, which we can load from ctypes. Say you put add.so in your addon’s content directory, then your javascript might look something like this:

Components.utils.import("resource://gre/modules/ctypes.jsm");

function add(a, b) {
  var file = getFile("chrome://myext/content/add.so");
  var lib = ctypes.open(file); 

  var addc = lib.declare("add",
                           ctypes.default_abi,
                           ctypes.int32_t, // return type
                           ctypes.int32_t, // arg1 type
                           ctypes.int32_t // arg2 type
  );
  return addc(a, b);
}


function getFile(chromeURL) {
  // convert the chrome URL into a file URL
  var cr = Components.classes['@mozilla.org/chrome/chrome-registry;1']
           .getService(Components.interfaces.nsIChromeRegistry);
  var io = Components.classes['@mozilla.org/network/io-service;1']
           .getService(Components.interfaces.nsIIOService);
  var uri = io.newURI(decodeURI(chromeURL), 'UTF-8', null);
  var fileURL = cr.convertChromeURL(uri);
  // get the nsILocalFile for the file
 return  fileURL.QueryInterface(Components.interfaces.nsIFileURL).file;
}

Now try calling your add function from js. The getFile function isn’t that pretty, hopefully in the future there will be a way to open a library from a chrome url. Also, you can’t use ctypes from Worker threads, so that is very sad.