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  }