package main import ( "context" _ "embed" "fmt" "log" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" ) // main implements functions with multiple returns values, using both an // approach portable with any WebAssembly 1.0 runtime, as well one dependent // on the "multiple-results" feature. // // The portable approach uses parameters to return additional results. The // parameter value is a memory offset to write the next value. This is the same // approach used by WASI. // - resultOffsetWasmFunctions // - resultOffsetHostFunctions // // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md // // Another approach is to enable the "multiple-results" feature. While // "multiple-results" is not yet a W3C recommendation, most WebAssembly // runtimes support it by default, and it is include in the draft of 2.0. // - multiValueWasmFunctions // - multiValueHostFunctions // // See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md func main() { // Choose the context to use for function calls. ctx := context.Background() // Create a new WebAssembly Runtime. r := wazero.NewRuntime(ctx) defer r.Close(ctx) // This closes everything this Runtime created. // Add a module that uses offset parameters for multiple results defined in WebAssembly. wasm, err := resultOffsetWasmFunctions(ctx, r) if err != nil { log.Panicln(err) } // wazero enables WebAssembly Core Specification 2.0 features by default. runtimeWithMultiValue := wazero.NewRuntime(ctx) // Add a module that uses multiple results values // ... defined in WebAssembly. wasmWithMultiValue, err := multiValueWasmFunctions(ctx, runtimeWithMultiValue) if err != nil { log.Panicln(err) } // ... defined in Go. multiValueFromImportedHost, err := multiValueFromImportedHostWasmFunctions(ctx, runtimeWithMultiValue) if err != nil { log.Panicln(err) } // Call the function from each module and print the results to the console. for _, mod := range []api.Module{wasm, wasmWithMultiValue, multiValueFromImportedHost} { getAge := mod.ExportedFunction("call_get_age") results, err := getAge.Call(ctx) if err != nil { log.Panicln(err) } fmt.Printf("%s: age=%d\n", mod.Name(), results[0]) } } // resultOffsetWasm was generated by the following: // // cd testdata; wat2wasm --debug-names result_offset.wat // //go:embed testdata/result_offset.wasm var resultOffsetWasm []byte // resultOffsetWasmFunctions are the WebAssembly equivalent of the Go-defined // resultOffsetHostFunctions. The source is in testdata/result_offset.wat func resultOffsetWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) { return r.Instantiate(ctx, resultOffsetWasm) } // multiValueWasm was generated by the following: // // cd testdata; wat2wasm --debug-names multi_value.wat // //go:embed testdata/multi_value.wasm var multiValueWasm []byte // multiValueWasmFunctions are the WebAssembly equivalent of the Go-defined // multiValueHostFunctions. The source is in testdata/multi_value.wat func multiValueWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) { return r.Instantiate(ctx, multiValueWasm) } // multiValueWasm was generated by the following: // // cd testdata; wat2wasm --debug-names multi_value_imported.wat // //go:embed testdata/multi_value_imported.wasm var multiValueFromImportedHostWasm []byte // multiValueFromImportedHostWasmFunctions return the WebAssembly which imports the Go-defined "get_age" function. // The imported "get_age" function returns multiple results. The source is in testdata/multi_value_imported.wat func multiValueFromImportedHostWasmFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) { // Instantiate the host module with the exported `get_age` function which returns multiple results. if _, err := r.NewHostModuleBuilder("multi-value/host"). // Define a function that returns two results NewFunctionBuilder(). WithFunc(func(context.Context) (age uint64, errno uint32) { age = 37 errno = 0 return }). Export("get_age"). Instantiate(ctx); err != nil { return nil, err } // Then, creates the module which imports the `get_age` function from the `multi-value/host` module above. return r.Instantiate(ctx, multiValueFromImportedHostWasm) }