LoginSignup
3
3

More than 5 years have passed since last update.

EmscriptenでJavaScriptエンジンduktapeをビルドする

Last updated at Posted at 2019-04-20

はじめに

JavaScriptエンジンの実装の一つであるduktapeをブラウザ上で動作させたい。と思いEmscriptenを使うことにしました。

動機

まぁブラウザ自体がJavaScriptエンジンを持っているのになぜわざわざduktapeを動作させたいのか? というと、以前記事を書いた esp32-arduinoでJavaScript(duktape)を使う
と関係があります。

今私はマイコンモジュールであるESP32を利用したゲーム機「o-bako」を作っています。

image.png

現状o-bakoではLuaを使ってゲームを記述できるようになっています。
そしてWebブラウザで動作するゲーム機のシミュレータも作りました。ここではブラウザ上で動作するLuaランタイムであるfengariを利用しました。

image.png

さて、ここまで作ったのですが、Luaというのは結構マイナーな言語です。できればもう少しメジャーな言語スクリプト言語でゲームが書きたいな・・と思い始めました。

そこで目を付けたのがduktapeです。duktapeはLuaとよく似たAPIを提供しているJavaScriptのエンジンです。
esp32-arduinoでJavaScript(duktape)を使うで検証したようにESP32でも動作します。

あとはブラウザシミュレータをどのように実装するかです。duktapeはJavaScriptのサブセットであるためブラウザのJavaScriptエンジンとは微妙に互換性が無いので、ブラウザのJavaScriptエンジンを使うのは難しそうです。またセキュリティの観点からもブラウザのJavaScriptエンジンで任意のコードを動かすのはあまりやりたくありません。

ということでEmscriptenでduktapeをビルドしよう!と思いついたわけです。

教えて!

ここまでのところで、「いやいやそういうことなら、この言語のほうが良いよ!」とか「ブラウザ上で安全にJavaScirptを評価するお勧めの方法があるよ!」といった知見をお持ちの方はぜひ教えてください!

とりあえず私はEmscriptenでduktapeをビルドするという方法を選んだので、以降その方法を紹介します。

duktapeとEmscripten

もともとduktapeはEmscriptenでのコンパイルもできるように作られていましたが、その成果物がリリースに含まれていません。
Makefileを見る限りこれはデモページのために作られているようです。

手っ取り早く成果物を手に入れるためにはデモページに含まれるファイルdukweb.js をダウンロードすればよさそうですが、せっかくなので自分でビルドしてEmscriptenを作ろうと思いました。

王道(面倒なのでやらない)

duktapeはMakefileにより成果物を作り出すようになっておりdukweb.jsもこれによって作られます。

つまり、このMakefileを手元で実行すればよいのですが・・

#
#  Makefile for the Duktape development repo.
#
#  This Makefile is intended for ONLY internal Duktape development
#  on Linux (or other UNIX-like operating systems), and covers:
#
#    - Building the Duktape source distributable.
#    - Running some basic test cases.
#    - Building the duktape.org website.
#
#  The Makefile now also works in a very limited fashion with Cygwin,
#  you can 'make dist' as long as you have enough software installed.
#
#  The source distributable has more platform neutral example Makefiles
#  for end user projects (though an end user should really just use their
#  own Makefile).
#
#  YOU SHOULD NOT COMPILE DUKTAPE WITH THIS MAKEFILE IN YOUR PROJECT!
#

まったくもってお勧めできない方法のようです。

Emscriptenを個別に実行してdukweb.jsを生成する

要はduktape.cとdukweb.cをEmscriptenでビルドすればdukweb.jsのようなものが作れそうなので、Makefileの記述を参考にしながらEmscriptenでビルドすることにしました。

手元の環境はWindowsなので、インストール方法を調べます・・ Windows上でのEmscriptenのセットアップ これかな?

しかし今やコンテナの時代。こんな面倒そうなセットアップをちまちまやりたくありません。

少し調べると https://hub.docker.com/r/trzeci/emscripten/ というコンテナイメージに行き着きました。これを使えばemscriptenのインストールをする必要がありません。(Dockerがインストールしてあれば)

ということで、適当なディレクトリにduktapeのリリース物を展開して、下記を実行します。

$ docker run --rm -v //<適当なディレクトリ>/duktape-2.3.0/duktape-2.3.0:/src -u emscripten trzeci/emscripten emcc src/duktape.c dukweb/dukweb.c  -DEMSCRIPTEN  -O2 --memory-init-file 0 -o out.js --closure 1 -I ./src -s EXPORTED_FUNCTIONS='["_dukweb_is_open", "_dukweb_open","_dukweb_close","_dukweb_eval"]' -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]'

これでout.js out.wasmが得られます。

引数については https://github.com/svaarala/duktape-wiki/blob/master/Portability.md#emscripten などに詳細が載っています。
また EXPORTED_FUNCTIONS はC言語の関数のうちJavaScript側に公開するものの指定のようです。さらにEXTRA_EXPORTED_RUNTIME_METHODSはJavaScriptからC言語側の関数を呼び出す際に必要なユーティリティ関数をJavaScript側に公開する設定のようです。

そしてこれを呼び出すための簡単なHTML・JavaScriptを書きます

<html>
<body>
<script src="out.js"></script>
<script>

function a(){
  console.log("call a");
}

window.addEventListener("load",function(){
  setTimeout(function(){ // とりあえずwasmのロードを待つ
    let dukwebEval = c.cwrap("dukweb_eval", "string", ["string"]); // Cの関数dukweb_evalをJSの関数化
    console.log("load");
    c.ccall("dukweb_open"); // Cの関数dukweb_openを呼び出す
    dukwebEval("Dukweb = {};")
    // duktape内からブラウザのJSを呼び出すための関数
    dukwebEval("Dukweb.eval = this.emscripten_run_script; delete this.emscripten_run_script;")
    // 試しにブラウザのJSを呼び出す。
    dukwebEval("Dukweb.eval('(window.a())')");
  }, 100);
});

</script>
</body>
</html>

一通り動くことが確認できました。
cwrapccallは、様々な説明を見る限りmoduleなどという名前の変数で提供されるのが一般的のようでしたが、今回はなぜかcという名前なのがちょっと気になります。
Emscriptenが出力するJavaScriptがそのように設定しているので、もしかしたらどこかで設定できるのかもしれません。

またdukweb_evalの中でthis.emscripten_run_script関数を呼び出すことで、ブラウザのJavaScriptを呼び出すことができるようです。
この時グローバルの変数にアクセスする場合は明示的にwindowを記載する必要があることに気づかずしばらくハマりました。

注意! 開発者コンソールを開いていると実行速度が遅くなります。

どうも実行速度が遅い・・ と思っていたのですが、どうやら「開発者コンソール」を開いていると速度が落ちるようです。

Slow javascript execution in IE11 until developer tools are enabledこの記事によると「開発者コンソール」を開いているとasm.jsの最適化が行われないようです。

「開発者コンソール」を閉じるといい感じの速度で動くようになりました。
気づいていなくてしばらくハマりました。

まとめ

Emscriptenを使ってduktapeをブラウザ上で動かすことができました。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3