Skip to content

Instantly share code, notes, and snippets.

@bellbind
Last active March 22, 2021 16:16
Show Gist options
  • Save bellbind/cc915d252eeb77a3e03e to your computer and use it in GitHub Desktop.
Save bellbind/cc915d252eeb77a3e03e to your computer and use it in GitHub Desktop.
[nodejs]Making native package for node.js-4.x with nan-2.0

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

  • The npm package file: package.json

    • "name", "version", "description", "license", "repository": for warning inhibition (also add a readme variant file)
    • "main": in this case, use out/Release/xxx.node shared object directly
    • "dependencies": use "nan-2" as dependency
  • The native module build description file: binding.gyp

    • "targets" - "include_dirs": for including nan headers as <nan.h>
    • "targets" - "target_name"; set name xxx for out/Release/xxx.node
    • "targets" - "sources"; list of C/C++ source files for the target
    • "targets" - "condition" - "cflags": for set clang/gcc options
  • A module main source: e.g. nan-example.cc

    • NAN_METHOD(name): generic declaration of native function
      • info: the argument of NAN_METHOD declaration as v8::FunctionCallbackInfo
    • info.GetReturnValue().Set(...): current style of return ... as in JS function
    • NAN_MODULE_INIT(name): generic declaration of module entry function
      • target: reference of exports object for the module
    • NAN_EXPORT(target, funcname): add native function to the target as same name
      • Nan::Export(target, name, func): add a function as renamed
    • NODE_MODULE(target_name, moduleinit): target_name should be same as in binding.gyp
  • Native module source detailed

    • Nan::To<v8type>: returns MaybeLocal<v8type>
    • Nan::To<cpptype>: returns Maybe<cpptype>
    • MaybeLocal<v8type> and Maybe<cppnumtype>: many current v8 API returns as haskell's Maybe style(Just or Nothong)
      • MaybeLocal<v8type>#ToLocalChecked(): forcely returns Local<v8type> value
      • Maybe<cppnumtype>#FromJust(): forcely returns cppnumtype value
    • *Nan::Utf8String(v8::String): returns as const char* string
    • Nan::New(const char*): create as MaybeLocal<v8::String> string
    • Nan::New(cnumtype): create as Local<v8::NUMTYPE> object (e.g. int32_t as Local<v8::Int32>, not MaybeLocal)
  • Detail for throwing Error function

    • Nan::Error(const char*): create Local<v8::Error> object
    • Nan::ThrowError(error): throw exception (and you should return the function)
      • call current isolate (e.g. info.GetIsolate() or v8::Isolate::getCurrrent()) v8::Isolate#ThrowException(error) inside
  • Detail for callback function

    • use v8::Object#CallAsFunction(recv, argc, argv)
      • returns Local<Value>
      • [NOTE] recv of Nan::CallAsFunction is only v8::Object (cannot accept null value)
  • Detail of TypedArray

    • at first cleate v8::ArrayBuffer then create v8::Int32Array
    • access data as usual v8::Object

How to Build a native extension

npm install

Reference

Native addon example with build system description for the Old Node-0.6 age version:

See also current info:

# -*- mode: python -*-
{
"targets": [
{
"include_dirs": ["<!(node -e \"require('nan')\")"],
"target_name": "NanExample",
"sources": [
"nan-example.cc"
],
"conditions": [
["OS != 'win'",{
"cflags": ["-Wall", "-Wextra", "-pedantic"],
"cflags_cc": ["-std=c++14"],
}]
],
},
],
}
// build: npm install
#include <iostream>
#include <nan.h>
// Case: Simple value returned Function
NAN_METHOD(Hello) {
// `info` is declared in NAN_METHOD
auto name = Nan::To<v8::String>(info[0]).ToLocalChecked();
std::clog << "[LOG] Name is " << *Nan::Utf8String(name) << std::endl;
auto greet = Nan::New("Hello ").ToLocalChecked();
auto result = v8::String::Concat(greet, name);
info.GetReturnValue().Set(result);
}
// Case: Exception raized Function
NAN_METHOD(RaiseError) {
if (Nan::To<bool>(info[0]).FromJust()) {
auto error = Nan::Error("Error from Native");
Nan::ThrowError(error);
return;
}
info.GetReturnValue().Set(Nan::New("Error Not Raised").ToLocalChecked());
}
// Case: Callback Function
NAN_METHOD(CallTwice) {
auto func = Nan::To<v8::Object>(info[0]).ToLocalChecked();
for (auto i = 0; i < 2; i++) {
v8::Local<v8::Value> args[1] = {Nan::New(i)};
auto ret = func->CallAsFunction(Nan::Null(), 1, args);
auto r = Nan::To<v8::String>(ret).ToLocalChecked();
std::clog << "called[" << i << "] " << *Nan::Utf8String(r) << std::endl;
}
}
// Case: use TypedArray
NAN_METHOD(Range) {
auto len = Nan::To<unsigned>(info[0]).FromJust();
auto buf = v8::ArrayBuffer::New(info.GetIsolate(), len * sizeof (int32_t));
auto array = v8::Int32Array::New(buf, 0, len);
for (auto i = 0u; i < len; ++i) Nan::Set(array, i, Nan::New(i));
info.GetReturnValue().Set(array);
}
// Case: use TypedArray with c array;
NAN_METHOD(Squares) {
auto len = Nan::To<unsigned>(info[0]).FromJust();
auto data = new int32_t[len];
for (auto i = 0u; i < len; ++i) data[i] = i * i;
// manage `free` data when kExternalized
auto size = len * sizeof (int_32_t);
auto flag = v8::ArrayBufferCreationMode::kInternalized;
auto buf = v8::ArrayBuffer::New(info.GetIsolate(), data, size, flag);
auto array = v8::Int32Array::New(buf, 0, len);
std::clog << "[external data] " << buf->IsExternal() << std::endl;
info.GetReturnValue().Set(array);
}
NAN_MODULE_INIT(InitAll) {
// `target` is declared in NAN_MODULE_INIT
NAN_EXPORT(target, Hello); // as shorthand
Nan::Export(target, "raiseError", RaiseError);
Nan::Export(target, "callTwice", CallTwice);
Nan::Export(target, "range", Range);
Nan::Export(target, "squares", Squares);
}
NODE_MODULE(NanExample, InitAll)
{
"name": "nan-example",
"version": "0.1.0",
"description": "Native module example with nan-2",
"license": "MIT",
"repository": "gist:cc915d252eeb77a3e03e",
"engine": {"node": ">=4.0.0"},
"main": "./build/Release/NanExample",
"dependencies": {
"nan": ">=2.0.0"
}
}
"use strict";
// function
let m = require(".");
console.log(m.Hello("Taro"));
// error raised
console.log(m.raiseError());
try {
m.raiseError(1);
} catch (err) {
console.log(err);
}
// callback
let i = 1;
m.callTwice((n) => {
console.log("called " + i++);
return "accepted " + n;
});
// TypedArray
var a = m.range(5);
console.log(a);
console.log(m.squares(5));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment