(続き) LuaでもWebAssemblyがしたかった(した)
前回に続いて一通り動かせるようになったの書きます。できるようになったのは以下の通り:
複数の型付き引数、戻り値の型指定ができるようになった
これができないと全く実用性がないのでサポートしました。 方法については色々悩んだ結果、JSのpackage.jsonっぽくdefinition.ymlという設定ファイルに定義して、それを元にコンパイルすることで実現しました。
例えば、
# エントリーファイル。webpackとかrollupでいうinput的なもの
entry_file: hello_world.lua
# 関数の型定義。ここに書いたものだけが実際にWASMにエクスポートされる
functions:
hello_world:
args:
- int
- string
return: string
# 依存モジュール定義。luarocksで入る
dependencies:
- xxx
- yyy
というような定義を書くと、引数がint, stringで2つ、戻り値がstringのhello_worldという関数がWASMで定義されるようになります。複数の関数もフィールド追加で可能です。
Lua側では、エクスポートする関数をグローバル空間に定義する必要があります。つまり、通常は local キーワードでローカル関数にしますが、あえてキーワードをつけずに定義します。 グローバル関数どうなのよとは思いますが、Cから関数呼ぶのが簡単だったので。
function hello_world(numundefined str)
return ('Hello World!undefined args: %d and %s'):format(numundefined str)
end
とすることで定義と一致させます。関数の存在チェックは実行時に行うので、コンパイルは通っても実行時に関数が未定義のエラーになる可能性はあります。 この辺はコンパイル時に検証しても良かったですが、後述の通り生成スクリプトをPythonにしてしまったので、Luaランタイムを触らなくなりました。とりあえずは定義を信頼するということで。
依存モジュールをバンドルできるようになった
上の定義ファイルで dependencies の配列に記述したLuarocksのモジュール(インストール時にコンパイルするCライブラリも含めて)をWASMにバンドルできるようにしました。これもできないと実用性低いなと思ったので。 やってることは、luarocksコマンドに --tree=[dir] というオプションを渡すと、そのディレクトリ内にモジュールを配置してくれる(パスは通ってない)ので、一時的にここにインストールして、ツリー構造に従って再帰的にライブラリを探索、Luaファイルは中身をバンドルし、Cライブラリはemccのコンパイル時にLinkさせるようにしています。とりあえずCライブラリを使う luaposix というLua向けのPOSIXライブラリをバンドルできたので、他のでも同じく行けるはず。他、自作モジュールは src/ 以下に配置するとバンドルします。
生成手法
必要なファイルを集めてきて、WASMにコンパイル可能なCファイルを生成、ライブラリをリンクしつつemccでコンパイルする、という手順を取っています。コンパイル可能なCファイルの生成はかなり辛い感じでした。 他、Luaにはpakcage.searcherに自作関数を定義できて、require(module)とした時にtableに入れておいてたスクリプト文字列をload(いわゆるeval)して解決させることができたり、色々やっています。 初めはこれもLuaで書いていたんですが、結局便利じゃんってことで生成スクリプトはPythonにしました。Luaでもできそうなので機会があれば書き直すかも。
これにより、必要なランタイムやライブラリが多岐にわたってしまったので、やはりdocker image上で生成してもらうのが良いかと思います。
実行速度
体感として遅いです。というのは、各関数の起動時にLuaのC APIである lua_Stateを生成して、ライブラリをロードして、関数を呼び出して結果を返し、lua_Stateを閉じるようなことをやっているからだと思います。 WASMでもmain()関数は実行されるので、そこでlua_Stateを開いて、それを使いまわした方がいいのかな、とも思いますが、じゃあどこでstateをcloseするんだってことで、今は逐一Lua VMを起動している感じになっています。この辺詳しい人にアドバイスもらいたい、、、
とりあえず
LuaでもWebAssemblyができるようになりました。ホントはtable -> JSON string みたいなものやりたかったけど、大体できて満足したので、とりあえずこれで。 また時間見つけたら実装追加していきたいなーと思います。
ysugimoto/webassembly-luaでやっているので、こうした方が良いとかあればPR/Issueお待ちしております :-)
現場からは以上です。