概要

node v8.0.0 がリリースされました。このバージョンからWebAssemblyがデフォルトで使用できるようになったので、試しにnode module(re2のvalidateするだけ)を作ってみました(re2-validator)。

準備

submoduleにre2を引っ張ってきてre2をconfigure makeします。

$ cd re2
$ emconfigure ./configure
$ make

コンパイル

emccで普通にコンパイルします。

$ emcc -Ire2 --bind -o dist/re2.js  re2_binding.cc re2/obj/libre2.a -s WASM=1 --llvm-lto 1 --llvm-opts 3 -s NO_FILESYSTEM=1  -s EXPORTED_RUNTIME_METHODS="[]" --closure 1 -Oz --pre-js pre.js --post-js post.js

ここでre2本体以外にre2_binding.cc、pre.js、post.jsという自作のファイルを使用しています。それぞれ説明すると、

re2_binding.ccで関数をModule._validateから使えるようにします。

re2_binding.cc
#include "re2/re2/re2.h"
#include <emscripten/bind.h>

using namespace emscripten;
using std::string;

bool validate(const string& str) {
  RE2 re(str, RE2::Quiet);
  return re.ok();
}

EMSCRIPTEN_BINDINGS(_re2_) {
  function("_validate", &validate);
}

pre.jsでModule.wasmBinaryFileを設定して、正しいパスを指すようにします。これをやらないとパスがずれます。

pre.js
var Module = {};
if (typeof __dirname === "string") Module["wasmBinaryFile"] = __dirname + "/re2.wasm";

post.jsで実際にJavaScriptから使いやすいように加工します(emccからclosure compilerのオプションを設定する方法がわからなかったので、es5的な感じで)。validate関数で文字列をutf8のUint8Arrayに変換してModule._validateに渡します。readyModule._validateの準備が完了するまで待つPromiseです。emscripten内部ではWebAssembly.instantiateを使用して非同期にwasmモジュールがコンパイル、インスタンス化されるので必要です(対話環境だったら、あんまり気にしないですけどね)。

post.js
var re2_buffer = new Uint8Array(1024);

module.exports['validate'] = function(s) {
  var size = stringToUTF8Array(s, re2_buffer, 0, 1024);
  return Module['_validate'](re2_buffer.subarray(0, size));
};

module.exports['ready'] = new Promise(function(resolve) {
  var intervalId = setInterval(function() {
    if (!Module['_validate']) return;
    clearInterval(intervalId);
    resolve();
  }, 1);
});

動作確認してみます。

$ node
> var re2Validator = require('./dist/re2');
> re2Validator.validate('(?!hello)'); // false

publishしたライブラリを使用する

すでに、node moduleとしてpublishしたものとします。npm iして、まぁ、動作確認でやったことなんですが、動かしてみます。

$ npm i --no-save @ukyo/re2-validator
$ node
> var re2Validator = require('@ukyo/re2-validator');
> re2Validator.validate('(?!hello)'); // false

これでWebAssemblyを使用したnode moduleを公開できましたね!

まとめ

node v8.0.0で動作する、WebAssemblyを使用したnode moduleを作ってみました。node環境では特に問題なく動くことが確認できました。ブラウザ環境でwasmのパスどうするんだっていう課題はありますね(wasmを配列とかbase64にして埋め込むしかないのかな?)。