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

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

## AssemblyScriptとは？

* AssemblyScriptとは、TypeScriptからWebAssemblyにコンパイルする言語とそのコンパイラのこと。
* Microsoftによって開発されている。
* 厳密にはTypeScript風な言語であって、TypeScriptではない。
    * 様々な制約と仕様上の違いがある。
    * any型やunion型が無かったり、`Array<number>#sort`が`Array<string>#sort`として実行されたり、細かい違いがある。
    * [AssemblyScript is *not* a subset of TypeScript - DEV Community 👩‍💻👨‍💻](https://dev.to/jtenner/assemblyscript-is-not-a-subset-of-typescript-4ka3)
* まだまだ開発途上(2019-11-21現在)
    * クロージャーがまだ無かったり、例外のthrowがプログラムの停止だったりする。
    * [Limitations - The AssemblyScript Book](https://docs.assemblyscript.org/basics/limitations)

## 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の型](https://docs.assemblyscript.org/basics/types)を用いるようである。ちなみに、`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で実行してみよう。

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

## 所感

### 安定性はまだまだ

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

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

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