Node.js と FFI の 2020-03-18 時点でのメモ; node-ffi 系 → node-ffi-napi 系
今回のメモの Node.js は:
node -v v13.5.0
です。先ずは Node.js 初心者らしく npm i ffi
して死にました:
npm i ffi
ほか多数のエラーを観測し、死んでしまいました。☠
どんどん使われなくなっているので何か代替に置き換わったのだろうと思い調べてみると、 ffi
は Node.js < 11 の時代で死んでしまったらしい事、 Node >= 11 用には fork した @saleae/ffi
が対応しているらしい事がわかりました。
既にこちらも Weekly download が怪しいですが、とりあえず npm i @saleae/ffi
してみました:
npm i @saleae/ffi
死亡。☠
ここで元々の ffi
の Issues を眺めてみると、
が見つかり、
ffi-napi
が Node >= 12 用に使えるらしいffi-napi
はコールバックの実装にまだ問題があるかもしれないらしい(このメモより14日前の情報)
事がわかりました。コールバックの問題点は、
- Fix crasher in callback calls by atishay · Pull Request #56 · node-ffi-napi/node-ffi-napi · GitHub (このメモより6日前に merged )
- Port fix for Multithreaded callback crash by atishay · Pull Request #50 · node-ffi-napi/node-ffi-napi · GitHub (このメモより2ヶ月くらい前に merged )
あたりについて、特に日付的に #56 の方の注意だと思います。現在は解決しているようです。とりあえず ffi-napi
もインストールチャレンジしてみました:
npm i ffi-napi
しばらくのち:
+ ffi-napi@2.4.7 added 10 packages from 57 contributors, removed 4 packages and audited 17 packages in 18.686s found 0 vulnerabilities
エラーなく導入できました。👍
実際に使う場合は型のバインディングに ref
も欲しいのですが、こちらも node-ffi-napi シリーズの repos が一通りあるので:
npm i ref-napi
します:
npm i ffi-napi
しばらくのち:
+ ref-napi@1.4.3 updated 1 package, moved 1 package and audited 23 packages in 5.993s found 0 vulnerabilities
ref-napi
も導入できました。👍
これでFFIの最低限の準備が整いました。適当に動作テストしてみます。
(1) テスト用に FFI で呼ばれる native な .dll (.so, .dylib) を作ります:
今回はなんとなく Rust で呼ぶと "にゃおーん" 文字列を返してくれる nyanko.dll を作ります。
# Windows でやっている場合は PowerShell で echo すると utf16 になるので echo '#[no_mangle] pub extern fn f() -> * const u8 { "にゃおーん".as_ptr() }' > nyanko.utf16.rs # UTF8 に一手間増えます(Windowsの謎仕様でBOMが付きますが rustc はスルーしてくれます) Get-Content nyanko.utf16.rs | Out-File -Encoding utf8 nyanko.rs # このくらいなら Cargo さんに頑張ってもらわなくても rustc 直呼びで手早く済ませられます rustc --crate-type cdylib nyanko.rs
これで Node.js から ffi-napi により FFI で呼ばれる側の nyanko.dll ができました。一応 nyanko.rs のソースだけ書き残しておくと
#[no_mangle] pub extern fn f() -> * const u8 { "にゃおーん".as_ptr() }
(2) テスト用に FFI で呼ぶ側の Node.js な tester.js を作ります:
echo "console.log( require('ffi-napi').Library( 'nyanko.dll',{ 'f': [ require('ref-napi').types.CString, [] ] } ).f() )" > tester.utf16.js Get-Content tester.utf16.js | Out-File -Encoding utf8 tester.js
ワンライナーにしちゃうとこちらはしんどいですね…。素直に書いたソースは:
var ffi = require('ffi-napi') var ref = require('ref-napi') var s = ref.types.CString var nyanko = ffi.Library( 'nyanko.dll',{ 'f': [ s, [] ] } ) console.log( nyanko.f() )
です😏
Node.js で tester.js を実行して node-ffi-napi / node-ref-napi が期待動作しネイティブライブラリーの nyanko.dll から "にゃおーん" が無事に返り表示されるはずです:
node tester.js にゃおーん
成功しました👍
コンパクト化したコマンドライン
ついでに npm
-> yarn
化。
# テスト用のディレクトリーと npm init していない場合はここから mkdir myapp cd myapp yarn init -y # ffi-napi, ref-napi を追加 yarn add ffi-napi ref-napi # テスト用の native dll を作成 echo '#[no_mangle] pub extern fn f() -> * const u8 { "にゃおーん".as_ptr() }' > nyanko.utf16.rs Get-Content nyanko.utf16.rs | Out-File -Encoding utf8 nyanko.rs rustc --crate-type cdylib nyanko.rs # テスト用の .js を作成 echo "console.log( require('ffi-napi').Library( 'nyanko.dll',{ 'f': [ require('ref-napi').types.CString, [] ] } ).f() )" > tester.utf16.js Get-Content tester.utf16.js | Out-File -Encoding utf8 tester.js # 動作確認 node tester.js にゃおーん