GoでWebAssemblyを作るときに必要になるsyscall/jsパッケージの型変換メモ。
使うのはGo1.12です。
Go1.11から破壊的な変更があるため注意。
wasm化の基本部分
まずベースとなるソース。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package main import( "fmt" "syscall/js" ) func test(this js.Value,vs []js.Value) interface{}{ fmt.Println(vs[0]) return "success" } func setFuncs(){ js.Global().Set("test", js.FuncOf(test)) } func main(){ setFuncs() done := make(chan struct{}, 0) <-done } |
wasmコンパイルは以下のように行います。
1 |
env GOOS=js GOARCH=wasm go build -o main.wasm main.go |
実行のHTMLは %GOROOT%/misc/wasm/wasm_exec.html
をコピーして書き換えます。
一部環境でOut of Memoryになる問題がありますが対策をこちらで書きました。
syscall/js型変換 : js => go
javascriptからgo-wasmの関数を呼び出すと引数がjs.Value
配列に格納されます。
reflectパッケージを使って型がどうなっているか見てみます。
またfmt.Println()
の内容はブラウザのコンソールに表示されます。
1 2 3 4 5 6 7 8 9 10 11 |
//GO func test(this js.Value,vs []js.Value) interface{}{ fmt.Println(vs[0]) //=> 15 fmt.Println(reflect.TypeOf(vs[0])) //=> js.Value fmt.Println(reflect.TypeOf(vs[0].String())) //=> string fmt.Println(reflect.TypeOf(vs[0].Int())) //=>panic: syscall/js: call of Value.Int on string return 1 } //JS(HTML) window.test("15") |
引数”15″を渡した場合の変換はString()
で行います。
ここで違う型(Int
)にしようとするとエラーで停止するので注意。
こんな感じでGOの関数を呼び出すことができます。
1 2 3 4 5 6 7 8 |
//GO func test(this js.Value,vs []js.Value) interface{}{ r := SomeFunction(vs[0].Bool(), vs[1].Float(), vs[2].Int(), vs[3].String()) return r } //JS(HTML) window.test(false, 34.22, 11, "15"); |
配列を渡すのは少し面倒です。
1 2 3 |
for i := 0; i < vs[0].Get("length").Int(); i++ { fmt.Println(vs[0].Index(i)) } |
マップ(オブジェクト)を渡すには多分JSの関数を呼ばないと無理そう。
js.Global().Get("Object").Call("keys",vs[0]).Call("forEach",js.FuncOf(...))
のような感じで試行錯誤してみましたが今のところ出来てないです。
syscall/js型変換 : go => js
こちらはgoの型のままreturn
するだけでいいです。
返値がinterface{}
なので何でも行けそうですが、javascriptの型に合わないものはエラーになります。
1 2 |
return new(bytes.Buffer) //=>panic: ValueOf: invalid value |
1234567891011 | Go | JavaScript || ---------------------- | ---------------------- || js.Value | [its value] || js.TypedArray | typed array || js.Func | function || nil | null || bool | boolean || integers and floats | number || string | string || []interface{} | new array || map[string]interface{} | new object |
引数でうまくいかなかったmapも簡単に返せます。
TypedArrayが返せるためバイナリデータを作って返すことも可能です。
返せる型は以下のものです。
1 2 3 4 5 6 7 8 9 10 |
var ( int8Array = Global().Get("Int8Array") int16Array = Global().Get("Int16Array") int32Array = Global().Get("Int32Array") uint8Array = Global().Get("Uint8Array") uint16Array = Global().Get("Uint16Array") uint32Array = Global().Get("Uint32Array") float32Array = Global().Get("Float32Array") float64Array = Global().Get("Float64Array") ) |
Uint8Arrayでバイト列が返せますね。
色々と試行錯誤でやっていますが、この内容は今後のアップデートで変更される可能性も高いのでコピペじゃなく理屈で覚えていきたいです。