github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/examples/allocation/tinygo/greet.go (about) 1 package main 2 3 import ( 4 "context" 5 _ "embed" 6 "fmt" 7 "log" 8 "os" 9 10 wazero "github.com/wasilibs/wazerox" 11 "github.com/wasilibs/wazerox/api" 12 "github.com/wasilibs/wazerox/imports/wasi_snapshot_preview1" 13 ) 14 15 // greetWasm was compiled using `tinygo build -o greet.wasm -scheduler=none --no-debug -target=wasi greet.go` 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 21 // from TinyGo. 22 // 23 // See README.md for a full description. 24 func main() { 25 // Choose the context to use for function calls. 26 ctx := context.Background() 27 28 // Create a new WebAssembly Runtime. 29 r := wazero.NewRuntime(ctx) 30 defer r.Close(ctx) // This closes everything this Runtime created. 31 32 // Instantiate a Go-defined module named "env" that exports a function to 33 // log to the console. 34 _, err := r.NewHostModuleBuilder("env"). 35 NewFunctionBuilder().WithFunc(logString).Export("log"). 36 Instantiate(ctx) 37 if err != nil { 38 log.Panicln(err) 39 } 40 41 // Note: testdata/greet.go doesn't use WASI, but TinyGo needs it to 42 // implement functions such as panic. 43 wasi_snapshot_preview1.MustInstantiate(ctx, r) 44 45 // Instantiate a WebAssembly module that imports the "log" function defined 46 // in "env" and exports "memory" and functions we'll use in this example. 47 mod, err := r.Instantiate(ctx, greetWasm) 48 if err != nil { 49 log.Panicln(err) 50 } 51 52 // Get references to WebAssembly functions we'll use in this example. 53 greet := mod.ExportedFunction("greet") 54 greeting := mod.ExportedFunction("greeting") 55 // These are undocumented, but exported. See tinygo-org/tinygo#2788 56 malloc := mod.ExportedFunction("malloc") 57 free := mod.ExportedFunction("free") 58 59 // Let's use the argument to this main function in Wasm. 60 name := os.Args[1] 61 nameSize := uint64(len(name)) 62 63 // Instead of an arbitrary memory offset, use TinyGo's allocator. Notice 64 // there is nothing string-specific in this allocation function. The same 65 // function could be used to pass binary serialized data to Wasm. 66 results, err := malloc.Call(ctx, nameSize) 67 if err != nil { 68 log.Panicln(err) 69 } 70 namePtr := results[0] 71 // This pointer is managed by TinyGo, but TinyGo is unaware of external usage. 72 // So, we have to free it when finished 73 defer free.Call(ctx, namePtr) 74 75 // The pointer is a linear memory offset, which is where we write the name. 76 if !mod.Memory().Write(uint32(namePtr), []byte(name)) { 77 log.Panicf("Memory.Write(%d, %d) out of range of memory size %d", 78 namePtr, nameSize, mod.Memory().Size()) 79 } 80 81 // Now, we can call "greet", which reads the string we wrote to memory! 82 _, err = greet.Call(ctx, namePtr, nameSize) 83 if err != nil { 84 log.Panicln(err) 85 } 86 87 // Finally, we get the greeting message "greet" printed. This shows how to 88 // read-back something allocated by TinyGo. 89 ptrSize, err := greeting.Call(ctx, namePtr, nameSize) 90 if err != nil { 91 log.Panicln(err) 92 } 93 94 greetingPtr := uint32(ptrSize[0] >> 32) 95 greetingSize := uint32(ptrSize[0]) 96 97 // This pointer is managed by TinyGo, but TinyGo is unaware of external usage. 98 // So, we have to free it when finished 99 if greetingPtr != 0 { 100 defer func() { 101 _, err := free.Call(ctx, uint64(greetingPtr)) 102 if err != nil { 103 log.Panicln(err) 104 } 105 }() 106 } 107 108 // The pointer is a linear memory offset, which is where we write the name. 109 if bytes, ok := mod.Memory().Read(greetingPtr, greetingSize); !ok { 110 log.Panicf("Memory.Read(%d, %d) out of range of memory size %d", 111 greetingPtr, greetingSize, mod.Memory().Size()) 112 } else { 113 fmt.Println("go >>", string(bytes)) 114 } 115 } 116 117 func logString(_ context.Context, m api.Module, offset, byteCount uint32) { 118 buf, ok := m.Memory().Read(offset, byteCount) 119 if !ok { 120 log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount) 121 } 122 fmt.Println(string(buf)) 123 }