github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/examples/multiple-results/multiple-results.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	_ "embed"
     6  	"fmt"
     7  	"log"
     8  
     9  	wazero "github.com/wasilibs/wazerox"
    10  	"github.com/wasilibs/wazerox/api"
    11  )
    12  
    13  // main implements functions with multiple returns values, using both an
    14  // approach portable with any WebAssembly 1.0 runtime, as well one dependent
    15  // on the "multiple-results" feature.
    16  //
    17  // The portable approach uses parameters to return additional results. The
    18  // parameter value is a memory offset to write the next value. This is the same
    19  // approach used by WASI.
    20  //   - resultOffsetWasmFunctions
    21  //   - resultOffsetHostFunctions
    22  //
    23  // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md
    24  //
    25  // Another approach is to enable the "multiple-results" feature. While
    26  // "multiple-results" is not yet a W3C recommendation, most WebAssembly
    27  // runtimes support it by default, and it is include in the draft of 2.0.
    28  //   - multiValueWasmFunctions
    29  //   - multiValueHostFunctions
    30  //
    31  // See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md
    32  func main() {
    33  	// Choose the context to use for function calls.
    34  	ctx := context.Background()
    35  
    36  	// Create a new WebAssembly Runtime.
    37  	r := wazero.NewRuntime(ctx)
    38  	defer r.Close(ctx) // This closes everything this Runtime created.
    39  
    40  	// Add a module that uses offset parameters for multiple results defined in WebAssembly.
    41  	wasm, err := resultOffsetWasmFunctions(ctx, r)
    42  	if err != nil {
    43  		log.Panicln(err)
    44  	}
    45  
    46  	// wazero enables WebAssembly Core Specification 2.0 features by default.
    47  	runtimeWithMultiValue := wazero.NewRuntime(ctx)
    48  
    49  	// Add a module that uses multiple results values
    50  
    51  	// ... defined in WebAssembly.
    52  	wasmWithMultiValue, err := multiValueWasmFunctions(ctx, runtimeWithMultiValue)
    53  	if err != nil {
    54  		log.Panicln(err)
    55  	}
    56  
    57  	// ... defined in Go.
    58  	multiValueFromImportedHost, err := multiValueFromImportedHostWasmFunctions(ctx, runtimeWithMultiValue)
    59  	if err != nil {
    60  		log.Panicln(err)
    61  	}
    62  
    63  	// Call the function from each module and print the results to the console.
    64  	for _, mod := range []api.Module{wasm, wasmWithMultiValue, multiValueFromImportedHost} {
    65  		getAge := mod.ExportedFunction("call_get_age")
    66  		results, err := getAge.Call(ctx)
    67  		if err != nil {
    68  			log.Panicln(err)
    69  		}
    70  
    71  		fmt.Printf("%s: age=%d\n", mod.Name(), results[0])
    72  	}
    73  }
    74  
    75  // resultOffsetWasm was generated by the following:
    76  //
    77  //	cd testdata; wat2wasm --debug-names result_offset.wat
    78  //
    79  //go:embed testdata/result_offset.wasm
    80  var resultOffsetWasm []byte
    81  
    82  // resultOffsetWasmFunctions are the WebAssembly equivalent of the Go-defined
    83  // resultOffsetHostFunctions. The source is in testdata/result_offset.wat
    84  func resultOffsetWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
    85  	return r.Instantiate(ctx, resultOffsetWasm)
    86  }
    87  
    88  // multiValueWasm was generated by the following:
    89  //
    90  //	cd testdata; wat2wasm --debug-names multi_value.wat
    91  //
    92  //go:embed testdata/multi_value.wasm
    93  var multiValueWasm []byte
    94  
    95  // multiValueWasmFunctions are the WebAssembly equivalent of the Go-defined
    96  // multiValueHostFunctions. The source is in testdata/multi_value.wat
    97  func multiValueWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
    98  	return r.Instantiate(ctx, multiValueWasm)
    99  }
   100  
   101  // multiValueWasm was generated by the following:
   102  //
   103  //	cd testdata; wat2wasm --debug-names multi_value_imported.wat
   104  //
   105  //go:embed testdata/multi_value_imported.wasm
   106  var multiValueFromImportedHostWasm []byte
   107  
   108  // multiValueFromImportedHostWasmFunctions return the WebAssembly which imports the Go-defined "get_age" function.
   109  // The imported "get_age" function returns multiple results. The source is in testdata/multi_value_imported.wat
   110  func multiValueFromImportedHostWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
   111  	// Instantiate the host module with the exported `get_age` function which returns multiple results.
   112  	if _, err := r.NewHostModuleBuilder("multi-value/host").
   113  		// Define a function that returns two results
   114  		NewFunctionBuilder().
   115  		WithFunc(func(context.Context) (age uint64, errno uint32) {
   116  			age = 37
   117  			errno = 0
   118  			return
   119  		}).
   120  		Export("get_age").
   121  		Instantiate(ctx); err != nil {
   122  		return nil, err
   123  	}
   124  	// Then, creates the module which imports the `get_age` function from the `multi-value/host` module above.
   125  	return r.Instantiate(ctx, multiValueFromImportedHostWasm)
   126  }