TypeScriptでWebAssemblyを開発できるAssemblyScriptを試してみた


TypeScriptでWebAssemblyを開発できるAssemblyScriptというのを見つけたので試してみる。

AssemblyScriptとは?

  • AssemblyScriptとは、TypeScriptからWebAssemblyにコンパイルする言語とそのコンパイラのこと。
  • Microsoftによって開発されている。
  • 厳密にはTypeScript風な言語であって、TypeScriptではない。
  • まだまだ開発途上(2019-11-21現在)

AssemblyScriptを試してみよう

必要な環境

Node.jsの9が必要なので、nodenvでその環境を用意しておく。

AssemblyScriptのインストール

まず、AssemblyScriptを試すプロジェクトを作る:

mkdir assemblyscript-playground
cd assemblyscript-playground
npm init

AssemblyScriptはnpmパッケージとして公開されていないので、GitHubから直接インストールする:

npm install --save-dev github:AssemblyScript/assemblyscript#v0.8.0

プロジェクトの雛形の生成

[asinit]を使って、WebAssemblyのコンパイルに必要なファイル群と設定を生成する:

npx asinit .

これを実行すると、package.jsonに次のようなNPMスクリプトが追加される:

package.json
{
  "scripts": {
    "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug",
    "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize",
    "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized"
  }
}

トップレベルに生成されたindex.jsは下記のようになっている:

index.js
const fs = require("fs");
const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm"));
const imports = {
  env: {
    abort(_msg, _file, line, column) {
       console.error("abort called at index.ts:" + line + ":" + column);
    }
  }
};
Object.defineProperty(module, "exports", {
  get: () => new WebAssembly.Instance(compiled, imports).exports
});

これは、普通のJavaScriptモジュールと同様に、WebAssemblyモジュールをrequireで読み込めるようにする仕掛けだ。便利。

assemblyディレクトリには、AssemblyScriptのソースコードが配置される。コード生成時には、次のような足し算をするサンプルが生成されている:

assembly/index.ts
// The entry file of your WebAssembly module.

export function add(a: i32, b: i32): i32 {
  return a + b;
}

ここで気になるのが、i32という型だ。普段TypeScriptを書いているなら、number型を使うところだが、AssemblyScriptではWebAssemblyの型を用いるようである。ちなみに、number型も使えるが、AssemblyScriptではf64のエイリアスになる。

ビルドしてみよう

プロジェクトの雛形を生成しただけでも、WebAssemblyをビルドしてみることができるので、下記のコマンドを実行してビルドしてみよう。

npm run asbuild

ビルドが成功すると、buildディレクトリに下記のファイルが生成される:

optimized.wasm
optimized.wasm.map
optimized.wat
untouched.wasm
untouched.wasm.map
untouched.wat

.wasmはバイナリで、.wsm.mapはそのソースマップ。

.watは人が読むためのテキスト表現になる。バイナリの読み下し文のようなもの。.watをじっくり読めば、どういうふうな命令になっているかを理解することができるが、今は読んで理解する必要はない。

WebAssemblyを動かしてみる

ビルドしたWebAssemblyをNode.jsで実行してみよう。

$ node
> const add = require('./index').add 
undefined
> add(1, 2)
3

所感

安定性はまだまだ

Nightlyをインストールしたらビルドできなかったりと、まだまだ開発途上感は否めない。今後に期待。

TSerにとっては学習しやすいツールになるかもしれない

WebAssemblyを使うシナリオのひとつに「JavaScriptのチューニングではもう限界なので、WebAssemblyで部分的にチューニングしよう」ということが想定される。WebAssemblyの開発言語というとRustをよく耳にするが、今までJavaScript/TypeScriptで作ってきた現場からすると、新しい言語の学習に対する負担は小さくないだろう。TS風の文法で書けるAssemblyScriptが実用的になってくれば、そうした学習コストを捻出しにくい場面でもWebAssemblyに挑戦しやくなり、いいかもしれない。