While the language specification does not prescribe a method for foreign function
interface, JavaScript was born with the idea of hosting it inside a browser and
therefore foreign function interface is essential to get things done.
The JavaScript you know is usually a combination of the language, the Web APIs,
and the browser runtime. Other projects use JavaScript as a scripting language
and provide a different combination.
Spidermonkey is a JavaScript runtime that is embeddable in C/C++/Rust projects and
allows you to interpret JavaScript and define bindings for JavaScript.
In this example we will see how we can instruct Spidermonkey to expose a native function
to a script. The prerequisites for this project are some
knowledge of C/C++.
Native functions
Native functions are usually written in C/C++ and have the following signature.
In a native function you can use the context of execution to interact with
the Spidermonkey library through functions defined in the “jsapi.h” header.
A native function receives the arguments from the JavaScript side
through the vp argument independently of the number of arguments passed.
The remaining argc argument is the argument count, how many JavaScript
values where used in the function call. This may be different by the number
of values we require.
There are two ways to bind native functions to Spidermonkey and we will see
both. Both go through a 2 step process where we first implement a native
function with the correct signature and then we register it with Spidermonkey.
The main difference between the two is the namespace on which we will register
them.
Native functions as functions
Step 1
This example shows the definition of a function called log which is available
globally. The function goes through all the provided arguments and prints them
as strings to the standard output of our program.
Step 2
Step two of our implementation is to make Spidermonkey aware of our
native function. What we mean by this is that our script will be able
to call the function log and Spidermonkey will then call our native
log function with the correct arguments and resume the normal control
flow after log returns.
Native functions as methods
Step 1
This example shows the definition of a console like object. If you have used
JavaScript in the browser before the console is one of the builtin objects
that the browser exposes to you. With the console you can print messages
that will be available in the browser’s developers tool.
In this example we will instead output those messages to the standard output
of our program.
Step 2
Step two of our implementation is to make Spidermonkey aware of our
native function. What we mean by this is that our scripts will be able to
call the method log of the console object and Spidermonkey will
then call our consoleLog function with the correct arguments and resume the
normal control flow after consoleLog returns.
Instead of defining one name log and bind it to a our function immediately
we go through an intermediate name: console. The main reason motivating this
choice is familiarity. Developers writing JavaScript are already using the browser’s
console methods and we want to offer the same interface here.
To obtain this behavior we first get a JavaScript object from Spidermonkey and
attach it to our console class definition and our console class methods.