2015年3月21日 星期六

NAN:Node.js 與 io.js 的 Native Addon 開發利器

Standard
自從 Node.js v0.11 版之後,內建的 V8 引擎被更新了,於是 JavaScript 引擎的原生 API 大幅度改變,導致很多以 C/C++ 所撰寫的原生模組紛紛出現相容性問題。影響範圍包括了前陣子發佈的 Node.js v0.12 以及 io.js 1.0+,因為都使用了新版的 V8 JavaScript Engine,而有同樣的問題。

其實這樣的問題已經不是新鮮事,自從 Node.js v0.8 到 v0.10 就開始有些許不相容的問題,只是到了 v0.12 和 io.js 之後,出現了更多狀況。因此 Node.js 圈子內的知名開發者 Rod Vagg 建立了 NAN 專案,用來解決這樣 Native API 不相容的問題。

NAN 全名為 Native Abstractions for Node.js,目標是設計一系列通用 API 供 Native Add-on 開發者所使用,讓開發者可以使用這通用的 API,一次性開發出支援 v0.8、v0.10、v0.12 和 io.js 1.0+ 的原生模組,不必再為 V8 原生 API 相容性所苦。

若想要使用 NAN,可以直接以 NPM 下載:
$ npm install nan

然後在 binding.gyp 中加入 include_dirs 的屬性設定,讓 Node.js 或 io.js 引入 NAN 模組:
'include_dirs': [
    "<!(node -e \"require('nan')\")"
]

經過這樣的設定配置後,我們就可以在 C/C++ 程式裡面引入 NAN 的標頭檔,開始使用 NAN 的 API:
#include <nan.h>

NAN_METHOD(Hello) {
    NanScope();
    NanReturnUndefined();
    // 或是這樣寫 NanReturnValue(Undefined());
}

上面的程式碼,代換成舊的版本(v0.8、v0.10),就等同於:
Handle<Value> Hello(const Arguments& args)
{
    HandleScope scope;
    return scope.close(Undefined());
}

若在新版的 Node.js v0.12 或是 io.js 之上,就等同於:
void Hello(const v8::FunctionCallbackInfo<Value>& args)
{
    Isolate *isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);

    args.GetReturnValue().Set(Undefined());
}

我們可以看到,NAN 已經包裝成一個簡單的 API,方便開發者去移植或開發 Native Add-on。

建立物件

此外,NAN 也將許多物件類型的建立方法,利用 Template 統一成 NanNew() 這樣一個 API,例如:
Local<String> string = NanNew<String>("hello world");
Local<Boolean> boolean = NanNew<Boolean>(NanTrue());
Local<Number> number = NanNew<Number>(123);
Local<Array> number = NanNew<Array>();

更多功能

NAN 有更多的功能,有興趣的人可以參考專案上的說明:
https://github.com/rvagg/nan

後記

因應 io.js 的推出,最近開始搬移手上的 Native Module,包括了 node-dbus、 brig 以及其他種種模組,碰到 V8 API 大改,讓人手足無措,好在 NAN 已經解決了大多數的問題。其實不只是 V8 的更新,libuv 的更新也是問題之一,只不過問題還算小,日後有空再來筆記下來。