112 lines
3.5 KiB
Go
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))
|
|
}
|