はじめに
Boost.勉強会 #11 東京での発表内容(Boost.勉強会 #11 東京で発表してきた - 凹みTips)で頂きましたご質問について、簡単にですが調査しましたのでまとめます。
質問の返答
Emscripten の行数が 12万行になった理由
結論としては「iostream の中身を全部変換しているから」のようです。
@nagoya313 さんのエントリで、puts しただけでは行数は 2000 行程度とのご指摘がありましたが、こちらでも検証してみた結果、確かに 2000 行程度でした。
発表時には「printf でも同じくらいの行数(12万行)があった」と答えてしまったのですが、発表前の検証で誤ったファイルを開いていたか変換をミスしていたか、いずれにせよミスを犯していたようです。当日、誤情報を発言してしまい申し訳ありませんでした。
人間が読める量になったので、最小限のコードを変換し、ざっと目を通してみました。変換したコードは以下になり、1418 行となりました。
int main() { return 0; }
冒頭に処理系(Node.js、WEB、...)の場合分けがあり、ここでは print や read といった関数を各処理系毎に振り分けています。124 行目あたりからは、型や C の関数と JS のヒモ付を行なっているように見えます。例えば、901 行目あたりからは _memcpy など Cの関数が _ 付きで定義されていますが、これは 427行目の ccall で、
function ccall(ident, returnType, argTypes, args) { return ccallFunc(getCFunc(ident), returnType, argTypes, args); }
から呼ばれると考えられます。ここで ident = memcpy、getCFunc は _ を付加しています。この辺りを読んでいると、_free は JS なので空の関数で定義されているといったコードがあったりと、結構面白いです。
更に下って行くと、974 行目の Browser オブジェクト内では、document.* 系の JS の関数が散見されるので、HTML とのつなぎ込みをやっていて、Canvas 周りのコードに結構行数を費やしているようです。また、クロスブラウザ対応の対応にもいくらかコードを割いています。
main 関数は最後の方、1304 行目で定義されており、以下の順序で呼ばれるようです。
// 1416 行目 run(); // 1353 行目 function run(args) { // 詳細略 ret = Module.callMain(args); } // 1316 行目 Module.callMain = function callMain(args) { // 詳細略 ret = Module['_main'](argc, argv, 0); } // 1304 行目 function _main() { var label = 0; var $1; $1=0; return 0; } Module["_main"] = _main;
そして、puts("hello, world!") を追加した場合に変換した結果と diff ったものが以下になります。
見てみると _puts が 1437 行目に見えますね。他にも _fputs などがあるので、stdio に含まれる関数がひと通り JS に変換されているようです。
更に詳細について知りたい方は本家 wiki を参照すれば色々書いてありそうです。
Emscripten の論文も置いてあるので、時間のあるときに私も読んでみたいです。
おわりに
追加の質問も受け付けておりますので @hecomi までお気軽にご質問下さい。