Files
wazero/examples/multiple-results/multiple-results.go
Crypt Keeper 92ba4929e5 Drops support for the WebAssembly text format (#614)
This drops the text format (%.wat) and renames
InstantiateModuleFromCode to InstantiateModuleFromBinary as it is no
longer ambiguous.

We decided to stop supporting the text format as it isn't typically used
in production, yet costs a lot of work to develop. Given the resources
available and the increased work added with WebAssembly 2.0 and soon
WASI 2, we can't afford to spend the time on it.

The old parser is used only internally and will eventually be moved to
its own repository named watzero, possibly towards archival.

See #59

Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-06-01 19:01:43 +08:00

152 lines
5.5 KiB
Go

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/main/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()
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)
}
// ... defined in Go.
host, err := resultOffsetHostFunctions(ctx, r)
if err != nil {
log.Panicln(err)
}
// wazero enables WebAssembly 1.0 by default. Opt-in to other features:
runtimeWithMultiValue := wazero.NewRuntimeWithConfig(
wazero.NewRuntimeConfig().WithFeatureMultiValue(true),
// ^^ Note: WebAssembly 2.0 (WithWasmCore2) includes "multi-value".
)
// 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.
hostWithMultiValue, err := multiValueHostFunctions(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, host, wasmWithMultiValue, hostWithMultiValue} {
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])
}
}
// resultOffsetHostFunctions defines host functions that illustrate multiple
// results using a technique compatible with any WebAssembly 1.0 runtime.
//
// To return a value in WASM written to a result parameter, you have to define
// memory and pass a location to write the result. At the end of your function,
// you load that location.
func resultOffsetHostFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
return r.NewModuleBuilder("result-offset/host").
// To use result parameters, we need scratch memory. Allocate the least
// possible: 1 page (64KB).
ExportMemoryWithMax("mem", 1, 1).
// get_age returns a result, while a second result is written to memory.
ExportFunction("get_age", func(ctx context.Context, m api.Module, resultOffsetAge uint32) (errno uint32) {
if m.Memory().WriteUint64Le(ctx, resultOffsetAge, 37) {
return 0
}
return 1 // overflow
}).
// Now, define a function that shows the Wasm mechanics returning
// something written to a result parameter. The caller provides a
// memory offset to the callee, so that it knows where to write the
// second result.
ExportFunction("call_get_age", func(ctx context.Context, m api.Module) (age uint64) {
resultOffsetAge := uint32(8) // arbitrary memory offset (in bytes)
_, _ = m.ExportedFunction("get_age").Call(ctx, uint64(resultOffsetAge))
age, _ = m.Memory().ReadUint64Le(ctx, resultOffsetAge)
return
}).Instantiate(ctx, r)
}
// 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.InstantiateModuleFromBinary(ctx, resultOffsetWasm)
}
// multiValueHostFunctions defines Wasm functions that illustrate multiple
// results using the "multiple-results" feature.
func multiValueHostFunctions(ctx context.Context, r wazero.Runtime) (api.Module, error) {
return r.NewModuleBuilder("multi-value/host").
// Define a function that returns two results
ExportFunction("get_age", func() (age uint64, errno uint32) {
age = 37
errno = 0
return
}).
// Now, define a function that returns only the first result.
ExportFunction("call_get_age", func(ctx context.Context, m api.Module) (age uint64) {
results, _ := m.ExportedFunction("get_age").Call(ctx)
return results[0]
}).Instantiate(ctx, r)
}
// 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.InstantiateModuleFromBinary(ctx, multiValueWasm)
}