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() +}