wa-lang.org/wazero@v1.0.2/examples/allocation/tinygo/greet.go (about) 1 package main 2 3 import ( 4 "context" 5 _ "embed" 6 "fmt" 7 "log" 8 "os" 9 10 "wa-lang.org/wazero" 11 "wa-lang.org/wazero/api" 12 "wa-lang.org/wazero/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, r) 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.InstantiateModuleFromBinary(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(ctx, uint32(namePtr), []byte(name)) { 77 log.Panicf("Memory.Write(%d, %d) out of range of memory size %d", 78 namePtr, nameSize, mod.Memory().Size(ctx)) 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 // Note: This pointer is still owned by TinyGo, so don't try to free it! 94 greetingPtr := uint32(ptrSize[0] >> 32) 95 greetingSize := uint32(ptrSize[0]) 96 // The pointer is a linear memory offset, which is where we write the name. 97 if bytes, ok := mod.Memory().Read(ctx, greetingPtr, greetingSize); !ok { 98 log.Panicf("Memory.Read(%d, %d) out of range of memory size %d", 99 greetingPtr, greetingSize, mod.Memory().Size(ctx)) 100 } else { 101 fmt.Println("go >>", string(bytes)) 102 } 103 } 104 105 func logString(ctx context.Context, m api.Module, offset, byteCount uint32) { 106 buf, ok := m.Memory().Read(ctx, offset, byteCount) 107 if !ok { 108 log.Panicf("Memory.Read(%d, %d) out of range", offset, byteCount) 109 } 110 fmt.Println(string(buf)) 111 }