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