Last night, I pushed an addition to my fork of Mastering Node. I decided to add a bit to the Addons chapter. The first example in this chapter only shows how to add a function to a natively-compiled module (i.e. an addon). This example shows you how to start a module which can be used in the following way:
var Uuid = require('./uuid.node').Uuid;
var uuid = new Uuid();
var myId = uuid.generate();
The project files referenced in the following text can be downloaded from the repo: jimschubert/masteringnode
FunctionTemplate
In v8, a FunctionTemplate is used to create the equivalent to:
var template = function() {
The function at this point is an object and not an instance of the function.
As an example, we will use the linux package uuid to generate a uuid. We will define the header for this addon as:
// addons/uuid/v0.1/uuid.h
#ifndef __node_uuid_h__
#define __node_uuid_h__
#include <string>
#include <v8.h>
#include <node.h>
#include "uuid/uuid.h"
using namespace v8;
using namespace node;
class Uuid : public node::ObjectWrap {
public:
Uuid() { }
static Persistent<FunctionTemplate> constructor;
static void Init(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
static Handle<Value> Generate(const Arguments &args);
static Handle<Value> GenerateRandom(const Arguments &args);
static Handle<Value> GenerateTime(const Arguments &args);
private:
~Uuid();
static std::string GetString(uuid_t id);
};
#endif // __node_uuid_h__
This addon will showcase three methods, Generate
, GenerateRandom
, and GenerateTime
. It will also include a trivial private GetString
method to demonstrate how to Unwrap a node::ObjectWrap
object and interact with C++ code that is not specific to node or v8.
A lot of the public function definitions should look similar to the Echo example. One notable difference is that instead of using a macro and hiding the FunctionTemplate
method, we are defining static Persistent<FunctionTemplate> constructor;
. The Persistent<T>
type is used “when you need to keep a reference to an object for more than one function call, or when handle lifetimes do not correspond to C++ scopes.” source. Since we’d expect our object’s constructor to last longer than a single function, we declare it separately and as a persistent handle. Another point to notice is that all of the method we’re pulling from uuid.h have the signature static Handle<Value> Method(const Arguments &args)
even though we will plan to call it as uuid.generate()
. This is because we will be accessing the scope of the call via args.This()
.
Although more methods are implemented in uuid.cc, we will look at three:
Uuid::Init(Handle<Object> target)
Handle<Value> Uuid::New(const Arguments &args)
Handle<Value> Uuid::Generate(const Arguments &args)
Just as before, node expects to find a signature of extern "C" void init(Handle<Object> target)
in order to initialize the addon. Inside this method, we may set parameters such as the version number from the previous example. We may also pass-through initialization to any modules within our node addon. In this example, our addon will be uuid.node and contain a single module, Uuid. There is no reason we can’t later add Uuid2 which, instead of returning a normalized string value might return a Buffer
object. To initialize the Uuid module, we pass the target
object along to Uuid::Init
and add the module definition to target
:
// addons/uuid/v0.1/uuid.cc
void Uuid::Init(Handle<Object> target) {
HandleScope scope;
// Setup the constructor
constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Uuid::New));
constructor->InstanceTemplate()->SetInternalFieldCount(1); // for constructors
constructor->SetClassName(String::NewSymbol("Uuid"));
// Setup the prototype
NODE_SET_PROTOTYPE_METHOD(constructor, "generate", Generate);
NODE_SET_PROTOTYPE_METHOD(constructor, "generateRandom", GenerateRandom);
NODE_SET_PROTOTYPE_METHOD(constructor, "generateTime", GenerateTime);
target->Set(String::NewSymbol("Uuid"), constructor->GetFunction());
}
In this scope, we are instantiating the constructor
using Uuid::New
as a new FunctionTemplate
. We then call InstanceTemplate()
and on that object we call SetInternalFieldCount(1)
. This tells v8 that this object holds a reference to one object.
Next, we setup the prototype using another macro provided by node. These calls say, for instance, “Add a method called generate to the constructor function which executes the native method Generate“.
Lastly, we have to create a “Uuid” module on the object returned by the call to require()
. Here, Uuid
will point to a function (constructor
) which returns a function that internally executes Uuid::New
. In other words, we have created something akin to:
var Uuid = function() { };
Uuid.constructor = function() {
return function() {
// Uuid::New executes here.
}
}
Although the above is not exactly what we have done, it may provide a better view for some to understand FunctionTemplate
references and why we assign one to the constructor object in such a way.
The Uuid::New
method is defined as:
// addons/uuid/v0.1/uuid.cc
Handle<Value> Uuid::New(const Arguments &args) {
HandleScope scope;
// no args are checked
Uuid *uuid = new Uuid();
uuid->Wrap(args.This());
return args.This();
}
As you would expect, calling the constructor function multiple times will create newly-scoped Uuid
objects on the heap. In this method, we wrap the parameter (scoped object) by setting a reference to Uuid
in the args as a contextual scope (i.e. this
) and then returns this
.
Within the Generate
method, we will want to unwrap the contextual Uuid
object and call the private method GetString
.
// addons/uuid/v0.1/uuid.cc
Handle<Value> Uuid::Generate(const Arguments &args) {
HandleScope scope;
uuid_t id;
uuid_generate(id);
Uuid *uuid = ObjectWrap::Unwrap<Uuid>(args.This());
return scope.Close(String::New(uuid->GetString(id).c_str()));
}
As with any JavaScript function call, we have to ensure functional scope. Scoped methods should create a HandleScope object at the start and call scope.Close()
at the end. HandleScope
will get rid of temporary handles when the scope is closed.
Within each of the _Generate*_ methods, we will create a uuid_t
type and call the corresponding method defined in _/usr/includes/uuid/uuid.h_ (location may vary per system). To demonstrate accessing the pointer to our original Uuid
object, we unwrap the contextual scope of this function using ObjectWrap::Unwrap<Uuid>(args.This())
. From this pointer, we can access any private methods such as GetString
. Be careful with your returned values, though. String::New
in the v8 library does not take std::string
in any of its signatures. Simply enough, std::string
provides a c_str()
method to return a const char*
which String::New
does accept.
uuid.node demo
Navigate to addons/uuid/v0.1/ and execute:
$ node-waf configure build
If there are build errors and the func.cc example from before built successfully, check that you have the uuid-dev package installed and rerun the above command. Then, navigate to build/default and try out the Uuid addon:
$ node
> var Uuid = require('./uuid.node').Uuid;
> var uuid = new Uuid();
> uuid.generate();
'83475e0c-212b-402c-bdc7-b81ebb7b34f8'
> uuid.generateRandom();
'4d597bda-8f5f-4c3c-b2fa-1cd6cd4a6903'
> uuid.generateTime();
'25a0dd30-5076-11e1-96be-0022fb93b24c'
> var util = require('util');
> util.inspect(uuid);
'{}'
> util.inspect(Uuid);
'[Function: Uuid]'
>
The above output may surprise you. Firstly, where is the version
option?! It’s at the required module level: require('./uuid.node').version;
. Secondly, if we can access uuid.generate()
and others, why don’t they display when inspecting the object? That’s because we defined those methods on the prototype:
> uuid.__proto__
{ generate: [Function: generate],
generateRandom: [Function: generateRandom],
generateTime: [Function: generateTime] }
>
Thirdly, you may have noticed that I didn’t say anything about constructor->SetClassName(String::NewSymbol("Uuid"));
in Uuid::Init
. You may have also wondered where SetClassName
actually sets a class name, considering JavaScript is a prototypal language. That string value is what is displayed when you call inspect and get the value '[Function: Uuid]'
. Just as you would expect, Uuid
is the constructor and it is named Uuid
.
Now, if you’ve played around with this a bit, you may have noticed that uuid.__proto__
gives us our three functions but uuid.prototype
is empty. Why is that? That’s because uuid.__proto__
really is uuid.constructor.prototype
, which is also really Uuid.prototype
. This is the essence of prototypal inheritance. If this concept is foreign or difficult to grasp, be sure to check out the excellent explanation on JavaScript Garden.
Logically, the next step would be to understand how to declare a prototype of our own.