This is intended to be a short guide to writing JavaScript bindings to C or C++ libraries using the V8 engine. It is intended primarily for node.js developers, though other projects using V8 may find it useful too. This tutorial/article is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 2.5 License.
Prerequisites
Before we begin you should have node installed on the system. The latest code is always preferred, but there is no hard rule, since most of the tutorial will stay the same.
In addition you are encouraged to read the Embedder’s Guide which introduces the concepts of Scope and Handles that are useful to know.
The basics: Generating UUIDs
UUIDs are unique identifiers. For example if you have a distributed system where you would like unique identifiers across all nodes, integers aren’t the best choice since they introduce synchronization problems across the nodes. UUIDs are guaranteed to be unique enough for most tasks and can be used as identifiers in this case.
Most POSIX systems have libuuid installed as part of e2fsprogs, and on OS X, it is a part of the System library. We will use it to introduce one function — uuid_generate — into JavaScript.
Basically this is how clients will use the library ( node refers to bindings as addons ):
var uuid = require('./uuid')
, sys = require('sys')
sys.debug( uuid.generate() );
The build environment
Node addons have a .node extension, so that our output file will be uuid.node. To build our addon we are going to use a Waf build file, since the node-waf utility is bundled with node. The node_postgres build file is usually a good starting point.
...
# Set this
VERSION = "0.0.1"
...
def build(bld):
obj = bld.new_task_gen("cxx", "shlib", "node_addon")
# Will generate build/default/uuid.node
obj.target = "uuid"
# compile all files in the src/ directory
obj.find_sources_in_dirs("src")
# The list of libraries to link against i.e. GCC's -l option
# Thanks to Elijah Insua
# http://groups.google.com/group/nodejs/msg/442a49ce6f86d70d
if sys.platform == 'darwin':
obj.lib = ["System"]
else:
obj.lib = ["uuid"]
# This is so that we have uuid.node in the root
# rather than doing require('./build/default/uuid.node')
def shutdown(bld):
# HACK to get binding.node out of build directory.
# better way to do this?
if Options.commands['clean']:
if exists('uuid.node'): unlink('uuid.node')
else:
if exists('build/default/uuid.node') and not exists('uuid.node'):
symlink(getcwd()+'/build/default/uuid.node', 'uuid.node')
Initialization
The node API documentation says that all addons must provide a init function which is called after node loads the binary. This is a good place to setup your binding, including global variable initialization and so on. The init function gets a v8 Object as the argument. This is the object global to your module, and allows your binding to expose functions/objects/variables to JavaScript.
Since we want a simple function that takes no arguments, this is what we have:
// used for strlen()
#include <cstring>
// obvious
#include <v8.h>
#include <node.h>
// the libuuid functions
#include <uuid/uuid.h>
using namespace v8;
using namespace node;
extern "C"
void init( Handle<Object> target ) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(uuid_v8::Generate);
target->Set( String::NewSymbol( "generate" ), t->GetFunction() );
}
The uuid_v8::Generate function is the one we will be writing to actually generate the UUID. Notice how v8 exposes the JavaScript to C++. To create a function, you first create a FunctionTemplate. The FunctionTemplate allows you to set prototype members or add properties to the function, but that is beyond the scope of this article and is usually not required.
To this template you will usually pass a wrapped C++ function ( read about function pointers if you are unsure how functions can be passed around ) . Now you get an instance of the function using GetFunction() and hook it to the generate function in the global scope. v8 implements quite a few custom types so that C++ and JavaScript types can be interchanged. But it also uses these types in the internals. So you have to pass the generate wrapped in a v8::String. All these types are instantiated using their New() method. Later we will see how node has a few useful macros for this.
The Generate Function
Everything upto now has been setup, you’ll do this in every binding. Now is the part that will be unique to each application. In this case writing the UUID generating function.
First, every function exposed to JavaScript MUST have the following declaration:
Handle<Value> Generate( const Arguments &args );
The return Value will encapsulate a JavaScript return type, one of String or Int32 or whatever else your function should return. Arguments is an array like object with each element being a Value. So
uuid.generate( 1, "test" );
would result in
args[0] -> Integer args[1] -> String
Our generate function doesn’t require any arguments, so we’ll simply ignore it. But later we will see how to access the arguments and convert them to the right types. With that out of the way, we can begin the function implementation.
namespace uuid_v8 {
Handle<Value> Generate( const Arguments &args ) {
HandleScope scope;
}
}
To generate a new UUID we use uuid_generate
// ... in Generate() uuid_t new_uuid; uuid_generate( new_uuid );
This is a opaque type, what we want is the string form. A UUID is 36 bytes, so we create a character array and use uuid_unparse to convert new_uuid and then return it.
char str[40]; uuid_unparse( new_uuid, str ); return String::New( str, strlen( str ) );
This is another form of String::New which takes a C string and its length and creates the v8 String object. Now…
$ node-waf configure build
That’s it! Run node test.js to see the output. With this you are ready to write basic, function only bindings. Now we will see how to create C++ objects and associate them with JavaScript, reacting to method calls on the objects and finally emitting events. For now, the api.h and api.cc files are a good way to see which methods each v8 type supports.