Files
wazero/examples/allocation/tinygo/greet.go
Crypt Keeper 1561c4ca7b Adds MustInstantiate to host imports (#814)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-09-28 16:21:30 +08:00

112 lines
3.5 KiB
Go

package main
import (
"context"
_ "embed"
"fmt"
"log"
"os"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)
// greetWasm was compiled using `tinygo build -o greet.wasm -scheduler=none --no-debug -target=wasi greet.go`
//
//go:embed testdata/greet.wasm
var greetWasm []byte
// main shows how to interact with a WebAssembly function that was compiled
// from TinyGo.
//
// See README.md for a full description.
func main() {
// Choose the context to use for function calls.
ctx := context.Background()
// Create a new WebAssembly Runtime.
r := wazero.NewRuntime(ctx)
defer r.Close(ctx) // This closes everything this Runtime created.
// Instantiate a Go-defined module named "env" that exports a function to
// log to the console.
_, err := r.NewHostModuleBuilder("env").
ExportFunction("log", logString).
Instantiate(ctx, r)
if err != nil {
log.Panicln(err)
}
// Note: testdata/greet.go doesn't use WASI, but TinyGo needs it to
// implement functions such as panic.
wasi_snapshot_preview1.MustInstantiate(ctx, r)
// Instantiate a WebAssembly module that imports the "log" function defined
// in "env" and exports "memory" and functions we'll use in this example.
mod, err := r.InstantiateModuleFromBinary(ctx, greetWasm)
if err != nil {
log.Panicln(err)
}
// Get references to WebAssembly functions we'll use in this example.
greet := mod.ExportedFunction("greet")
greeting := mod.ExportedFunction("greeting")
// These are undocumented, but exported. See tinygo-org/tinygo#2788
malloc := mod.ExportedFunction("malloc")
free := mod.ExportedFunction("free")
// Let's use the argument to this main function in Wasm.
name := os.Args[1]
nameSize := uint64(len(name))
// Instead of an arbitrary memory offset, use TinyGo's allocator. Notice
// there is nothing string-specific in this allocation function. The same
// function could be used to pass binary serialized data to Wasm.
results, err := malloc.Call(ctx, nameSize)
if err != nil {
log.Panicln(err)
}
namePtr := results[0]
// This pointer is managed by TinyGo, but TinyGo is unaware of external usage.
// So, we have to free it when finished
defer free.Call(ctx, namePtr)
// The pointer is a linear memory offset, which is where we write the name.
if !mod.Memory().Write(ctx, uint32(namePtr), []byte(name)) {
log.Panicf("Memory.Write(%d, %d) out of range of memory size %d",
namePtr, nameSize, mod.Memory().Size(ctx))
}
// Now, we can call "greet", which reads the string we wrote to memory!
_, err = greet.Call(ctx, namePtr, nameSize)
if err != nil {
log.Panicln(err)
}
// Finally, we get the greeting message "greet" printed. This shows how to
// read-back something allocated by TinyGo.
ptrSize, err := greeting.Call(ctx, namePtr, nameSize)
if err != nil {
log.Panicln(err)
}
// Note: This pointer is still owned by TinyGo, so don't try to free it!
greetingPtr := uint32(ptrSize[0] >> 32)
greetingSize := uint32(ptrSize[0])
// The pointer is a linear memory offset, which is where we write the name.
if bytes, ok := mod.Memory().Read(ctx, greetingPtr, greetingSize); !ok {
log.Panicf("Memory.Read(%d, %d) out of range of memory size %d",
greetingPtr, greetingSize, mod.Memory().Size(ctx))
} else {
fmt.Println("go >>", string(bytes))
}
}
func logString(ctx context.Context, m api.Module, offset, byteCount uint32) {
buf, ok := m.Memory().Read(ctx, offset, byteCount)
if !ok {
log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount)
}
fmt.Println(string(buf))
}