All Articles

(続) LuaをWebAssemblyにコンパイルして実行する話

(続き) 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お待ちしております :-)

現場からは以上です。