node-gyp rebuildのerrorを解決する

Node.jsの開発スピードが速すぎて、公開しているNode-REDのライブラリがいつの間にかnpm installできなくなっていました。このライブラリはC++のドライバにアクセスするためNode.jsのC++ Addonを利用していますが、npm install時のnode-gypで失敗しているようでした。V8 API[^1]のトラブルシューティングに関しては、検索してもなかなか情報が見つからず、参考になれば幸いです。

ちなみに、本家のサイト[^2]によるとライブラリの開発には

* N-API
* nan
* direct use of internal V8, libuv and Node.js libraries

の3つがあると書かれていますが、ここで扱うのは3つ目のタイプです。

# Node.jsのversion

10.xまでは問題なかったのですが、11.xからエラーが出るようになりました。現在最新の13.11を対象としています(※2020/05/15追記：もう最新ではなくなりました)。ちなみに、11.xからV8のMajor versionが6から7に上がっています。[^3]

#Object->Set()でエラー

Maybe versionになったため、引数にcontextを追加で渡せとのエラーです。Maybe versionの場合は.ToChecked()で返します。[^4]

###ログ
```cpp
../nodes/***/***_wrap.cc:286:46: error: no matching function for call to ‘v8::Array::Set(int&, v8::Local<v8::Integer>)’
     dst_addr->Set(i,Integer::New(isolate,tmp));
                                              ^
In file included from /home/***/.cache/node-gyp/13.11.0/include/node/node.h:67:0,
                 from ../nodes/***/***_wrap.cc:29:
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3547:37: note: candidate: v8::Maybe<bool> v8::Object::Set(v8::Local<v8::Context>, v8::Local<v8::Value>, v8::Local<v8::Value>)
   V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context,
                                     ^~~
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3547:37: note:   candidate expects 3 arguments, 2 provided
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3550:37: note: candidate: v8::Maybe<bool> v8::Object::Set(v8::Local<v8::Context>, uint32_t, v8::Local<v8::Value>)
   V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context, uint32_t index,
                                     ^~~
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3550:37: note:   candidate expects 3 arguments, 2 provided

```

### コード

```cpp

// 修正前
Local<Array> dst_addr = Array::New(isolate,4);
...
dst_addr->Set(i,Integer::New(isolate,tmp));

// 修正後
Local<Context> context = isolate->GetCurrentContext();
Local<Array> dst_addr = Array::New(isolate,4);
...
dst_addr->Set(context,i,Integer::New(isolate,tmp)).ToChecked();

```

# Object->Set()でエラー その2

別パターンですが、Maybe versionに関するエラーです。同様にcontextを渡して、.ToChecked()で返します。String::NewFromUtf8()の引数、戻り値の型もMaybeLocalに変わったので、引数にNewStringType::kNormalを追加し、.ToLocalChecked()で返します。

### ログ
```cpp
../nodes/***/***_wrap.cc:292:83: error: no matching function for call to ‘v8::Object::Set(v8::MaybeLocal<v8::String>, v8::Local<v8::Integer>)’
    obj->Set(String::NewFromUtf8(isolate,"header"),Integer::New(isolate,mac.header));
                                                                                   ^
In file included from /home/***/.cache/node-gyp/13.11.0/include/node/node.h:67:0,
                 from ../nodes/***/***_wrap.cc:29:
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3547:37: note: candidate: v8::Maybe<bool> v8::Object::Set(v8::Local<v8::Context>, v8::Local<v8::Value>, v8::Local<v8::Value>)
   V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context,
                                     ^~~
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3547:37: note:   candidate expects 3 arguments, 2 provided
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3550:37: note: candidate: v8::Maybe<bool> v8::Object::Set(v8::Local<v8::Context>, uint32_t, v8::Local<v8::Value>)
   V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context, uint32_t index,
                                     ^~~
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3550:37: note:   candidate expects 3 arguments, 2 provided

```

### コード
```cpp
// 修正前
obj->Set(String::NewFromUtf8(isolate,"header"),Integer::New(isolate,mac.header));

// 修正後
obj->Set(context,(String::NewFromUtf8(isolate,"header",NewStringType::kNormal)).ToLocalChecked(),Integer::New(isolate,mac.header)).ToChecked();

```

