WebAssembly: calling C functions from Javascript with emscripten

July 27, 2017  

I started to look into webassembly with the goal of implementing a version of Conway’s Game of Life which was able to use an engine written in C to calculate the next state of the game.

The WebAssembly logo

In this short article I want to illustrate the steps I made to compile a simple C function into a webassembly module callable by Javascript. To compile the C code I used emscripten, an open source LLVM (Low Level Virtual Machine) to Javascript compiler.

Installing emscripten

The emscripten logo

Installing the emscripten toolchain is pretty straightforward, the official documentation is available here. The installation process could take quite a lot of time anyway (depending on your network and machine speed), since the project is quite big.

Note: I use macOS therefore the syntax used in the article may be slightly different on Windows or Linux :)

Once emscripten is installed you have to set the system path to the active version of it (from the emsdk directory):

source ./emsdk_env.sh

To test if emscripten is correctly configured just run:

emcc -v

Writing and compiling a C function

The C function that I am going to compile implements the logic to define if a cell of the grid will be dead or alive in the next iteration of the game. Let’s create a file called getCellStatus.c and paste this code in it.

int dead = 0;
int alive = 1;

int getCellStatus(int status, int neighboursCount) {
  switch(neighboursCount) {
    case 3:
      status = alive;
      break;
    case 2:
      break;
    default:
      status = dead;
  }

  return status;
}

The logic of getCellStatus is pretty simple. It accepts two integer parameters: the status and the number of neighbours, and returns the new status of the cell as an integer.

Now run:

emcc getCellStatus.c -o getCellStatus.js -s EXPORTED_FUNCTIONS=[‘_getCellStatus’]

This command will generate a javascript file (getCellStatus.js) wrapping our C code in a module.

For the moment we need to manually export the function that we want to call from Javascript by adding the EXPORTED_FUNCTIONS options to the command.

Now there are two ways to call a C compiled function from Javascript:

  • Wrapping the function using cwrap() (this returns Javascript function callable multiple times)
  • Directly called the function using ccall() (this returns directly the return value of the C function)

We can now call getCellStatus by adding some code to the end of our Javascript module.

Wrapping a function with cwrap

cwrap accepts three parameters and returns a Javascript function:

  1. The name of the function to be wrapped
  2. The return type of the function
  3. An array with the types of the parameters of the function (which can be omitted if the function has no parameter)

In our case a call to cwrap would be as follows:

const getCellStatus = Module.cwrap('getCellStatus ', 'number', ['number', 'number'])

console.log(getCellStatus(1, 3))
console.log(getCellStatus(0, 3))
console.log(getCellStatus(0, 2))
console.log(getCellStatus(1, 1))

// Prints 1 1 0 0

Calling a function with ccall

ccall is similar to cwrap. It returns the value returned by the C function and requires an additional parameter defining the actual arguments to pass to the function call.

const getCellStatus = Module.ccall('getCellStatus ', 'number', ['number', 'number'], [1, 3])

// Prints 1

Using EMSCRIPTEN_KEEPALIVE to export functions

When we compiled our C file to Javascript we had to manually declare in the command which functions we wanted to export.

Emscripten provide us with an useful annotation to avoid this: EMSCRIPTEN_KEEPALIVE. So let’s rewrite our C file:

#include <emscripten.h>

int dead = 0;
int alive = 1;

EMSCRIPTEN_KEEPALIVE
int getCellStatus(int status, int neighboursCount) {
  switch(neighboursCount) {
    case 3:
      status = alive;
      break;
    case 2:
      break;
    default:
      status = dead;
  }

  return status;
}

Compile the function (no need to declare exported functions now)

emcc getCellStatus.c -o getCellStatus.js

Then we can just create an index.js file to test our function

const em_module = require('./getCellStatus.js')

console.log(em_module.ccall('getCellStatus', [0, 3])) // using ccall

console.log(em_module._getCellStatus(1, 2)) // vdirect call

And run it with

node index_getCellStatus.js

// Prints 0 1

Conclusion

As you can see using emscripten to generate a Javascript module from a C file and to call compiled C function with it is pretty easy.

To get further details about how you can use emscripten to interact with C functions read the official documentation here.

Anyway this is a really basic usage of this tool, more advanced access to the memory or to wasm files requires a little more effort. I will try to explain how to do that in the second part of this article.

Thanks for reading 😎