8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

WebAssemblyから、Goのメソッドを呼び出す

Posted at

試したこと

WebAssemblyで、Goで宣言した構造体のメソッドを呼び出します。環境は、go1.12.7 + Chromeで確認しています。

ちなみに、syscall/jsを使っているので、GOOS=js GOARCH=wasm 以外で使うことはたぶん難しいです。

構造体の定義

確認用に、Numというメンバを持ったTestという構造体を使います。メソッドは、Testを出力するPrint()と、Numの値を2倍にするTwice()Test.Numに値を足すAdd()、値を取得するGetNum()を用意します。


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
}

コマンドラインで実行してTest構造体を使ってみます。


package main

import (
	"fmt"
)

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
}

func main() {
	var test = &Test{
		Num: 1,
	}
	test.Print() // result should be &{1}
	test.Twice() 
	test.Print() // result should be &{2}
	test.Add(1)  
	test.Print() // result should be &{3}
	fmt.Println(test.GetNum()) // result should be 3
}

実行結果は↓のようになります。


&{1}
&{2}
&{3}
3

WebAssembly用にコードを修正

コードの全体です。


package main

import (
	"fmt"
	"syscall/js"
)

type Test struct {
	Num int
}

func (t *Test) Print(this js.Value, args []js.Value) interface{} {
	fmt.Println(t)
	return nil
}

func (t *Test) Twice(this js.Value, args []js.Value) interface{} {
	t.Num *= 2
	return nil
}

func registerCallbacks() {
	var test = &Test{
		Num: 1,
	}
	js.Global().Set("test", js.ValueOf(
		map[string]interface{}{
			"Print": js.FuncOf(test.Print),
			"Twice": js.FuncOf(test.Twice),
		},
	))
}

func main() {
	c := make(chan struct{}, 0)
	registerCallbacks()
	<-c
}

コードの解説

WebAssemblyから呼び出せるように関数の定義を変更する

WebAssemblyから呼び出すときの作法に従い、引数をjs.Valueargs[]js.Value、 返り値をinterface{}にします。


func (t *Test) Print(this js.Value, args []js.Value) interface{} {
	fmt.Println(t)
	return nil
}

引数を参照する

引数の値を直接Go側で使うことはできないので、args[0].Int()を呼び出して整数型に変換します。


func (t *Test) Add(this js.Value, args []js.Value) interface{} {
	t.Num += args[0].Int()
	return nil
}

返り値を戻す

返り値はそのまま返せないので、js.ValueOf()で返します。


func (t *Test) GetNum(this js.Value, args []js.Value) interface{} {
	return js.ValueOf(t.Num)
}

構造体のメソッドのjavascriptへの登録

js.Global()を呼び出して、javascriptの空間にtest.Printtest.Twicetest.Addtest.GetNumをそれぞれ登録します。

func registerCallbacks() {
	var test = &Test{
		Num: 1,
	}
	js.Global().Set("test", js.ValueOf(
		map[string]interface{}{
			"print":  js.FuncOf(test.Print),
			"twice":  js.FuncOf(test.Twice),
			"add":    js.FuncOf(test.Add),
			"getNum": js.FuncOf(test.GetNum),
		},
	))
}

関数の登録

関数の登録を呼び出します。main関数が終了しないように、チャネルを使って処理をブロックします。


func main() {
	c := make(chan struct{}, 0)
	registerCallbacks()
	<-c
}

テストする

以下の、htmlファイルでテストをします。


<!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())
		}
	</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>
</body>
</html>

WebAssemblyの実行に使う wasm_exec.js (go1.12.7用)は、公式から落とせます。

また、Goのファイルは、GOOS=js GOARCH=wasm go build -o test.wasm でビルドしておきます。

上記のhtmlファイル、wasm_exec.js, test.wasm をサーバ上に置いてアクセスすると、以下のようにボタンが表示されます。html, wasm_exec.js, test.wasm

キャプチャ.PNG

コマンドラインで確認したときと同様に、printtwiceprintaddprintget test.Num を押すと、ブラウザのコンソールに以下のように表示されました。

&{1}
&{2}
&{3}
3

構造体は直接渡せない?

構造体を直接登録するのは、試してみましたができないようでした。panic: ValueOf: invalid valueが出ます。js.ValueOf()の実装を見ると、任意の構造体は扱えない感じになっていました。

func registerCallbacks() {
	var test = &Test{
		Num: 1,
	}
	js.Global().Set("test", js.ValueOf(test))
}

ソース

githubに置いておきました。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?