Files
wazero/examples/allocation/rust/greet.go
Crypt Keeper 45ff2fe12f Propagates context to all api interface methods that aren't constant (#502)
This prepares for exposing operations like Memory.Grow while keeping the
ability to trace what did that, by adding a `context.Context` initial
parameter. This adds this to all API methods that mutate or return
mutated data.

Before, we made a change to trace functions and general lifecycle
commands, but we missed this part. Ex. We track functions, but can't
track what closed the module, changed memory or a mutable constant.
Changing to do this now is not only more consistent, but helps us
optimize at least the interpreter to help users identify otherwise
opaque code that can cause harm. This is critical before we add more
functions that can cause harm, such as Memory.Grow.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-04-25 08:13:18 +08:00

109 lines
3.3 KiB
Go

package main
import (
"context"
_ "embed"
"fmt"
"log"
"os"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
)
// greetWasm was compiled using `cargo build --release --target wasm32-unknown-unknown`
//go:embed testdata/greet.wasm
var greetWasm []byte
// main shows how to interact with a WebAssembly function that was compiled
// from Rust.
//
// 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()
// Instantiate a Go-defined module named "env" that exports a function to
// log to the console.
env, err := r.NewModuleBuilder("env").
ExportFunction("log", logString).
Instantiate(ctx)
if err != nil {
log.Fatal(err)
}
defer env.Close(ctx)
// 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.InstantiateModuleFromCode(ctx, greetWasm)
if err != nil {
log.Fatal(err)
}
defer mod.Close(ctx)
// Get references to WebAssembly functions we'll use in this example.
greet := mod.ExportedFunction("greet")
greeting := mod.ExportedFunction("greeting")
allocate := mod.ExportedFunction("allocate")
deallocate := mod.ExportedFunction("deallocate")
// 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 Rust'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 := allocate.Call(ctx, nameSize)
if err != nil {
log.Fatal(err)
}
namePtr := results[0]
// This pointer was allocated by Rust, but owned by Go, So, we have to
// deallocate it when finished
defer deallocate.Call(ctx, namePtr, nameSize)
// The pointer is a linear memory offset, which is where we write the name.
if !mod.Memory().Write(ctx, uint32(namePtr), []byte(name)) {
log.Fatalf("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.Fatal(err)
}
// Finally, we get the greeting message "greet" printed. This shows how to
// read-back something allocated by Rust.
ptrSize, err := greeting.Call(ctx, namePtr, nameSize)
if err != nil {
log.Fatal(err)
}
greetingPtr := uint32(ptrSize[0] >> 32)
greetingSize := uint32(ptrSize[0])
// This pointer was allocated by Rust, but owned by Go, So, we have to
// deallocate it when finished
defer deallocate.Call(ctx, uint64(greetingPtr), uint64(greetingSize))
// 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.Fatalf("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.Fatalf("Memory.Read(%d, %d) out of range", offset, byteCount)
}
fmt.Println(string(buf))
}