コンテンツへスキップ

TypeScriptからWebAssemblyを生成するAssemblyScriptを試す

WebAssemblyはプログラミング言語ではなく、様々なプログラミング言語から生成されるWebブラウザ上で実行できるバイナリフォーマットになります。基本はRustやC、C++であり、他にもGo、Java、Kotlin、Swiftなど様々なプログラミング言語が対応しています。

しかし複数のプログラミング言語を混ぜて開発するのは要員確保も大変です。そこで注目したいのがTypeScriptからWebAssemblyファイルを生成するAssemblyScriptです。今回はその特徴を紹介します。

JavaScriptの関数を呼び出せる

AssemblyScriptではdeclareを使って関数を定義することで、JavaScript側の関数を呼び出せます。例えば以下はsayHello関数を呼び出しています。

そしてJavaScript側では次のように記述します。mainの中でsayHelloという関数を定義することで、AssemblyScriptからJavaScriptの関数呼び出しを可能にしています。

JavaScriptからAssemblyScriptの関数を呼び出す

逆にJavaScriptからAssemblyScriptの関数を呼び出す場合はexportを使って関数を公開します。型を指定しなければなりません。

これで、WebAssemblyファイル読み込み後の result.instance.exports の中に関数が入ります。

文字列を返す場合

文字列の場合は多少注意が必要です。まず関数の定義の時点で文字列の長さも返すように定義します。

次に受け取った側ではメモリ中の値を読み取り、デコードしなければなりません。

memはWebAssemblyファイルを読み込んだ後 result.instance.exports.memory.buffer で取得できます。

まとめ

WebAssemblyがTypeScriptで書けるようになれば、 TypeScriptだけでフロントエンドの開発が完結できます。もちろんサブセットなので全ての機能が使える訳ではありません。しかし高速な処理が必要になった際に、AssemblyScriptであれば書き慣れた構文でWebAssembly化できるメリットが大きいでしょう。

AssemblyScript/assemblyscript: A TypeScript to WebAssembly compiler 🚀

WebAssemblyファイルをデコンパイルする

WebAssemblyはあらかじめコンパイルされているのでJavaScriptのように実行時にコードをパースする必要がなく、高速に動作します。WebAssemblyのコードはバイナリデータになっているので処理を隠蔽できているように見えますが、デコンパイルを行うことで人間が読める形に変換できます。

今回はwabtを使ってWebAssemblyのデコンパイルを行ってみます。

元になるコード

今回はGoを使っています。一番簡単なコードです。

WebAssemblyにする

このコードをコンパイルしてWebAssemblyにします。

そしてできあがるのが test.wasm です。このファイルは1.3MBありました。

デコンパイルする

ではここからデコンパイルします。その際に使うのがwabtです。WebAssembly用のツールが各種含まれています。ビルド時にはcmakeが必要です。

そして生成されるwasm2watコマンドを使ってデコンパイルします。

デコンパイルすると25.8MBになりました。かなり肥大化しているのが分かります。

コードを見る

デコンパイルされたコードは、WebAssemblyのテキスト版です。

その中に Hello, wasm という文字列も入っています。

つまりWebAssemblyでバイナリ化していたとしても、任意の処理がどこで、どのように行われているか、探そうと思えば探し出せると言うことです。


WebAssemblyでコードをコンパイルしたとしても、100%安全という訳ではありません。Javaと同レベルくらいに考える方が良いかもしれません。隠し方は幾つかありますので、なるべく分かりづらくなる方法を選ぶべきで、安直にキーなどをコードに書かない方が良さそうです。

GoのWebAssemblyでネットワークを利用する

Go1.11からサポートされたWebAssemblyにおいて、ネットワークを利用する方法を紹介します。今回はJSONファイルの取得方法です。

注意点

実際のネットワークアクセスはWebブラウザの開発者ツール、ネットワークタブ内で確認できます。ネットワークアクセスを匿名化したり、CORS制限も適用されますので注意してください。

利用法

今回はCORS制限がなく、APIキーも不要なMicrolinkのコンテンツを取得します。 "net/http" パッケージを使ってネットワークアクセスをします。そして ioutil.ReadAll を使ってコンテンツを取得します。これはバイト文字列になっていますので、 string 関数を使って文字列化します。


これまでのWebAssemblyはDOMやFetch APIなどが使えないのが難点でした。しかしGo版のWebAssemblyによって、その問題はなくなっています。 WebAssemblyの可能性を飛躍的に高めてくれるのではないでしょうか。

GoでWebAssemblyを使ってみよう

Go1.11からWebAssemblyが公式サポートされました。これまでWebAssemblyの開発はRustがメインに使われてきましたが、Goは構文も分かりやすく、非常に良い選択肢になると思われます。

特に面白いのはDOM連携がとても容易な点ではないかと思います。今回はその使い方を紹介します。

まず基本から

Go1.11以降のインストールが終わっている前提とします。例えばコンソールに出力するためには以下のようなコードを書きます。

これをコンパイルします。test.wasmというファイルが作成されます。

Webブラウザで実行する際には、公式に用意されているHTML/JavaScriptファイルを使うと簡単です。

そしてWebサーバを立ち上げて wasm_exec.html を読み込みます。runというボタンがあるので、それを押すと処理が実行されます。

DOM連携

Goの場合、main関数を必ず使うようです。そしてmain関数は引数が使えません。そこで syscall/js パッケージを読み込みます。これを読み込むと、例えば #num1 の値を次のようにして取得できます。

逆にセットする場合は下記のようになります。

Callはメソッドの呼び出し、値の取得はGet、設定はSetと非常にシンプルな形です。例えば足し算の処理は下記のように記述できます。

これで足し算ができます。デモをこちらに置いてありますので体験してみてください。


なお、GoのWASMはサイズが若干大きめです(上記の足し算を行うもので2.5MBあります)。また、関数が使えない(分かっていないだけかも知れません)ので、ファイル数が増えたり、main関数での分岐が必要になるかも知れません。

とは言え、Goの文法でWASMが書けるメリットは非常に大きく、Webアプリケーションの高速化が容易に実現できたり、全体をGoで開発することも夢ではなさそうです。

WebAssemblyがDOM、APIに対応します!

WebAssemblyはWebアプリケーションを高速に処理できる技術ですが、幾つかの欠点もありました。その一つがDOMを処理できないこと、さらにFetch APIやWebAudioなどのAPIが使えない点です。

しかし先日Announcing the web-sys crate! | Rust and WebAssemblyがアナウンスされ、WebAssemblyでもDOMや各種APIの利用が可能となっています。

実際にはラッピング?

今回発表されたのはweb-sysというクレート(パッケージ)です。すでにWebAssemblyに対応しているWebブラウザであれば利用可能となっていることから、WebAssemblyエンジン自体のバージョンアップは不要なようです。そのため、DOMやAPI操作はメインスレッド側のJavaScriptで行っているかと思われます。ただし、Fetch APIで使ったURLなどはWebブラウザ側のソースにはありませんでした(ネットワークログを見ると、どこにアクセスしたかは分かります)。

Hello World

Hello Worldのアラートを出す場合のコードです。

DOM操作

DOMに記述する際のコードです。 web_sys クレートを介してWindowやDocumentにアクセスできます。

Fetch API

Fetch APIを使って外部リソースを取得するコードです。Promiseを使います。


rustwasm/wasm-bindgen: Facilitating high-level interactions between wasm modules and JavaScriptに多数のサンプルが掲載されていますので、ぜひコードを見てください。動いているコードはExamples – The wasm-bindgen Guideにて確認できます。WebGLやCanvas、WebAudioなどのサンプルが掲載されています。

WebAssemblyはどれくらい速いのか

WebAssembly(WASM)はWebブラウザ上で動くプログラムです。JavaScriptとの違いはコンパイルされた実行ファイルであることで、JavaScriptのようにパースする必要がないので高速に処理されます。

高速とは言っても、どれくらい速いのか実行してみないと分からないでしょう。そこで試した結果を紹介します。

フィボナッチ関数を実行する

フィボナッチ関数はJavaScriptで書くと次のように表現されます。

例えば5のフィボナッチ数は8、6のフィボナッチ数は13になります。小さな数であれば一瞬で求められますが、40くらいの数字になると時間がかかるようになります。同じ関数をRustで書くと次のようになります。

計算する

では実際に計算を行ってみた結果です(macOS Safariでの実行結果。ハードウェアの性能によって数値は異なります)。単位はmsです。

  JavaScript WASM
20 2 1
30 69 4
40 6,733 423
42 17,356 1,061
44 46,195 2,839

この結果から分かるのは、少ない数であればJavaScriptもWebAssemblyも殆ど変わりませんが、40以上になるとWebAssemblyが圧倒的に高速になります。50になるとJavaScript側で処理するのは現実的ではないでしょう。フィボナッチ関数に限らず、リストのソート処理であったり、ループ処理などはWebAssemblyの方が効率的なはずです。


WebAssemblyではネットワーク関数が使えない、DOMが扱えないと言った制限がありますが(現在解消されようとしています)、計算処理の速度においては使わない手はありません。開発できる言語はRustに限らず、様々な言語があります。ぜひWebAssemblyの活用を検討してください。

PWAハンズオンを開催しました

PWA(Progressive Web Apps)というキーワードに注目が集まっていますが、実際に取り組む機会がないという方は多いようです。そこでhifiveのTodoアプリをベースに、PWAを体験してもらうハンズオンを開催しました。

コンテンツはGitHubにあります

体験してもらったハンズオンコンテンツはhifivemania/pwahandsondocsに置いてあります。内容としてはこれに合わせて進めたので、オンラインで体験してもらうことも可能です。ハンズオンはこの資料を基に各自のペースで進めてもらうのですが、何か分からないところがあればすぐにフォローしてもらえるのが参加メリットになります。

hifiveとは

ハンズオンの最初にhifiveとは何かを紹介しました。今回のハンズオンではTodo投稿機能などにおいてhifiveを使っていますが、PWAを扱う上では必須ではありません。しかしビジネスとしてWebシステムを開発する上で問題になりがちな要員確保や中長期的なメンテナンスという問題においてhifiveは最適な解決策になるべく開発を進めています。これはPWAにおいて役立つ場面があるはずです。

hifive – HTML5企業Webシステムのための開発プラットフォーム – hifive

ハンズオン開始

ハンズオンは前述の通り、各自のペースで行ってもらいます。そのため、ハンズオン中は基本的に静かで黙々と作業されています。進めていく中でつまずくポイントは幾つも、人によっても異なるのでそれらを随時フォローアップしていきました。

今回体験してもらったもの

今回のハンズオンではPWAにおいて以下の要素を体験してもらいました。

  • オンライン/オフライン対応
  • アプリ化
  • Webプッシュ通知

PWAでは他にも要素技術があります。今回のを基礎として、自社やクライアント向けの開発に役立てて欲しいと思います。

次回開催について

PWAハンズオンは好評いただいており、すでに2回目が設定済みです。会場は今回と同じく渋谷のhoops link tokyoさんになります。日時は4月12日(金)19時からです。PWAを体験してみたい方はぜひご参加ください!

PWAハンズオン – connpass