この記事は フラー株式会社 Advent Calendar 2024 の16日目の記事です。
はじめに
Go 1.24 Release Notes を眺めていたらWebAssembly に関する機能追加・変更がありました。リリース時期は2025年2月とまだ先であり、リリースに含まれるか不確定ですが、興味があったので調べることにしました。
以下がアップデート内容です。この内、go:wasmexportディレクティブのサポートとgo:wasmimportの関数の型に使える型が増えた件を細かく見ていきます。
筆者は Goのコードから wasm をビルドした経験がありますが、手を動かした確認をとっていないため、不正確な点があるかもしれません。あらかじめご了承ください。
WebAssembly¶
The go:wasmexport directive is added for Go programs to export functions to the WebAssembly host.
On WebAssembly System Interface Preview 1 (GOOS=wasip1, GOARCH=wasm),
Go 1.24 supports building a Go program as a reactor/library,
by specifying the -buildmode=c-shared build flag.
More types are now permitted as argument or result types for go:wasmimport functions.
Specifically, bool, string, uintptr, and pointers to certain types are allowed (see the proposal for detail), along with 32-bit and 64-bit integer and float types, and unsafe.Pointer, which are already allowed.
These types are also permitted as argument or result types for go:wasmexport functions.
The support files for WebAssembly have been moved to lib/wasm from misc/wasm.
go:wasmexport ディレクティブの追加
Issue: cmd/compile: add go:wasmexport directive
ユーザーの要望により go:wasmexport コンパイラディレクティブが追加されます。goのソースコードに以下のコメントを追加することでブラウザなどから呼び出すことができます。ちなみに、関数のみでメソッドは対応していません。
//go:wasmexport add
func add(x, y int) int {
return x + y
}
Rustではwasm-bindgenで既に実現された機能のようですが、Goではgoroutineの扱いやGCなどの様々な懸念点があり、issue が長い間塩漬けになっていました。
より詳細にみていきます。wasmで生成される初期化関数には _start, _initializeの二つがあります。これらの違いは、_start はランタイムを初期化し、mainを呼び出す一方、_initializeはランタイムを初期化し、mainを呼び出しません。_startをエクスポートするモードがコマンドモード、_initializeをエクスポートするモードをリアクター(ライブラリ)モードと言います。
_start,_initializeの詳細はCurrent Unstable API にあります。WASI Preview 1(GOOS=wasip1, GOARCH=wasm)では、ビルドフラグに-buildmode=c-shared
をつけることでリアクターモードにすることができ、これが、リリースノートにあったアップデートの2行目の内容になります。
以上が go:wasmexportの説明ですが、その他にも面白い議論がされていました。
例えば、エクスポートされた関数は新しいgoroutine上で実行されます。そのgoroutineでgoroutineが実行され、ブロックされた場合はどうするかを考えたり、エクスポートされた関数からJSの関数をインポートしてその中でさらにエクスポートした関数を呼ぶ場合はどうするかを考えていたりしていました。気になる方は issue を参照ください。
ちなみに tinygoではgo:wasmexportは一足先に導入されています。
go:wasmimport の関数に使える型が増えた件
Issue: cmd/compile: relax wasm/wasm32 function import signature type constraints
背景を正しく理解できていないのですが、正確には使える型の制限が緩和されたそうです。Go1.21 で go:wasmimportが WASI Preview 1 のサポートされました。その際、ホストとクライアント間、アーキテクチャ(32bit, 64bit)が違う場合、型変換が高度で高価であったため、使用できる型を制限していました。具体的には以下の型です。
Go value | Wasm value |
---|---|
int32, uint32 | i32 |
int64, uint64 | i64 |
float32 | f32 |
float64 | f64 |
unsafe.Pointer | i32 |
それが、wasm32の登場により、アーキテクチャ間のミスマッチを解消できるようになり、型の制限を緩和できるようになったようです。
具体的な型は膨大で複雑なため、詳細はissueを参照ください。個人的には文字列をwasmからJSに渡すのが簡単になったのが嬉しいです。ちなみに go:wasmexportで使える型は緩和前のgo:wasmimport で使える型と同じです。制限が緩和されるアップデートを待ちましょう...
おわりに
以前 Goで WebAssembly を触ったことがあるんですが、アップデートあったので内容を読んでみました。やっぱり issue を読むの楽しいです。歴史を学ぶことができるし、明解な文章は読んでて気持ちがいいです。次は17日目でまた私になります。タイトル未定です。