kotlinでWebAssemblyしてみる(サンプルコード読解編)
kotlinを使ったWebAssemblyについて、サンプルコードの読解をしてみたいと思います。
前回は環境構築と、その動作確認のためにサンプルコードのビルドと実行を行いました。今回はそのサンプルコードがどのように動いているのか見てみたいと思います。
サンプルコードはこちら
ビルド周り
前回ビルドを実行する時にbuild.shを実行しましたが、その中身は以下のようになっています。
build.sh
./gradlew build
cp ./build/konan/bin/wasm32/*.wasm web/
cp ./build/konan/bin/wasm32/*.wasm.js web/
内容としては、gradleのビルドタスクを実行した後、成果物を配置しているようです。
build.gradleの中身は以下のようになっています(後ろのコメントアウト部分は省きました)
build.gradle
buildscript {
repositories {
jcenter()
maven { url "http://kotlin.bintray.com/kotlinx" }
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies" }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.moowork.gradle:gradle-node-plugin:$gradle_node_version"
classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:$kotlin_native_version"
}
}
apply plugin: 'konan'
konanArtifacts {
// This artifact will be built only for Linux and Wasm32
program('stats', targets: ['wasm32']) {
srcDir 'src/main/kotlin'
libraries {
useRepo 'lib'
klib 'dom'
}
}
}
通常のgradleと同じように依存関係を記述しています。konanArtifactsの中でプログラムの名前(‘stats’)とビルドのターゲット(wasm32)が指定され、そのソースの位置と使用するライブラリが定義されています。
ソースコード(wasm側)
次にソースコードの中身を見ていきます。ソースコードは
- main.kt
- model.kt
- view.kt
の3つのファイルに記述されています。それぞれ見ていきましょう。
main.kt
このファイルにはmainとloopの二つのメソッドが定義されています。メインルーチンが纏められています。
fun main(args: Array
) - 本プログラムのエントリポイント
- domライブラリを使用してIdがmyCanvasの要素をCanvasとして取得
- 1000msごとにloopメソッドに取得したCanvasを引数として渡して実行
fun loop(canvas: Canvas)
- /stats.jsonからデータを取得
- 取得後の内容を引数としてModel.push()の実行
- View(canvas).render()を実行してキャンバスの表示の更新を行う
model.kt
このファイルにはModelオブジェクトが定義されています。表示するデータを保持するオブジェクトのようです。
- object Model
- 概要
- 各種データが定義されている
- メソッド
- fun push(new: Array
) - 新しく受け取ったデータをメンバ変数にセットする
- igotdata()を実行している
- ファイル最後尾で’imported_igotdata’としてigotdata()を外部に公開
- こうすることでWebassembly側からjsのリスナーに通知を送ることが出来る
- fun push(new: Array
- 概要
view.kt
このファイルにはStyleオブジェクトとLayout,Viewクラスが定義されています。canvasへの描画処理を行っています。
object Style
- 概要
- 背景色など、スタイルに関するデータの定義
- 概要
open class Layout(val rect: DOMRect)
- 概要
- キャンバスへの描画処理に必要なデータの定義
- 概要
class View(canvas: Canvas): Layout(canvas.getBoundingClientRect())
- 概要
- キャンバスへの描画処理の実装
- 注目すべきはcontextの扱いです。main.ktでdocument.getElementByIdが使われていたように、ライブラリを活用することで通常のjsと同じメソッドで同じような処理を実行しています。
- 概要
ソースコード(フロント側)
フロント側のソースコードはindex.htmlのみです。その中でメインの処理が書かれている部分を抜粋します。
index.htmlの一部
<script src="./stats.wasm.js"></script>
<script>
konan.libraries.push({"imported_igotdata":function(msg){ alert("I got the data, updating");}})
var filename = "./stats.wasm";
fetch(filename).then( function(response) {
return response.arrayBuffer();
}).then(function(arraybuffer) {
instantiateAndRun(arraybuffer, [filename]);
});
</script>
./stats.wasm.jsはビルドすると生成されるファイルです。
konan.libraries.push({"imported_igotdata":function(msg){ alert("I got the data, updating");}})
ここでmodel.ktで外部に公開していたimported_igotdataが登場します。wasm側でigotdata()が実行される度に、こちらでアラートが実行されます。
おわりに
雑な解説でしたがサンプルコードは以上のような構造となっていました。個人的には、wasm側からフロント側に簡単に通知する方法があったり、用意されたライブラリがjsにある機能をそっくりそのまま実装しちゃっているので思ってたよりも非常にお手軽に出来ると感じました。さすがにこれでjsがいらなくなる程になるわけではなさそうですが、wasmだけでも色々なことができそうです。
次回はkotlin/nativeのwasmとjsで計算速度の比較を行ってみたいと思います。