Skip to content

Instantly share code, notes, and snippets.

@bellbind
Last active October 22, 2015 21:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bellbind/a68620383e0180b3afc6 to your computer and use it in GitHub Desktop.
Save bellbind/a68620383e0180b3afc6 to your computer and use it in GitHub Desktop.
[nodejs]Making native extension as ObjectWrap for node-4 with nan-2

Minimal Code for Native Module as ObjectWrap for node-4 with nan-2

  • A module main source: r.g. nan-objectwrap.cc
    • Nan::ObjectWrap: define for Holding C++ object(e.g. pointers) in each JS Object
    • info.IsConstructCall(): true when called with new
      • it is important check because ObjectWrap::Wrap crush vm when in non-IsConstructCall case
    • info.Callee(): returns Local<Function> object of the NAN_METHOD
    • Nan::NewInstance(function, argc, argv): same as new Function(arg...) in JS
    • Nan::ObjectWrap#Wrap(object): bind C++ ObjectWrap object to the JS object instance(usually info.This())
    • Nan::ObjectWrap::Unwrap<T>(object): access ObjectWrap object bound the JS object(usually info.Holder() as this in JS)
    • NAN_GETTER and NAN_SETTER: Native function for property accessor
      • info: parameters of accessor function
      • property: property name as String (getter and setter)
      • value: setting value in setter
    • v8::FunctionTemplate: as a constructor function, it bundled sub templates to set additional members as constructor.prototype
      • InstanceTemplate(): use for adding instance member functions
      • PrototypeTemplate(): use for adding static/class member functions
    • v8::InstanceTemplate#SetInternalFieldCount(n): set a count of custom members slot in C++ layer in v8
      • for ObjectWrap of node.js, it should set 1
    • Nan::SetPrototypeMethod(functpl, name, cppfunc): set cppfunc as instance method
    • Nan::SetAccessor(objtpl, name, getter, setter=nullptr): define Getter and Setter to the object(instance or prototpe) template
    • Nan::GetFunction(v8::FunctionTemplate): get constructor function object from the template bundle
    • Nan::Set(v8::Object target, v8::Value key, v8::Value value): set property to JS object

Reference

Simple Example for a native module with exporting functions

APIs:

# -*- mode: python -*-
{
"targets": [
{
"include_dirs": ["<!(node -e \"require('nan')\")"],
"target_name": "NanObjectWrap",
"sources": [
"nan-objectwrap.cc"
],
"conditions": [
["OS != 'win'",{
"cflags": ["-Wall", "-Wextra", "-pedantic"],
"cflags_cc": ["-std=c++14"],
}]
],
},
],
}
#include <string>
#include <vector>
#include <nan.h>
// Nan::ObjectWrap example
class NanPerson: public Nan::ObjectWrap {
public:
static NAN_MODULE_INIT(Init);
private:
// Native JS Functions for accessing the custom object properties
static NAN_METHOD(New); // constructor
static NAN_METHOD(GetName); // method
static NAN_GETTER(NameGet); // (specific) property getter
static NAN_SETTER(NameSet); // (specific) property setter
// the native object properties
NanPerson(std::string);
std::string name;
};
//v8::Persistent<v8::Function> NanPerson::constructor;
NanPerson::NanPerson(std::string aName): name(aName) {}
NAN_METHOD(NanPerson::New) {
if (!info.IsConstructCall()) {
// [NOTE] generic recursive call with `new`
std::vector<v8::Local<v8::Value>> args(info.Length());
for (std::size_t i = 0; i < args.size(); ++i) args[i] = info[i];
auto inst = Nan::NewInstance(info.Callee(), args.size(), args.data());
if (!inst.IsEmpty()) info.GetReturnValue().Set(inst.ToLocalChecked());
return;
}
auto name = Nan::To<v8::String>(info[0]).ToLocalChecked();
auto person = new NanPerson(*Nan::Utf8String(name));
// [NOTE] break vm if run the under code in no-`new` function call
person->Wrap(info.This()); // `Wrap` bind C++ object to JS object
}
NAN_METHOD(NanPerson::GetName) {
// `Unwrap` refer C++ object from JS Object
auto person = Nan::ObjectWrap::Unwrap<NanPerson>(info.Holder());
auto name = Nan::New(person->name).ToLocalChecked();
info.GetReturnValue().Set(name);
}
NAN_GETTER(NanPerson::NameGet) {
auto person = Nan::ObjectWrap::Unwrap<NanPerson>(info.Holder());
auto name = Nan::New(person->name).ToLocalChecked();
info.GetReturnValue().Set(name);
}
NAN_SETTER(NanPerson::NameSet) {
auto person = Nan::ObjectWrap::Unwrap<NanPerson>(info.Holder());
// [NOTE] `value` is defined argument in `NAN_SETTER`
auto name = Nan::To<v8::String>(value).ToLocalChecked();
person->name = *Nan::Utf8String(name);
}
NAN_MODULE_INIT(NanPerson::Init) {
auto cname = Nan::New("Person").ToLocalChecked();
auto ctor = Nan::New<v8::FunctionTemplate>(New);
auto ctorInst = ctor->InstanceTemplate(); // target for member functions
ctor->SetClassName(cname); // as `ctor.name` in JS
ctorInst->SetInternalFieldCount(1); // for ObjectWrap, it should set 1
// add member functions and accessors
Nan::SetPrototypeMethod(ctor, "getName", GetName);
auto pname = Nan::New("name").ToLocalChecked();
Nan::SetAccessor(ctorInst, pname, NameGet, NameSet);
Nan::Set(target, cname, Nan::GetFunction(ctor).ToLocalChecked());
}
NAN_MODULE_INIT(InitAll) {
NanPerson::Init(target);
}
NODE_MODULE(NanObject, InitAll)
{
"name": "nan-objectwrap",
"version": "0.1.0",
"description": "Native module as objectwrap with nan-2",
"license": "MIT",
"repository": "gist:a68620383e0180b3afc6",
"engine": {"node": ">=4.0.0"},
"main": "./build/Release/NanObjectWrap",
"dependencies": {
"nan": ">=2.0.0"
}
}
var m = require(".");
var p = new m.Person("Taro");
console.log(p.name);
p.name = "Jiro";
console.log(p.getName());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment