NodeオリジナルプラグインN-APIピット

8235 ワード

前言
Nodeの原生モジュールはかつて人に冷遇されたコーナーであり、その一部の原因はNodeプログラマーの一部がフロントエンドから来ているためであり、システムのC++知識がない可能性があり、学校で学んだ基本的なC文法と簡単なC++概念に限られている可能性があり、難解な役割ドメイン、メモリ回収、テンプレートに対する認識は非常に限られている.そして多くの人がC++に対して恐怖を持っていて、高深さが分かりにくいと思っているかもしれません.確かに、C++は確かにそんなに簡単には学べないものですが、難しいから面白いという言葉があります.
この世界でC++に精通できる人はいません.
C++に精通できる人はいません.つまり、私たちはほとんどいません.精通していません.あなたと大牛も違いません.そう考えると、それに触れるときは、それほど抵抗しないかもしれません.
前に言ったNode原生モジュールが重視されていない原因は、Node自身がC++プラグインのメカニズムを複雑に設計しすぎて、V 8の使用を理解し、どのようにNodeを使用するかを理解する必要があるからだ.hの方法とタイプ.さらに悲しいことに、バージョンが変更されるにつれて、コンパイルされたプラグインがリリースされて間もなく、バージョンの互換性がないため、バージョンに適応するために再コンパイルせざるを得なくなります.この時、NAM(Native Abstractions for Node.js)というものが走り出しました.オリジナルプラグインの開発者が毎回のバージョン変更のために新しいAPIを再利用する必要がなく、本来必要でない仕事をすることを助けるためです.リリース8.0まで、Nodeは開発者に新しいN-APIをもたらしました.C++プラグインの開発の歴史をもっと知りたいなら、死月先生のことをお勧めします.
暴力からNANまでNAPIまで--Node.js原生モジュール開発方式の変遷
N-APIとは
公式の彼の説明は:
N-API (pronounced N as in the letter, followed by API) is an API for building native Addons. It is independent from the underlying JavaScript runtime (ex V8) and is maintained as part of Node.js itself. This API will be Application Binary Interface (ABI) stable across versions of Node.js. It is intended to insulate Addons from changes in the underlying JavaScript engine and allow modules compiled for one version to run on later versions of Node.js without recompilation. Addons are built/packaged with the same approach/tools outlined in the section titled C++ Addons. The only difference is the set of APIs that are used by the native code. Instead of using the V8 or Native Abstractions for Node.js APIs, the functions available in the N-API are used. APIs exposed by N-API are generally used to create and manipulate JavaScript values. Concepts and operations generally map to ideas specified in the ECMA262 Language Specification.
N-APIは、ローカルプラグインを構築するAPIです.JavaScriptの実行時(例えばv 8)に依存するのではなく、Nodeとして機能する.js自身の構築の一部が実行されます.このAPIは、アプリケーションバイナリインタフェースとして機能する(ABIは、アプリケーションとオペレーティングシステムとの間、アプリケーションとそのライブラリとの間、またはアプリケーションのコンポーネント間の低インタフェースを記述する.)安定してるjsの中.その目的は、プラグインがJSエンジンの最下位実装を変更し、新しいバージョンごとにノードを再コンパイルすることなく、ノードを一度コンパイルできるようにすることです.
このプラグインの構築とパッケージングは、一般的なC++プラグインと同じ方法と応用を使用します.唯一の違いは、このAPIのセットがオリジナルコードを使用することです.N-APIは、v 8またはローカル抽象のNodeではなくローカル関数を用いる.js API.
N−API露出APIは、通常、JSを作成および操作するための値である.概念と操作のいくつかの思想も通常ECMA 262言語規範に列挙される.
原生プラグインの開発を統一し、原生プラグインにより多くの自由空間を与えるために公式にABIを使用しているパターンが見られます.つまり、開発者は複雑なV 8タイプを考慮せず、落胆させる役割ドメインの破棄保留値の問題を考えずに、Cを発行するようにノードを開発する原生プラグインに入ることができます.
N-APIの意味
筆者はノードの学習者として、このAPIの誕生について何の評価もできない.しかし、ここで私の意味に対する理解を書くことができます.N-APIはまず、互換性に注目するのではなく、開発者に作成したプラグインのために新しい機能を追加する自信を与え、コミュニティがノードのためにオリジナルのプラグインを開発する自信を高めたに違いない.N−APIはJS実行時に依存しない独立したインタフェースとして,それを用いて開発されたプラグインは強制GCや強制V 8タイプを含むV 8に制限されない.これにより開発者により多くの可能性が与えられ,次に科学計算のライブラリがノードに移植され,汎用計算異機種計算をサポートすることも可能になると予測した.開発コストを簡素化し,N−APIはABIとして存在するため,ユーザは一般的なCインタフェースを操作するようにそれらを操作するだけで,スタック開発者のC++素養の要求を極めて低減した.より高性能なオリジナルプラグインを開発する人が増える見通しだ.
まず使ってみましょう
ここで強調すると、N−APIは現在も実験的なインタフェースであり、8.6.0バージョンではデフォルトでオンであり、他のバージョンではノードを実行する前にパラメータオンサポートを設定する必要がある.そして実験的な特長なので、バージョンの変化は速くなく、現在あなたが調べることができるすべての例は走ることができないかもしれません.
各バージョンの完全なインスタンスを見たい場合は、Node 8に移動します.0.0以降のバージョンのソース
addons-napi@Node
中にはすべての例のAPIの使用方法があり、私の8.7.0バージョンでコンパイルして実行することができます.NAPIを用いる方法と,C++プラグインの構築は同じであることが分かる.まずプロジェクトディレクトリを生成します
n-api_test/
|-------- /test.js
|-------- /addon.c
|---------/binding.gyp
|---------/common.h

