github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/examples/allocation/rust/greet.go (about) 1 package main 2 3 import ( 4 "context" 5 _ "embed" 6 "fmt" 7 "log" 8 "os" 9 10 "github.com/tetratelabs/wazero" 11 "github.com/tetratelabs/wazero/api" 12 ) 13 14 // greetWasm was compiled using `cargo build --release --target wasm32-unknown-unknown` 15 // 16 //go:embed testdata/greet.wasm 17 var greetWasm []byte 18 19 // main shows how to interact with a WebAssembly function that was compiled 20 // from Rust. 21 // 22 // See README.md for a full description. 23 func main() { 24 // Choose the context to use for function calls. 25 ctx := context.Background() 26 27 // Create a new WebAssembly Runtime. 28 r := wazero.NewRuntime(ctx) 29 defer r.Close(ctx) // This closes everything this Runtime created. 30 31 // Instantiate a Go-defined module named "env" that exports a function to 32 // log to the console. 33 _, err := r.NewHostModuleBuilder("env"). 34 NewFunctionBuilder().WithFunc(logString).Export("log"). 35 Instantiate(ctx) 36 if err != nil { 37 log.Panicln(err) 38 } 39 40 // Instantiate a WebAssembly module that imports the "log" function defined 41 // in "env" and exports "memory" and functions we'll use in this example. 42 mod, err := r.Instantiate(ctx, greetWasm) 43 if err != nil { 44 log.Panicln(err) 45 } 46 47 // Get references to WebAssembly functions we'll use in this example. 48 greet := mod.ExportedFunction("greet") 49 greeting := mod.ExportedFunction("greeting") 50 allocate := mod.ExportedFunction("allocate") 51 deallocate := mod.ExportedFunction("deallocate") 52 53 // Let's use the argument to this main function in Wasm. 54 name := os.Args[1] 55 nameSize := uint64(len(name)) 56 57 // Instead of an arbitrary memory offset, use Rust's allocator. Notice 58 // there is nothing string-specific in this allocation function. The same 59 // function could be used to pass binary serialized data to Wasm. 60 results, err := allocate.Call(ctx, nameSize) 61 if err != nil { 62 log.Panicln(err) 63 } 64 namePtr := results[0] 65 // This pointer was allocated by Rust, but owned by Go, So, we have to 66 // deallocate it when finished 67 defer deallocate.Call(ctx, namePtr, nameSize) 68 69 // The pointer is a linear memory offset, which is where we write the name. 70 if !mod.Memory().Write(uint32(namePtr), []byte(name)) { 71 log.Panicf("Memory.Write(%d, %d) out of range of memory size %d", 72 namePtr, nameSize, mod.Memory().Size()) 73 } 74 75 // Now, we can call "greet", which reads the string we wrote to memory! 76 _, err = greet.Call(ctx, namePtr, nameSize) 77 if err != nil { 78 log.Panicln(err) 79 } 80 81 // Finally, we get the greeting message "greet" printed. This shows how to 82 // read-back something allocated by Rust. 83 ptrSize, err := greeting.Call(ctx, namePtr, nameSize) 84 if err != nil { 85 log.Panicln(err) 86 } 87 greetingPtr := uint32(ptrSize[0] >> 32) 88 greetingSize := uint32(ptrSize[0]) 89 // This pointer was allocated by Rust, but owned by Go, So, we have to 90 // deallocate it when finished 91 defer deallocate.Call(ctx, uint64(greetingPtr), uint64(greetingSize)) 92 93 // The pointer is a linear memory offset, which is where we write the name. 94 if bytes, ok := mod.Memory().Read(greetingPtr, greetingSize); !ok { 95 log.Panicf("Memory.Read(%d, %d) out of range of memory size %d", 96 greetingPtr, greetingSize, mod.Memory().Size()) 97 } else { 98 fmt.Println("go >>", string(bytes)) 99 } 100 } 101 102 func logString(ctx context.Context, m api.Module, offset, byteCount uint32) { 103 buf, ok := m.Memory().Read(offset, byteCount) 104 if !ok { 105 log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount) 106 } 107 fmt.Println(string(buf)) 108 }