Skip to content

Interacting with code

kripken edited this page Mar 22, 2012 · 54 revisions

Interacting with Compiled Code

Calling Compiled Functions

It's easy to call compiled code from normal JavaScript. For example, run this command in the Emscripten home directory:

./emcc tests/hello_function.cpp -o function.html

Open the page in a web browser. Nothing will happen because you compiled a function, and there is no main so nothing will be run by default. But, open a JavaScript environment (Control-Shift-K on Firefox, Control-Shift-J on Chrome), then type (as three separate commands, pressing Enter after each one):

int_sqrt = cwrap('int_sqrt', 'number', ['number'])
int_sqrt(12)
int_sqrt(28)

You should get the results 3, 5, which are the correct output: The compiled function does a square root operation, but acts on integers. Open tests/hello_function.cpp to see the source code that you just called here.

cwrap which was used in this example will wrap a compiled C function, returning a JavaScript function you can call normally. cwrap gets as its first parameter the name of the function to be wrapped, then the return type of the function, then an array of parameter types (the array can be omitted if there are no parameters). The types are native JavaScript types, "number" (for a C integer, float, or general pointer) or "string" (for a C char* that represents a string).

There is also ccall, which is like cwrap but receives another parameter with the parameters to pass to the function, and calls the function. cwrap is useful to wrap a function once and call it several times, while ccall is useful for a single call to a function.

Some things to keep in mind with ccall and cwrap:

  • In the example above, we compiled a C function. That's easier than C++ because C++ functions are name-mangled.
  • By default Emscripten does dead code elimination to minimize code size. See the FAQ entry on "Functions in my C/C++ source code vanish when I compile to JavaScript..?"
  • In -O2 and above, closure compiler is run, which minifies function names - which means you won't be able to find your compiled functions. To prevent that, run emcc with `-s EXPORTED_FUNCTIONS="['_main','_other_function']". Exported functions will retain their names even through closure compiler.

Accessing Memory

You can access memory using getValue(ptr, type) and setValue(ptr, value, type). The first argument is a pointer, a number representing a memory address. type must be an LLVM IR type, one of i8,i16,i32,i64,float,double or a pointer type like i8* (or just *). Note that the types here are not as in ccall and cwrap - this is a lower-level operation, and we do need to care what specific integer etc. type is being used.

You can also access memory 'directly' by manipulating the arrays that represent memory. See the compiled code for details, but this is not recommended unless you are sure you know what you are doing, and need the additional speed.

Other Methods

You can directly interact in various other ways with the compiled code:

  • Functions in the original source become JS functions, so you can call them directly if you do type translations yourself - this will be faster than using ccall or cwrap, but a little more complex. Note that you need to prefix function calls with '_' (all compiled functions have that added).
    • The types of the parameters you pass to functions need to make sense. Integers and floating point values can be passed as is. Aside from those, there are pointers, which are simply integers in the generated code.
    • Strings in JavaScript must be converted to pointers for compiled code, the relevant functions are Pointer_stringify which given a pointer returns a JavaScript string, and the other direction can be accomplished by allocate(intArrayFromString(someString), 'i8', ALLOC_STACK) which will convert a JavaScript string someString to a pointer. Note that conversion to a pointer allocates memory (that's the call to allocate there).
  • There are various other convenience functions, see preamble.js (that file will be included with the generated code).
  • The bindings generator can generate convenient JS classes from C++ headers. See the bindings test in the test runner, and the ammo.js project (the main consumer of that tool)
  • For filesystem-related manners, see the Filesystem Guide.

Affecting Execution

You can affect how code runs by creating an object called Module before the compiled script. Certain properties on Module can then have various effects:

  • arguments: The commandline arguments (if the compiled code checks argc, argc, it will be seeing arguments)
  • print: Called when something is printed to standard output.
  • preRun: A function to call right before calling run, but after defining and setting up the environment. This is useful, for example, to set up directories and files using the FileSystem API (since that needs the FileSystem API to be defined, but also needs to be done before the program starts to run).
  • noInitialRun: If set to true, run will not be called (so memory will not be initialized and the compiled code's main is not executed), and you should call it yourself later.

For example,

var Module = {
  'print': function(text) { alert(text) }
};

This will cause all printouts from the program to be calls to alert.

Important: If you run closure compiler on your code (which is done by default in -O2 and above), you will need quotation marks around the properties of Module as in the example above (and you need to run closure on the compiled code together with the declaration of Module).

Setting Module

When generating just JavaScript, no Module object is created. So you can use emcc's --pre-js to add some JS code that defines the Module object with the stuff you need.

When generating HTML, a Module object is created for you and filled with some defaults for printing, etc. (compile a little hello world example to see, or view src/shell.html). The simplest thing is to use --pre-js to add some JS code that adds properties to that existing Module object.