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  }