中国の動画配信サービス「ビリビリ(bilibili)」から動画をダウンロードしたつもりが、
wasm(ワスム)
という拡張子のバイナリファイルが落ちてきてVLCで再生できなかった。
なんだこれ?
ググってみると、
「WASM(ワスム)」は、Chrome、Mozilla Firefox、Microsoft Edge、SafariなどのWebブラウザーで実行されるWebAssembly形式で保存されたバイナリコードが含まれている。
らしい。
WebAssembly(ウェブ アセンブリ)
って大層な名前だね。
C/C++がで書いたコードがコンパイルできてHTMLやJavaScriptを利用してWeb上で動くの?
と思ってググると、そのとおりだった。
C、C++、またはRust、Golang、TypeScriptからコンパイルされたバイナリWebAssemblyコードであり、WebページやアプリにインポートしてJavaScript経由で使用できます。
世の中の進化スゲーーー。
ようするにWebAssemblyとはプログラミング言語やライブラリの名前ではなく、ブラウザでプログラムを高速実行するための、
「ブラウザ上で動くバイナリコードの新しいフォーマット(仕様)」
である。
何も(知ら)ないから何か見つかる。
出会いはいつも突然に。
ちょっと使ってみよう。
C/C++によるWebAssemblyのhello world
ググってみると日本語の記事は多くない。
C++をEmscriptenでコンパイルしてブラウザ上で動かしてみた | DevelopersIO
C++をEmscriptenを使ってWebAssemblyへコンパイルしてブラウザ上で動かしてみました!
【私の環境】
- Windows10(CPU:Intel Core i5 2.40GHz、RAM:16.0GB、HDD 250GB)
- gnupack_devel-13.05-2015.07.19 (cygwin)
インストール
Cygwin上で、マニュアルに従ってコマンドを打つだけ。
|
git clone https://github.com/emscripten-core/emsdk.git # Enter that directory cd emsdk |
とすれば……格好良いけど git をインストールしてないので、サイトからZIPで落としてくる。
GitHub - emscripten-core/emsdk: Emscripten SDK
Emscripten SDK. Contribute to emscripten-core/emsdk development by creating an account on GitHub.
インストール&有効化コマンドは次のとおり。
|
git clone https://github.com/emscripten-core/emsdk.git cd emsdk-main ./emsdk install latest ./emsdk activate latest cd .. |
実装&起動
ソースコードは次のようになる。
|
#include <stdio.h> int main() { printf("Hello World\n"); return 0; } |
コンパイル方法は少し特殊。
|
# コンパイル方法 emsdk-main/upstream/emscripten/em++ hello.cpp -s WASM=1 -o hello.html |
「-s WASM=1」がコンパイル後にwasmファイルを出力するオプションだ。
なおC言語のソースをコンパイルには「emcc」を使う。
これでローカルに3つのファイル「hello.html」「hello.js」「hello.wasm」ができる。
あとは WebAssembly に対応しているブラウザーで hello.html を読み込むだけ。
|
# 実行方法 /C/Python311/python.exe emsdk-main/upstream/emscripten/emrun.py hello.html |
ローカルの場合には「localhost」にブラウザが起動して画面表示される。
実際のサーバ上で確認する場合には前述の3つのファイルをUploadすれば動作する。
超簡単だった。
なお、既定で有効なのは Firefox 52、Chrome 57、Opera 44 以降らしい。
「Chrome 57」は、2017年3月29日にリリースされているので6年前からある技術だったのか……。
オラーリの日本語書籍は半年前に発売なので、時期的には丁度よいのかも。
C/C++によるWebAssemblyをJava Scriptから呼び出す
ググってみると、さらに入門編の記事は少ない。
WebAssemblyでC++とJavaScript間のやり取り - Qiita
はじめに昨日の私の記事では、Qt for WebAssemblyを使ったGUIアプリの開発方法を紹介しました。今日の記事ではQtは一切関係ありません。C++とJavaScriptとの間で、関数を呼…
とりあえず記事に従って進めてみよう。
HTMLの修正
まず hello.html を index.html にコピーして編集する。
1200行以上ある WebAssemblyモジュールをロードする以外のヴィジュアル要素を全て取り除く。素敵!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
<!doctype html> <html lang="en-us"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>wasmtest</title> </head> <body> <script type='text/javascript'> var Module = { preRun: [], postRun: [], print: (function() { return function(text) { if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); console.log(text); }; })(), printErr: function(text) { if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); //console.log(text); }, totalDependencies: 0, monitorRunDependencies: function(left) { this.totalDependencies = Math.max(this.totalDependencies, left); } }; console.log('Downloading...'); window.onerror = function(event) { // TODO: do not warn on ok events like simulating an infinite loop or exitStatus }; </script> <script type="text/javascript" src="hello.js"></script> <script> Module.onRuntimeInitialized = function(){ _initialized(); } </script> <button onclick="_clicked1()">OK</button> <input id="input1" value="hogehoge"> <div id="contents"></div> </body> </html> |
とりあえず「button」「input」「div」それぞれ入れてみた。
C++コードの修正
サンプルのソースコードは断片的に記載があったので、何度もエラーに悩まされたけどコード全体としては次のようになった。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
#include <emscripten.h> #include <string> #include <stdio.h> // EM_JS(戻り型, 関数名, 仮引数, { スクリプトコード }); EM_JS(void *, getElementValue_, (char const *id), { console.log('called getElementValue_...'); var e = document.getElementById(UTF8ToString(id)); var str = e.value; var len = lengthBytesUTF8(str) + 1; // コンパイル時に -s EXPORTED_FUNCTIONS="['_malloc']" しないとエラーがでる var heap = _malloc(len); stringToUTF8(str, heap, len); return heap; }); // C++から次のようにvalueの値を取得 std::string getElementValue(std::string const &id) { void *p = getElementValue_(id.c_str()); std::string s((char const *)p); free(p); return s; } void setElementValue(std::string const &id, std::string const &value) { // C/C++ のコード内で JavaScript を記述 EM_ASM({ console.log('called setElementValue...'); var e = document.getElementById(UTF8ToString($0)); // UTF-8文字列をJavaScriptの文字列に変換 e.value = UTF8ToString($1); }, id.c_str(), value.c_str()); } void setElementInnerHTML(std::string const &id, std::string const &html) { // C/C++ のコード内で JavaScript を記述 EM_ASM({ console.log('called setElementInnerHTML...'); var e = document.getElementById(UTF8ToString($0)); e.innerHTML = UTF8ToString($1); }, id.c_str(), html.c_str()); } // モジュールの初期化が終わるまで待って呼びだす extern "C" void EMSCRIPTEN_KEEPALIVE initialized() { puts("initialized"); } // ボタンが押された extern "C" void EMSCRIPTEN_KEEPALIVE clicked1() { puts("clicked1"); setElementInnerHTML("contents", "<p style='color:red'>Hello, world</p>"); std::string value = getElementValue("input1"); puts(value.c_str()); } |
結局「_malloc」を使うときに「-s EXPORTED_FUNCTIONS=”[‘_malloc’]”」がビルドオプションに無かったのが問題だった。
|
# コンパイル方法 emsdk-main/upstream/emscripten/em++ -s EXPORTED_FUNCTIONS="['_malloc']" hello.cpp -s WASM=1 -o hello.html |
動作画面は寂しいけど、ブラウザ上からC++のコードを呼び出しできている。
何か嬉しい。
デバッグ方法(不具合解析)
コンパイル済のバイナリファイルなので、どこで死んだのか分からない。
そして毎回ログ入れてコンパイルし直すなんて時間の無駄だよ。
そんな時は「-g」オプションが使えるようだ。
|
# コンパイル方法 emsdk-main/upstream/emscripten/em++ -g hello.cpp -s WASM=1 -o hello.html |
で「hello.wasm」だけ差し替える。
新しいChrome拡張機能で、C++ソースファイルをステップ実行してコンパイル済みWasmコードをデバッグ
Googleは最近、WebAssemblyファイルのデバッグに関して開発者エクスペリエンスを向上させるためにChrome DevToolsチームが行ったことの進捗状況を発表した。新しい拡張機能(ベータ版)を使用すると、開発者はオリジナルのソースコードをステップ実行することで、WebAssemblyにコンパイルされたCお...
Chromeの場合は「︙」→「その他のツール」→「デベロッパーツール」→「Console」を確認する。
【-gオプション無し(17KB)】
【-gオプションあり(5220KB)】
Call stackが関数名で表示されるようになった。
おわりに
確かに動いた。
技術の進化は凄まじい。
Java Scriptって難読化はできるけどソースコード見えちゃうし、PHPと連携させるとサーバ負荷や処理速度が遅いからWebサービス作るのは以前諦めた。
数十年前に出会っていたら色々と実装してた……かもしれない。