https://qiita.com/neko-suki/items/7fb822b9adfa6f1c12eb の続きです。
今回は、以下の2点を扱います。
- Goで定義した構造体の定義は変更せずに、WebAssemblyからGoで定義した構造体のメソッドを呼ぶ
- 構造体のメソッドの引数に、同じ構造体の別のインスタンスを渡す
両者とももっと賢く書く方法がある気がしますが、自分で調べた範囲では見つけられませんでした。
Goで定義した構造体の定義は変更せずに、WebAssemblyからGoで定義した構造体のメソッドを呼ぶ
特にいいやり方が思いつかなかったので、ラッパーを書きました。
構造体の定義
構造体の定義は元のままです。これをWebAssemblyから使えるようにします。元の構造体事態は何らかの事情で変更したくない場合を想定しています。
type Test struct {
Num int
}
func (t *Test) Print() {
fmt.Println(t)
}
func (t *Test) Twice() {
t.Num *= 2
}
func (t *Test) Add(val int) {
t.Num += val
}
func (t *Test) GetNum() int {
return t.Num
}
WebAssembly用の実装
WebAssembly側から呼び出せるようにラッパー関数をそれぞれ書きました。
package main
import (
"syscall/js"
)
func (t *Test) PrintWrapper(this js.Value, args []js.Value) interface{} {
t.Print()
return nil
}
func (t *Test) TwiceWrapper(this js.Value, args []js.Value) interface{} {
t.Twice()
return nil
}
func (t *Test) AddWrapper(this js.Value, args []js.Value) interface{} {
t.Add(args[0].Int())
return nil
}
func (t *Test) GetNumWrapper(this js.Value, args []js.Value) interface{} {
return js.ValueOf(t.GetNum())
}
func registerCallbacks() {
var test = &Test{
Num: 1,
}
js.Global().Set("test", js.ValueOf(
map[string]interface{}{
"print": js.FuncOf(test.PrintWrapper),
"twice": js.FuncOf(test.TwiceWrapper),
"add": js.FuncOf(test.AddWrapper),
"getNum": js.FuncOf(test.GetNumWrapper),
},
))
}
func main() {
c := make(chan struct{}, 0)
registerCallbacks()
<-c
}
構造体のメソッドの引数に、同じ構造体の別のインスタンスを渡す
例えば、以下のように同じ構造体の別のインスタンスを受け取って内部で利用する場合を考えます。
func (t *Test) AddAnotherTest(t2 *Test) {
t.Num += t2.Num
}
WebAssemblyの実装
まずは、関数登録部分です。構造体自信のポインタを取得するための関数 _ptr
を追加しています。これによって、登録した構造体のインスタンスがjavascript側から引数として渡されたときにポインタを取得できるようになります。_ptr
に直接ポインタの値を代入しないのはjavascript側で値を改変されないようにするためです。(プライベートメンバとして登録できれば必要ないはずですが、今回はそこまで調べていないです。)
func registerCallbacks() {
var test = &Test{
Num: 1,
}
js.Global().Set("test", js.ValueOf(
map[string]interface{}{
"_ptr": js.FuncOf(test.getPtr),
"print": js.FuncOf(test.PrintWrapper),
"twice": js.FuncOf(test.TwiceWrapper),
"add": js.FuncOf(test.AddWrapper),
"getNum": js.FuncOf(test.GetNumWrapper),
"addAnotherTest": js.FuncOf(test.AddAnotherTestWrapper),
},
))
var test2 = &Test{
Num: 2,
}
js.Global().Set("test2", js.ValueOf(
map[string]interface{}{
"_ptr": js.FuncOf(test2.getPtr),
"print": js.FuncOf(test2.PrintWrapper),
"twice": js.FuncOf(test2.TwiceWrapper),
"add": js.FuncOf(test2.AddWrapper),
"getNum": js.FuncOf(test2.GetNumWrapper),
"addAnotherTest": js.FuncOf(test2.AddAnotherTestWrapper),
},
),
)
}
次にポインタ取得用関数の実装です。unsafe.Pointer
をjs.ValueOf()
でjs.Value
型に変換して、javascript側に渡します。
func (t *Test) getPtr(this js.Value, args []js.Value) interface{} {
return js.ValueOf(unsafe.Pointer(t))
}
最後に、構造体を受け取って処理する部分です。javascript側からtest.AddAnotherTest(test2)
という形で呼ばれる想定です。
まず、args[0].Get('_ptr')
で test2._ptr
関数を呼び出しして、test2
のポインタを取得します。ただしこの段階ではjs.Value
型なので変換が必要です。unsafe.Pointer
はfloat64で実装されているようなので、.Float()
を呼び出します。その結果をunsafe.Pointer
型に変換して、最後にTest
のポインタに変換します。
func (t *Test) AddAnotherTestWrapper(this js.Value, args []js.Value) interface{} {
ptrJSValue := args[0].Call("_ptr")
t2 := (*Test)(unsafe.Pointer(ptrJSValue.Float()))
t.AddAnotherTest(t2)
return nil
}
テスト用のhtml
テスト用に使うhtmlです。addAnotherTest()
というのが追加されています。例えば、twice
, addAnotherTest
, print
という順番でボタンを押すと、consoleに&{4}
が表示されます。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Go wasm</title>
</head>
<body>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("test.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
<script>
function getNum(){
console.log(test.getNum())
}
function addAnotherTest(){
console.log(test2)
test.AddAnotherTest(test2)
}
</script>
<button onClick="test.print();" id="runButton" >print</button><br>
<button onClick="test.twice()" id="runButton">twice</button><br>
<button onClick="test.add(1)" id="runButton">add</button><br>
<button onClick="getNum()" id="runButton">get test.Num</button><br>
<button onClick="addAnotherTest()" id="runButton"> addAnotherTest</button><br>
</body>
</html>
ソース
下に置いてあります。
https://github.com/neko-suki/wasm_struct