これhは、プラグインが使用する事前定義マクロを含むファイルであり、ソースコードで取得してbindingの作成を開始することができる.gyp
//binding.gyp
{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "addon.c" ]
    }
  ]
}

最初のプラグインaddonを書き始めました
//addon.c

#include 
#include 
#include "./common.h"

napi_value Method(napi_env env, napi_callback_info info) {
  napi_value world;
  const char* str = "world";
  size_t str_len = strlen(str);
  NAPI_CALL(env, napi_create_string_utf8(env, str, str_len, &world));
  return world;
}

napi_value Init(napi_env env, napi_value exports) {
  napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("hello", Method);
  NAPI_CALL(env, napi_define_properties(env, exports, 1, &desc));
  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
//common.h
#define NAPI_RETVAL_NOTHING  // Intentionally blank #define

#define GET_AND_THROW_LAST_ERROR(env)                                    \
  do {                                                                   \
    const napi_extended_error_info *error_info;                          \
    napi_get_last_error_info((env), &error_info);                        \
    bool is_pending;                                                     \
    napi_is_exception_pending((env), &is_pending);                       \
    /* If an exception is already pending, don't rethrow it */           \
    if (!is_pending) {                                                   \
      const char* error_message = error_info->error_message != NULL ?    \
        error_info->error_message :                                      \
        "empty error message";                                           \
      napi_throw_error((env), NULL, error_message);                      \
    }                                                                    \
  } while (0)

#define NAPI_ASSERT_BASE(env, assertion, message, ret_val)               \
  do {                                                                   \
    if (!(assertion)) {                                                  \
      napi_throw_error(                                                  \
          (env),                                                         \
        NULL,                                                            \
          "assertion (" #assertion ") failed: " message);                \
      return ret_val;                                                    \
    }                                                                    \
  } while (0)

// Returns NULL on failed assertion.
// This is meant to be used inside napi_callback methods.
#define NAPI_ASSERT(env, assertion, message)                             \
  NAPI_ASSERT_BASE(env, assertion, message, NULL)

// Returns empty on failed assertion.
// This is meant to be used inside functions with void return type.
#define NAPI_ASSERT_RETURN_VOID(env, assertion, message)                 \
  NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING)

#define NAPI_CALL_BASE(env, the_call, ret_val)                           \
  do {                                                                   \
    if ((the_call) != napi_ok) {                                         \
      GET_AND_THROW_LAST_ERROR((env));                                   \
      return ret_val;                                                    \
    }                                                                    \
  } while (0)

// Returns NULL if the_call doesn't return napi_ok.
#define NAPI_CALL(env, the_call)                                         \
  NAPI_CALL_BASE(env, the_call, NULL)

// Returns empty if the_call doesn't return napi_ok.
#define NAPI_CALL_RETURN_VOID(env, the_call)                             \
  NAPI_CALL_BASE(env, the_call, NAPI_RETVAL_NOTHING)

#define DECLARE_NAPI_PROPERTY(name, func)                                \
  { (name), 0, (func), 0, 0, 0, napi_default, 0 }

#define DECLARE_NAPI_GETTER(name, func)                                  \
  { (name), 0, 0, (func), 0, 0, napi_default, 0 }
$ node-gyp configure &&node-gyp buildファイルを構成してコンパイルします.テストのjsファイルを作成します

//test.js
var assert = require('assert');
var addon = require(`./build/Release/addon.node`);
console.log(addon.hello());
$node test.js正しい出力を得る
world
(node:27082) Warning: N-API is an experimental feature and could change at any time.

まとめ
最新版のNAPIデザインでは、直接Cを言語として使用することもあり、開発の難易度を大幅に低減しています.これまで私の文章で述べたNAPIの特性を利用して2 Gを超える連続メモリ空間を使用することも可能で、これからも完成し続けます.