拓展nodejs內核

拓展nodejs的方式有很多種,可以寫npm包,可以寫c++插件,還可以修改內核重新編譯分發。本文介紹如何通過爲nodejs內核增加一個c++模塊的方式拓展nodejs的功能(git地址:https://github.com/theanarkh/learn-how-to-extend-node)。相比修改nodejs內核代碼,新增一個nodejs內置模塊需要了解更多的知識。下面我們開始。
1 首先在src文件夾下新增兩個文件。
cyb.h

#ifndef SRC_CYB_H_
#define SRC_CYB_H_

#include "v8.h"

namespace node {

class Environment;

class Cyb {
 public:

  static void Initialize(v8::Local<v8::Object> target,
                         v8::Local<v8::Value> unused,
                         v8::Local<v8::Context> context,
                         void* priv);

 private:
  
  static void Console(const v8::FunctionCallbackInfo<v8::Value>& args);
};


}  // namespace node

#endif

cyb.cc

#include "cyb.h"
#include "env-inl.h"
#include "util-inl.h"
#include "node_internals.h"

namespace node {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;

void Cyb::Initialize(Local<Object> target,
                         Local<Value> unused,
                         Local<Context> context,
                         void* priv) {
  Environment* env = Environment::GetCurrent(context);
  // 申請一個函數模塊,模板函數是Console
  Local<FunctionTemplate> t = env->NewFunctionTemplate(Console);
  // 申請一個字符串
  Local<String> str = FIXED_ONE_BYTE_STRING(env->isolate(), "console");
  // 設置函數類名
  t->SetClassName(str);
  // 導出函數,target即exports
  target->Set(env->context(),
              str,
              t->GetFunction(env->context()).ToLocalChecked()).Check();
}

void Cyb::Console(const FunctionCallbackInfo<Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  v8::Local<String> str = String::NewFromUtf8(isolate, "hello world");
  args.GetReturnValue().Set(str);
}

}  // namespace node
// 聲明該模塊
NODE_MODULE_CONTEXT_AWARE_INTERNAL(cyb_wrap, node::Cyb::Initialize)

我們新定義一個模塊,是不能自動添加到nodejs內核的。我們還需要額外的操作。我們需要修改node.gyp文件。把我們新增的文件加到配置裏,否則編譯的時候,不會編譯這個新增的模塊。我們可以在node.gyp文件中找到src/tcp_wrap.cc,然後在他後面加入我們的文件就行。

src/cyb_wrap.cc
src/cyb_wrap.h

這時候nodejs會編譯我們的代碼了。但是nodejs的內置模塊有一定的機制,我們的代碼加入了nodejs內核,不代表就可以使用了。我們看一下nodejs對內置++
模塊的機制。nodejs在初始化的時候會調用RegisterBuiltinModules函數註冊所有的內置c++模塊。

void RegisterBuiltinModules() {
#define V(modname) _register_##modname();
  NODE_BUILTIN_MODULES(V)
#undef V
}

我們看到該函數只有一個宏。我們看看這個宏。

#define NODE_BUILTIN_MODULES(V)                                                \
  NODE_BUILTIN_STANDARD_MODULES(V)                                             \
  NODE_BUILTIN_OPENSSL_MODULES(V)                                              \
  NODE_BUILTIN_ICU_MODULES(V)                                                  \
  NODE_BUILTIN_REPORT_MODULES(V)                                               \
  NODE_BUILTIN_PROFILER_MODULES(V)                                             \
  NODE_BUILTIN_DTRACE_MODULES(V)   

宏裏面又是一堆宏,本文不是源碼解析,所以不深入講解,可以參考之前的文章。我們要做的就是修改這個宏。因爲我們是自定義的內置模塊,所以我們可以增加一個宏。

#define NODE_BUILTIN_EXTEND_MODULES(V)                                       \
  V(cyb_wrap) 

然後把這個宏追加到那一堆宏後面。

#define NODE_BUILTIN_MODULES(V)                                                \
  NODE_BUILTIN_STANDARD_MODULES(V)                                             \
  NODE_BUILTIN_OPENSSL_MODULES(V)                                              \
  NODE_BUILTIN_ICU_MODULES(V)                                                  \
  NODE_BUILTIN_REPORT_MODULES(V)                                               \
  NODE_BUILTIN_PROFILER_MODULES(V)                                             \
  NODE_BUILTIN_DTRACE_MODULES(V)                                               \
  NODE_BUILTIN_EXTEND_MODULES(V)

這時候,nodejs不僅可以編譯我們的代碼,還把我們的代碼定義的模塊註冊到內置c++模塊裏了。接下來就是如何使用c++模塊了。
2 在lib文件夾新建一個cyb.js,作爲nodejs原生模塊

const cyb = internalBinding('cyb_wrap'); 
module.exports = cyb;

internalBinding函數是在執行cyb.js,注入的函數。不能在用戶js裏使用。internalBinding函數就是根據模塊名從內置模塊裏找到對應的模塊。即我們的cyb.cc。新增原生模塊,我們也需要修改node.gyp文件,否則代碼也不會被編譯進node內核。我們找到node.gyp文件的lib/net.js。在後面追加lib/cyb.js。該配置下的文件是給js2c.py使用的。如果不修改,我們在require的時候,就會找不到該模塊。最後我們在lib/internal/bootstrap/loader文件裏找到internalBindingWhitelist變量,在數組最後增加cyb_wrap。這個配置是給process.binding函數使用的,如果不修改這個配置,通過process.binding就找不到我們的模塊。process.binding是可以在用戶js裏使用的。

    到此,我們完成了所有的修改工作,重新編譯nodejs。然後編寫測試程序。
3 新建一個測試文件testcyb.js

// const cyb = process.binding('cyb_wrap');
const cyb = require('cyb'); 
console.log(cyb.console())

輸出hello world。

總結:本文介紹如何給nodejs內核新增一個新的模塊。關於c++內置模塊的機制可以參考(nodejs之模塊系統源碼分析(上)),更多內容可以參考nodejs源碼。學習修改nodejs內核是成爲nodejs contributor的第一步,結合v8和libuv的知識。你可以爲nodejs做得更多,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章