AssemblyScriptは、TypeScript的な文法でWebAssemblyの .wasm
ファイルを出力できるツールです。
GolangやRustでWebAssemblyを作成する時と違い、Runtimeなどがなく、極めてシンプルです。(言い返せば、必要最小限の機能しか備えていません...)
TL;DL
下記URLはDemoソースコードです。(node.js v8以上が必須)
https://github.com/jerrywdlee/assemblyscript-minimum
Hands On
プロジェクトの初期化
AssemblyScriptはBinaryenを使ってバイナリーファイルをコンパイルしているので、環境によってインストールが時間かかる場合があります。
$ npm init
$ npm i -D AssemblyScript/assemblyscript
$ npx asinit .
ここまではおまじないみたいなものです。
成功すれば、プロジェクト配下はこんな感じになります。
assemblyscript-minimum
|--README.md
|--assembly
| |--index.ts
| |--tsconfig.json
|--build
| |--.gitignore
|--index.js
|--node_modules
|--package-lock.json
|--package.json
コンパイル
プロジェクトが初期化された時、assembly/index.ts
というサンプルファイルが作成されて、一応コンパイルしてみましょう。
$ npm run asbuild
コンパイルをすると、build/
配下で下記ファイルが生成されます。
|--build
| |--.gitignore
+| |--optimized.wasm
+| |--optimized.wasm.map
+| |--optimized.wat
+| |--untouched.wasm
+| |--untouched.wasm.map
+| |--untouched.wat
簡単にいうと、untouched.*
シリーズはコンパイルのみ行って圧縮されていないもので、optimized.*
シリーズは変数名などを圧縮しました(実行するには両方とも一緒)。
*.wasm
はWebAssemblyバイナリーファイルで、*.wat
はS式ベースのテキスト表現らしいです。
ブラウザで実行
index.html
を作成して、optimized.wasm
をロード
<h1>It works!</h1>
<script>
async function loadWasm(url, opt = {}) {
const wasmRaw = await fetch(url).then(res => res.arrayBuffer());
const wasmObj = await WebAssembly.instantiate(wasmRaw, opt);
return wasmObj;
}
loadWasm('build/optimized.wasm', {}).then(wasm => {
console.log(wasm);
});
</script>
ブラウザで確認
プロジェクト配下で何らかの手段でサーバーを立てて、index.html
を確認しましょう。
例えば下記のコマンドのどれかを使えば、localhost:8080にてサーバーが立てられます。
# Python2の場合
$ python -m SimpleHTTPServer 8080
# Python3の場合
$ python3 -m http.server 8080
# Rubyの場合
$ ruby -run -e httpd . -p 8080
# node.jsの場合
$ npx http-server -p 8080
localhost:8080にアクセスして、consoleを確認すると、こんなものがみれます。
矢印のところの関数は assembly/index.ts
の下記関数です。
export function add(a: i32, b: i32): i32 {
return a + b;
}
コンソルに下記コマンドを入れば確認できます。
loadWasm('build/optimized.wasm').then(wasm => {console.log(wasm.instance.exports.add(42, 24))})
軽く解説
fetch
で build/optimized.wasm
をArrayBufferとしてロードし、WebAssembly.instantiate()
で実行可能状態にインスタンス化しています。Exportされた関数はWASM_OBJECT.instance.exports.FUNCTION_NAME
よりアクセスできます。
WebAssembly.instantiateStreaming()
というメソッドについての紹介は散在していますが、おすすめできません。何故ならinstantiateStreaming()
はサーバー側が application/wasm
というMIME typeの提供が必要である上、実装されたブラウザも少ないです(少なくとも2018年12月時点でiPhoneのSafariでは該当メソッドは未実装)。
Node.jsで実行
npx asinit .
で自動生成されたindex.js
はnode.jsモジュールとしてWebAssemblyを動かす方法を示してくれました。
$ node
> const wasm = require('./index.js');
undefined
> wasm
{ memory: Memory {}, table: Table {}, add: [Function: 0] }
> wasm.add(42, 24)
66
>
とても簡単です。
PS: 続編は近日公開予定です