# BooleanValue()でエラー

BooleanValueの引数が空なので、Isolate* isolateを引数で渡せとのエラーです。

※V8 7.4の前後でエラーの内容が変わるので両対応しています。

### ログ

```cpp
../nodes/***/***_wrap.cc:196:33: error: no matching function for call to ‘v8::Value::BooleanValue()’
  latest = args[0]->BooleanValue();
                                 ^
In file included from /home/***/.cache/node-gyp/13.11.0/include/node/node.h:67:0,
                 from ../nodes/***/***_wrap.cc:29:
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:2771:8: note: candidate: bool v8::Value::BooleanValue(v8::Isolate*) const
   bool BooleanValue(Isolate* isolate) const;
        ^~~~~~~~~~~~
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:2771:8: note:   candidate expects 1 argument, 0 provided

```

### コード

```cpp
// 修正前
static void foo(const FunctionCallbackInfo<Value>& args) {
	Isolate* isolate = args.GetIsolate();
	latest = args[0]->BooleanValue();
	args.GetReturnValue().Set(Boolean::New(isolate,true));
	return;
}

// 修正後
static void foo(const FunctionCallbackInfo<Value>& args) {
	Isolate* isolate = args.GetIsolate();
#if (V8_MAJOR_VERSION > 7) || ((V8_MAJOR_VERSION == 7) && (V8_MINOR_VERSION >= 4))
	latest = args[0]->BooleanValue(isolate);
#elif (V8_MAJOR_VERSION >= 7)
	Local<Context> context = isolate->GetCurrentContext();
	latest = args[0]->BooleanValue(context).ToChecked();
#else
	latest = args[0]->BooleanValue();
#endif
	args.GetReturnValue().Set(Boolean::New(isolate,true));
	return;
}
```

# Object->Get()でエラー

Object->Set()と同様。Maybe versionに関するエラー。

###ログ

```cpp
../nodes/***/***_wrap.cc:449:26: error: no matching function for call to ‘v8::Array::Get(int)’
  dst_addr[0] = arr->Get(0)->NumberValue(context).FromMaybe(0);
                          ^
In file included from /home/***/.cache/node-gyp/13.11.0/include/node/node.h:67:0,
                 from ../nodes/***/***_wrap.cc:29:
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3594:43: note: candidate: v8::MaybeLocal<v8::Value> v8::Object::Get(v8::Local<v8::Context>, v8::Local<v8::Value>)
   V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,
                                           ^~~
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3594:43: note:   candidate expects 2 arguments, 1 provided
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3597:43: note: candidate: v8::MaybeLocal<v8::Value> v8::Object::Get(v8::Local<v8::Context>, uint32_t)
   V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,
                                           ^~~
/home/***/.cache/node-gyp/13.11.0/include/node/v8.h:3597:43: note:   candidate expects 2 arguments, 1 provided

```

###コード

```cpp
// 修正前
dst_addr[0] = arr->Get(0)->NumberValue(context).FromMaybe(0);

// 修正後
Local<Context> context = isolate->GetCurrentContext();

dst_addr[0] = arr->Get(context,0).ToLocalChecked()->NumberValue(context).FromMaybe(0);

```

# まとめ

Node.jsの10.x(V8 6.8)から11.x(V8 7.0)でV8 APIに大きな変更が入り[^5] (※2020/05/15追記：予告されていたDeprecatedなAPIを削除した、が正解でした)、全体的にMaybe versionが適用されているAPIでnode-gypエラーが出ていました。V8の文法に慣れず読み解くのに苦労しました。

[^1]: v8.dev (https://v8.dev)
[^2]: C++ Addons (https://nodejs.org/api/addons.html#addons_c_addons)
[^3]: Previous Releases (https://nodejs.org/en/download/releases/)
[^4]: Use Maybe version of V8 APIs (https://github.com/joyeecheung/node/blob/v8-maybe-doc/CPP_STYLE_GUIDE.md#use-maybe-version-of-v8-apis)<br>v8/node deprecated APIs and how to handle them #7 (https://github.com/bcoin-org/bcrypto/issues/7)
[^5]: V8 API changes (https://docs.google.com/document/d/1g8JFi8T_oAE_7uAri7Njtig7fKaPDfotU6huOa1alds/edit)