cherry/wasmtest: a test program for wasmexport
Change-Id: I54700d85b52e7be12f337fc72b39a479605dc38f
Reviewed-on: https://go-review.googlesource.com/c/scratch/+/604235
TryBot-Bypass: Cherry Mui <cherryyz@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
diff --git a/cherry/wasmtest/go.mod b/cherry/wasmtest/go.mod
new file mode 100644
index 0000000..63eef9f
--- /dev/null
+++ b/cherry/wasmtest/go.mod
@@ -0,0 +1,5 @@
+module golang.org/x/scratch/cherry/wasmtest
+
+go 1.22
+
+require github.com/tetratelabs/wazero v1.7.3
diff --git a/cherry/wasmtest/go.sum b/cherry/wasmtest/go.sum
new file mode 100644
index 0000000..b9dbbc9
--- /dev/null
+++ b/cherry/wasmtest/go.sum
@@ -0,0 +1,2 @@
+github.com/tetratelabs/wazero v1.7.3 h1:PBH5KVahrt3S2AHgEjKu4u+LlDbbk+nsGE3KLucy6Rw=
+github.com/tetratelabs/wazero v1.7.3/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
diff --git a/cherry/wasmtest/testprog/x.go b/cherry/wasmtest/testprog/x.go
new file mode 100644
index 0000000..d4f2355
--- /dev/null
+++ b/cherry/wasmtest/testprog/x.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+ "runtime"
+ "runtime/debug"
+)
+
+func init() {
+ println("init function called")
+}
+
+var ch = make(chan float64)
+
+//go:wasmexport E
+func E(a int64, b int32, c float64, d float32) { // various types of args, no result
+ println("=== E ===")
+ // goroutine
+ go func() { ch <- float64(a) + float64(b) + c + float64(d) + 100 }()
+ debug.PrintStack() // traceback
+ grow([100]int{10}) // stack growth
+ runtime.GC() // GC
+ println("=== E end ===")
+}
+
+//go:wasmexport F
+func F() int64 { // no arg, has result
+ f := int64(<-ch * 100) // force a goroutine switch
+ println("F =", f)
+ return f
+}
+
+//go:wasmexport G
+func G(x int32) {
+ println("G", x)
+ if x%2 == 0 {
+ G(x - 1) // simple recursion within this module
+ } else {
+ J(x - 1) // mutual recursion between host and this module
+ }
+ println("G", x, "end")
+}
+
+//go:wasmimport test I
+func I() int64
+
+//go:wasmimport test J
+func J(int32)
+
+func main() {
+ println("hello")
+ println("main: I =", I())
+}
+
+func grow(x [100]int) {
+ if x[0] == 0 {
+ println("=== grow ===")
+ debug.PrintStack()
+ return
+ }
+ x[0]--
+ grow(x)
+}
diff --git a/cherry/wasmtest/w.go b/cherry/wasmtest/w.go
new file mode 100644
index 0000000..f2ad733
--- /dev/null
+++ b/cherry/wasmtest/w.go
@@ -0,0 +1,128 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// A program for testing wasmexport.
+// This is the driver/host program, which provides the imports
+// and calls the exports. testprog is the source of the Wasm
+// module, which can be compiled to either an executable or a
+// library.
+//
+// To build it as executable:
+// GOARCH=wasm GOOS=wasip1 go build -o /tmp/x.wasm ./testprog
+//
+// To build it as a library:
+// GOARCH=wasm GOOS=wasip1 go build -buildmode=c-shared -o /tmp/x.wasm ./testprog
+//
+// Then run the driver (which works for both modes):
+// go run w.go /tmp/x.wasm
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/tetratelabs/wazero"
+ "github.com/tetratelabs/wazero/api"
+ "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
+)
+
+// exported from wasm
+var E func(a int64, b int32, c float64, d float32)
+var F func() int64
+var G func(int32)
+
+func I() int64 {
+ println("I start")
+ E(20, 3, 0.4, 0.05)
+ r := F() * 2
+ G(4)
+ println("I end =", r)
+ return r
+}
+
+func J(x int32) {
+ println("J", x)
+ if x > 0 {
+ G(x)
+ }
+ println("J", x, "end")
+}
+
+func main() {
+ ctx := context.Background()
+
+ r := wazero.NewRuntime(ctx)
+ defer r.Close(ctx)
+
+ // provide import functions from host
+ _, err := r.NewHostModuleBuilder("test").
+ NewFunctionBuilder().WithFunc(I).Export("I").
+ NewFunctionBuilder().WithFunc(J).Export("J").
+ Instantiate(ctx)
+ if err != nil {
+ panic(err)
+ }
+
+ buf, err := os.ReadFile(os.Args[1])
+ if err != nil {
+ panic(err)
+ }
+
+ config := wazero.NewModuleConfig().
+ WithStdout(os.Stdout).WithStderr(os.Stderr).
+ WithStartFunctions() // don't call _start
+
+ wasi_snapshot_preview1.MustInstantiate(ctx, r)
+
+ m, err := r.InstantiateWithConfig(ctx, buf, config)
+ if err != nil {
+ panic(err)
+ }
+
+ // get export functions from the module
+ E = func(a int64, b int32, c float64, d float32) {
+ exp := m.ExportedFunction("E")
+ _, err := exp.Call(ctx, api.EncodeI64(a), api.EncodeI32(b), api.EncodeF64(c), api.EncodeF32(d))
+ if err != nil {
+ panic(err)
+ }
+ }
+ F = func() int64 {
+ exp := m.ExportedFunction("F")
+ r, err := exp.Call(ctx)
+ if err != nil {
+ panic(err)
+ }
+ rr := int64(r[0])
+ println("host: F =", rr)
+ return rr
+ }
+ G = func(x int32) {
+ exp := m.ExportedFunction("G")
+ _, err := exp.Call(ctx, api.EncodeI32(x))
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ entry := m.ExportedFunction("_start")
+ if entry != nil {
+ // Executable mode.
+ fmt.Println("Executable mode: start")
+ _, err := entry.Call(ctx)
+ fmt.Println(err)
+ return
+ }
+
+ // Library mode.
+ entry = m.ExportedFunction("_initialize")
+ fmt.Println("Library mode: initialize")
+ _, err = entry.Call(ctx)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println("\nLibrary mode: call export functions")
+ I()
+}