wazero

package module
v1.0.0-rc.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 1, 2023 License: Apache-2.0 Imports: 25 Imported by: 321

README

wazero: the zero dependency WebAssembly runtime for Go developers

WebAssembly Core Specification Test Go Reference License

WebAssembly is a way to safely run code compiled in other languages. Runtimes execute WebAssembly Modules (Wasm), which are most often binaries with a .wasm extension.

wazero is a WebAssembly Core Specification 1.0 and 2.0 compliant runtime written in Go. It has zero dependencies, and doesn't rely on CGO. This means you can run applications in other languages and still keep cross compilation.

Import wazero and extend your Go application with code written in any language!

Example

The best way to learn wazero is by trying one of our examples. The most basic example extends a Go application with an addition function defined in WebAssembly.

Runtime

There are two runtime configurations supported in wazero: Compiler is default:

By default, ex wazero.NewRuntime(ctx), the Compiler is used if supported. You can also force the interpreter like so:

r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
Interpreter

Interpreter is a naive interpreter-based implementation of Wasm virtual machine. Its implementation doesn't have any platform (GOARCH, GOOS) specific code, therefore interpreter can be used for any compilation target available for Go (such as riscv64).

Compiler

Compiler compiles WebAssembly modules into machine code ahead of time (AOT), during Runtime.CompileModule. This means your WebAssembly functions execute natively at runtime. Compiler is faster than Interpreter, often by order of magnitude (10x) or more. This is done without host-specific dependencies.

If interested, check out the RATIONALE.md and help us optimize further!

Conformance

Both runtimes pass WebAssembly Core 1.0 and 2.0 specification tests on supported platforms:

Runtime Usage amd64 arm64 others
Interpreter wazero.NewRuntimeConfigInterpreter()
Compiler wazero.NewRuntimeConfigCompiler()

Support Policy

The below support policy focuses on compatability concerns of those embedding wazero into their Go applications.

wazero

wazero is an early project, so APIs are subject to change until version 1.0. To use wazero meanwhile, you need to use the latest pre-release like this:

go get github.com/tetratelabs/wazero@latest

wazero will tag a new pre-release at least once a month until 1.0. 1.0 is scheduled for March 2023 and will require minimally Go 1.18. Except experimental packages, wazero will not break API on subsequent minor versions.

Meanwhile, please practice the current APIs to ensure they work for you, and give us a star if you are enjoying it so far!

Go

wazero has no dependencies except Go, so the only source of conflict in your project's use of wazero is the Go version.

wazero follows the same version policy as Go's Release Policy: two versions. wazero will ensure these versions work and bugs are valid if there's an issue with a current Go version.

Additionally, wazero intentionally delays usage of language or standard library features one additional version. For example, when Go 1.29 is released, wazero can use language features or standard libraries added in 1.27. This is a convenience for embedders who have a slower version policy than Go. However, only supported Go versions may be used to raise support issues.

Platform

wazero has two runtime modes: Interpreter and Compiler. The only supported operating systems are ones we test, but that doesn't necessarily mean other operating system versions won't work.

We currently test Linux (Ubuntu and scratch), MacOS and Windows as packaged by GitHub Actions, as well compilation of 32-bit Linux and 64-bit FreeBSD.

  • Interpreter
    • Linux is tested on amd64 (native) as well arm64 and riscv64 via emulation.
    • MacOS and Windows are only tested on amd64.
  • Compiler
    • Linux is tested on amd64 (native) as well arm64 via emulation.
    • MacOS and Windows are only tested on amd64.

wazero has no dependencies and doesn't require CGO. This means it can also be embedded in an application that doesn't use an operating system. This is a main differentiator between wazero and alternatives.

We verify zero dependencies by running tests in Docker's scratch image. This approach ensures compatibility with any parent image.


wazero is a registered trademark of Tetrate.io, Inc. in the United States and/or other countries

Documentation

Overview

Example

This is an example of how to extend a Go application with an addition function defined in WebAssembly.

Since addWasm was compiled with TinyGo's `wasi` target, we need to configure WASI host imports.

A complete project that does the same as this is available here: https://github.com/tetratelabs/wazero/tree/main/examples/basic

package main

import (
	"context"
	_ "embed"
	"fmt"
	"log"

	"github.com/tetratelabs/wazero"
	"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)

// addWasm was generated by the following:
//
//	cd examples/basic/testdata; tinygo build -o add.wasm -target=wasi add.go
//
//go:embed examples/basic/testdata/add.wasm
var addWasm []byte

// This is an example of how to extend a Go application with an addition
// function defined in WebAssembly.
//
// Since addWasm was compiled with TinyGo's `wasi` target, we need to configure
// WASI host imports.
//
// A complete project that does the same as this is available here:
// https://github.com/tetratelabs/wazero/tree/main/examples/basic
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.

	// Instantiate WASI, which implements host functions needed for TinyGo to
	// implement `panic`.
	wasi_snapshot_preview1.MustInstantiate(ctx, r)

	// Instantiate the guest Wasm into the same runtime. It exports the `add`
	// function, implemented in WebAssembly.
	mod, err := r.Instantiate(ctx, addWasm)
	if err != nil {
		log.Panicln(err)
	}

	// Call the `add` function and print the results to the console.
	x, y := uint64(1), uint64(2)
	results, err := mod.ExportedFunction("add").Call(ctx, x, y)
	if err != nil {
		log.Panicln(err)
	}

	fmt.Printf("%d + %d = %d\n", x, y, results[0])

}
Output:

1 + 2 = 3
Example (CompileCache)

This is a basic example of using the file system compilation cache via wazero.NewCompilationCacheWithDir. The main goal is to show how it is configured.

package main

import (
	"context"
	_ "embed"
	"log"
	"os"

	"github.com/tetratelabs/wazero"
)

// This is a basic example of using the file system compilation cache via wazero.NewCompilationCacheWithDir.
// The main goal is to show how it is configured.
func main() {
	// Prepare a cache directory.
	cacheDir, err := os.MkdirTemp("", "example")
	if err != nil {
		log.Panicln(err)
	}
	defer os.RemoveAll(cacheDir)

	ctx := context.Background()

	// Create a runtime config which shares a compilation cache directory.
	cache := newCompilationCacheWithDir(cacheDir)
	defer cache.Close(ctx)
	config := wazero.NewRuntimeConfig().WithCompilationCache(cache)

	// Using the same wazero.CompilationCache instance allows the in-memory cache sharing.
	newRuntimeCompileClose(ctx, config)
	newRuntimeCompileClose(ctx, config)

	// Since the above stored compiled functions to disk as well, below won't compile from scratch.
	// Instead, compilation result stored in the directory is re-used.
	newRuntimeCompileClose(ctx, config.WithCompilationCache(newCompilationCacheWithDir(cacheDir)))
	newRuntimeCompileClose(ctx, config.WithCompilationCache(newCompilationCacheWithDir(cacheDir)))

}

func newCompilationCacheWithDir(cacheDir string) wazero.CompilationCache {
	cache, err := wazero.NewCompilationCacheWithDir(cacheDir)
	if err != nil {
		log.Panicln(err)
	}
	return cache
}

// newRuntimeCompileDestroy creates a new wazero.Runtime, compile a binary, and then delete the runtime.
func newRuntimeCompileClose(ctx context.Context, config wazero.RuntimeConfig) {
	r := wazero.NewRuntimeWithConfig(ctx, config)
	defer r.Close(ctx) // This closes everything this Runtime created except the file system cache.

	_, err := r.CompileModule(ctx, addWasm)
	if err != nil {
		log.Panicln(err)
	}
}
Output:

Example (RuntimeConfig_WithCustomSections)

This is a basic example of retrieving custom sections using RuntimeConfig.WithCustomSections.

package main

import (
	"context"
	_ "embed"
	"log"

	"github.com/tetratelabs/wazero"
	"github.com/tetratelabs/wazero/api"
)

// This is a basic example of retrieving custom sections using RuntimeConfig.WithCustomSections.
func main() {
	ctx := context.Background()
	config := wazero.NewRuntimeConfig().WithCustomSections(true)

	r := wazero.NewRuntimeWithConfig(ctx, config)
	defer r.Close(ctx)

	m, err := r.CompileModule(ctx, addWasm)
	if err != nil {
		log.Panicln(err)
	}

	if m.CustomSections() == nil {
		log.Panicln("Custom sections should not be nil")
	}

	mustContain(m.CustomSections(), "producers")
	mustContain(m.CustomSections(), "target_features")

}

func mustContain(ss []api.CustomSection, name string) {
	for _, s := range ss {
		if s.Name() == name {
			return
		}
	}
	log.Panicf("Could not find section named %s\n", name)
}
Output:

Example (WithFSConfig_embedFS)

This example shows how to configure an embed.FS.

package main

import (
	"embed"
	"io/fs"
	"log"

	"github.com/tetratelabs/wazero"
)

//go:embed testdata/index.html
var testdataIndex embed.FS

var moduleConfig wazero.ModuleConfig

// This example shows how to configure an embed.FS.
func main() {
	// Strip the embedded path testdata/
	rooted, err := fs.Sub(testdataIndex, "testdata")
	if err != nil {
		log.Panicln(err)
	}

	moduleConfig = wazero.NewModuleConfig().
		// Make "index.html" accessible to the guest as "/index.html".
		WithFSConfig(wazero.NewFSConfig().WithFSMount(rooted, "/"))
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CompilationCache

type CompilationCache interface{ api.Closer }

CompilationCache reduces time spent compiling (Runtime.CompileModule) the same wasm module.

Instances of this can be reused across multiple runtimes, if configured via RuntimeConfig.

func NewCompilationCache

func NewCompilationCache() CompilationCache

NewCompilationCache returns a new CompilationCache to be passed to RuntimeConfig. This configures only in-memory cache, and doesn't persist to the file system. See wazero.NewCompilationCacheWithDir for detail.

The returned CompilationCache can be used to share the in-memory compilation results across multiple instances of wazero.Runtime.

func NewCompilationCacheWithDir

func NewCompilationCacheWithDir(dirname string) (CompilationCache, error)

NewCompilationCacheWithDir is like wazero.NewCompilationCache except the result also writes state into the directory specified by `dirname` parameter.

If the dirname doesn't exist, this creates it or returns an error.

Those running wazero as a CLI or frequently restarting a process using the same wasm should use this feature to reduce time waiting to compile the same module a second time.

The contents written into dirname are wazero-version specific, meaning different versions of wazero will duplicate entries for the same input wasm.

Note: The embedder must safeguard this directory from external changes.

type CompiledModule

type CompiledModule interface {
	// Name returns the module name encoded into the binary or empty if not.
	Name() string

	// ImportedFunctions returns all the imported functions
	// (api.FunctionDefinition) in this module or nil if there are none.
	//
	// Note: Unlike ExportedFunctions, there is no unique constraint on
	// imports.
	ImportedFunctions() []api.FunctionDefinition

	// ExportedFunctions returns all the exported functions
	// (api.FunctionDefinition) in this module keyed on export name.
	ExportedFunctions() map[string]api.FunctionDefinition

	// ImportedMemories returns all the imported memories
	// (api.MemoryDefinition) in this module or nil if there are none.
	//
	// ## Notes
	//   - As of WebAssembly Core Specification 2.0, there can be at most one
	//     memory.
	//   - Unlike ExportedMemories, there is no unique constraint on imports.
	ImportedMemories() []api.MemoryDefinition

	// ExportedMemories returns all the exported memories
	// (api.MemoryDefinition) in this module keyed on export name.
	//
	// Note: As of WebAssembly Core Specification 2.0, there can be at most one
	// memory.
	ExportedMemories() map[string]api.MemoryDefinition

	// CustomSections returns all the custom sections
	// (api.CustomSection) in this module keyed on the section name.
	CustomSections() []api.CustomSection

	// Close releases all the allocated resources for this CompiledModule.
	//
	// Note: It is safe to call Close while having outstanding calls from an
	// api.Module instantiated from this.
	Close(context.Context) error
}

CompiledModule is a WebAssembly module ready to be instantiated (Runtime.InstantiateModule) as an api.Module.

In WebAssembly terminology, this is a decoded, validated, and possibly also compiled module. wazero avoids using the name "Module" for both before and after instantiation as the name conflation has caused confusion. See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#semantic-phases%E2%91%A0

Note: Closing the wazero.Runtime closes any CompiledModule it compiled.

type FSConfig

type FSConfig interface {
	// WithDirMount assigns a directory at `dir` to any paths beginning at
	// `guestPath`.
	//
	// If the same `guestPath` was assigned before, this overrides its value,
	// retaining the original precedence. See the documentation of FSConfig for
	// more details on `guestPath`.
	//
	// # Isolation
	//
	// The guest will have full access to this directory including escaping it
	// via relative path lookups like "../../". Full access includes operations
	// such as creating or deleting files, limited to any host level access
	// controls.
	//
	// # os.DirFS
	//
	// This configuration optimizes for WASI compatability which is sometimes
	// at odds with the behavior of os.DirFS. Hence, this will not behave
	// exactly the same as os.DirFS. See /RATIONALE.md for more.
	WithDirMount(dir, guestPath string) FSConfig

	// WithReadOnlyDirMount assigns a directory at `dir` to any paths
	// beginning at `guestPath`.
	//
	// This is the same as WithDirMount except only read operations are
	// permitted. However, escaping the directory via relative path lookups
	// like "../../" is still allowed.
	WithReadOnlyDirMount(dir, guestPath string) FSConfig

	// WithFSMount assigns a fs.FS file system for any paths beginning at
	// `guestPath`.
	//
	// If the same `guestPath` was assigned before, this overrides its value,
	// retaining the original precedence. See the documentation of FSConfig for
	// more details on `guestPath`.
	//
	// # Isolation
	//
	// fs.FS does not restrict the ability to overwrite returned files via
	// io.Writer. Moreover, os.DirFS documentation includes important notes
	// about isolation, which also applies to fs.Sub. As of Go 1.19, the
	// built-in file-systems are not jailed (chroot). See
	// https://github.com/golang/go/issues/42322
	//
	// # os.DirFS
	//
	// Due to limited control and functionality available in os.DirFS, we
	// advise using WithDirMount instead. There will be behavior differences
	// between os.DirFS and WithDirMount, as the latter biases towards what's
	// expected from WASI implementations.
	WithFSMount(fs fs.FS, guestPath string) FSConfig
}

FSConfig configures filesystem paths the embedding host allows the wasm guest to access. Unconfigured paths are not allowed, so functions like `path_open` result in unsupported errors (e.g. syscall.ENOSYS).

Guest Path

`guestPath` is the name of the path the guest should use a filesystem for, or empty for any files.

All `guestPath` paths are normalized, specifically removing any leading or trailing slashes. This means "/", "./" or "." all coerce to empty "".

Multiple `guestPath` values can be configured, but the last longest match wins. For example, if "tmp", then "" were added, a request to open "tmp/foo.txt" use the filesystem associated with "tmp" even though a wider path, "" (all files), was added later.

A `guestPath` of "." coerces to the empty string "" because the current directory is handled by the guest. In other words, the guest resolves ites current directory prior to requesting files.

More notes on `guestPath`

  • Go compiled with runtime.GOOS=js do not pay attention to this value. Hence, you need to normalize the filesystem with NewRootFS to ensure paths requested resolve as expected.
  • Working directories are typically tracked in wasm, though possible some relative paths are requested. For example, TinyGo may attempt to resolve a path "../.." in unit tests.
  • Zig uses the first path name it sees as the initial working directory of the process.

Scope

Configuration here is module instance scoped. This means you can use the same configuration for multiple calls to Runtime.InstantiateModule. Each module will have a different file descriptor table. Any errors accessing resources allowed here are deferred to instantiation time of each module.

Any host resources present at the time of configuration, but deleted before Runtime.InstantiateModule will trap/panic when the guest wasm initializes or calls functions like `fd_read`.

Windows

While wazero supports Windows as a platform, all known compilers use POSIX conventions at runtime. For example, even when running on Windows, paths used by wasm are separated by forward slash (/), not backslash (\).

Notes

  • FSConfig is immutable. Each WithXXX function returns a new instance including the corresponding change.
  • RATIONALE.md includes design background and relationship to WebAssembly System Interfaces (WASI).

func NewFSConfig

func NewFSConfig() FSConfig

NewFSConfig returns a FSConfig that can be used for configuring module instantiation.

type HostFunctionBuilder

type HostFunctionBuilder interface {
	// WithGoFunction is an advanced feature for those who need higher
	// performance than WithFunc at the cost of more complexity.
	//
	// Here's an example addition function:
	//
	//	builder.WithGoFunction(api.GoFunc(func(ctx context.Context, params []uint64) []uint64 {
	//		x, y := uint32(params[0]), uint32(params[1])
	//		sum := x + y
	//		return []uint64{sum}
	//	}, []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32})
	//
	// As you can see above, defining in this way implies knowledge of which
	// WebAssembly api.ValueType is appropriate for each parameter and result.
	//
	// See WithGoModuleFunction if you also need to access the calling module.
	WithGoFunction(fn api.GoFunction, params, results []api.ValueType) HostFunctionBuilder

	// WithGoModuleFunction is an advanced feature for those who need higher
	// performance than WithFunc at the cost of more complexity.
	//
	// Here's an example addition function that loads operands from memory:
	//
	//	builder.WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, mod api.Module, params []uint64) []uint64 {
	//		mem := m.Memory()
	//		offset := uint32(params[0])
	//
	//		x, _ := mem.ReadUint32Le(ctx, offset)
	//		y, _ := mem.ReadUint32Le(ctx, offset + 4) // 32 bits == 4 bytes!
	//		sum := x + y
	//
	//		return []uint64{sum}
	//	}, []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32})
	//
	// As you can see above, defining in this way implies knowledge of which
	// WebAssembly api.ValueType is appropriate for each parameter and result.
	//
	// See WithGoFunction if you don't need access to the calling module.
	WithGoModuleFunction(fn api.GoModuleFunction, params, results []api.ValueType) HostFunctionBuilder

	// WithFunc uses reflect.Value to map a go `func` to a WebAssembly
	// compatible Signature. An input that isn't a `func` will fail to
	// instantiate.
	//
	// Here's an example of an addition function:
	//
	//	builder.WithFunc(func(cxt context.Context, x, y uint32) uint32 {
	//		return x + y
	//	})
	//
	// # Defining a function
	//
	// Except for the context.Context and optional api.Module, all parameters
	// or result types must map to WebAssembly numeric value types. This means
	// uint32, int32, uint64, int32 float32 or float64.
	//
	// api.Module may be specified as the second parameter, usually to access
	// memory. This is important because there are only numeric types in Wasm.
	// The only way to share other data is via writing memory and sharing
	// offsets.
	//
	//	builder.WithFunc(func(ctx context.Context, m api.Module, offset uint32) uint32 {
	//		mem := m.Memory()
	//		x, _ := mem.ReadUint32Le(ctx, offset)
	//		y, _ := mem.ReadUint32Le(ctx, offset + 4) // 32 bits == 4 bytes!
	//		return x + y
	//	})
	//
	// This example propagates context properly when calling other functions
	// exported in the api.Module:
	//
	//	builder.WithFunc(func(ctx context.Context, m api.Module, offset, byteCount uint32) uint32 {
	//		fn = m.ExportedFunction("__read")
	//		results, err := fn(ctx, offset, byteCount)
	//	--snip--
	WithFunc(interface{}) HostFunctionBuilder

	// WithName defines the optional module-local name of this function, e.g.
	// "random_get"
	//
	// Note: This is not required to match the Export name.
	WithName(name string) HostFunctionBuilder

	// WithParameterNames defines optional parameter names of the function
	// signature, e.x. "buf", "buf_len"
	//
	// Note: When defined, names must be provided for all parameters.
	WithParameterNames(names ...string) HostFunctionBuilder

	// WithResultNames defines optional result names of the function
	// signature, e.x. "errno"
	//
	// Note: When defined, names must be provided for all results.
	WithResultNames(names ...string) HostFunctionBuilder

	// Export exports this to the HostModuleBuilder as the given name, e.g.
	// "random_get"
	Export(name string) HostModuleBuilder
}

HostFunctionBuilder defines a host function (in Go), so that a WebAssembly binary (e.g. %.wasm file) can import and use it.

Here's an example of an addition function:

hostModuleBuilder.NewFunctionBuilder().
	WithFunc(func(cxt context.Context, x, y uint32) uint32 {
		return x + y
	}).
	Export("add")

Memory

All host functions act on the importing api.Module, including any memory exported in its binary (%.wasm file). If you are reading or writing memory, it is sand-boxed Wasm memory defined by the guest.

Below, `m` is the importing module, defined in Wasm. `fn` is a host function added via Export. This means that `x` was read from memory defined in Wasm, not arbitrary memory in the process.

fn := func(ctx context.Context, m api.Module, offset uint32) uint32 {
	x, _ := m.Memory().ReadUint32Le(ctx, offset)
	return x
}

type HostModuleBuilder

type HostModuleBuilder interface {

	// NewFunctionBuilder begins the definition of a host function.
	NewFunctionBuilder() HostFunctionBuilder

	// Compile returns a CompiledModule that can be instantiated by Runtime.
	Compile(context.Context) (CompiledModule, error)

	// Instantiate is a convenience that calls Compile, then Runtime.InstantiateModule.
	// This can fail for reasons documented on Runtime.InstantiateModule.
	//
	// Here's an example:
	//
	//	ctx := context.Background()
	//	r := wazero.NewRuntime(ctx)
	//	defer r.Close(ctx) // This closes everything this Runtime created.
	//
	//	hello := func() {
	//		fmt.Fprintln(stdout, "hello!")
	//	}
	//	env, _ := r.NewHostModuleBuilder("env").
	//		NewFunctionBuilder().WithFunc(hello).Export("hello").
	//		Instantiate(ctx)
	//
	// # Notes
	//
	//   - Closing the Runtime has the same effect as closing the result.
	//   - Fields in the builder are copied during instantiation: Later changes do not affect the instantiated result.
	//   - To avoid using configuration defaults, use Compile instead.
	Instantiate(context.Context) (api.Module, error)
}

HostModuleBuilder is a way to define host functions (in Go), so that a WebAssembly binary (e.g. %.wasm file) can import and use them.

Specifically, this implements the host side of an Application Binary Interface (ABI) like WASI or AssemblyScript.

For example, this defines and instantiates a module named "env" with one function:

ctx := context.Background()
r := wazero.NewRuntime(ctx)
defer r.Close(ctx) // This closes everything this Runtime created.

hello := func() {
	fmt.Fprintln(stdout, "hello!")
}
env, _ := r.NewHostModuleBuilder("env").
	NewFunctionBuilder().WithFunc(hello).Export("hello").
	Instantiate(ctx, r)

If the same module may be instantiated multiple times, it is more efficient to separate steps. Here's an example:

compiled, _ := r.NewHostModuleBuilder("env").
	NewFunctionBuilder().WithFunc(getRandomString).Export("get_random_string").
	Compile(ctx)

env1, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.1"))
env2, _ := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("env.2"))

See HostFunctionBuilder for valid host function signatures and other details.

Notes

  • HostModuleBuilder is mutable: each method returns the same instance for chaining.
  • methods do not return errors, to allow chaining. Any validation errors are deferred until Compile.
  • Insertion order is not retained. Anything defined by this builder is sorted lexicographically on Compile.

type ModuleConfig

type ModuleConfig interface {
	// WithArgs assigns command-line arguments visible to an imported function that reads an arg vector (argv). Defaults to
	// none. Runtime.InstantiateModule errs if any arg is empty.
	//
	// These values are commonly read by the functions like "args_get" in "wasi_snapshot_preview1" although they could be
	// read by functions imported from other modules.
	//
	// Similar to os.Args and exec.Cmd Env, many implementations would expect a program name to be argv[0]. However, neither
	// WebAssembly nor WebAssembly System Interfaces (WASI) define this. Regardless, you may choose to set the first
	// argument to the same value set via WithName.
	//
	// Note: This does not default to os.Args as that violates sandboxing.
	//
	// See https://linux.die.net/man/3/argv and https://en.wikipedia.org/wiki/Null-terminated_string
	WithArgs(...string) ModuleConfig

	// WithEnv sets an environment variable visible to a Module that imports functions. Defaults to none.
	// Runtime.InstantiateModule errs if the key is empty or contains a NULL(0) or equals("") character.
	//
	// Validation is the same as os.Setenv on Linux and replaces any existing value. Unlike exec.Cmd Env, this does not
	// default to the current process environment as that would violate sandboxing. This also does not preserve order.
	//
	// Environment variables are commonly read by the functions like "environ_get" in "wasi_snapshot_preview1" although
	// they could be read by functions imported from other modules.
	//
	// While similar to process configuration, there are no assumptions that can be made about anything OS-specific. For
	// example, neither WebAssembly nor WebAssembly System Interfaces (WASI) define concerns processes have, such as
	// case-sensitivity on environment keys. For portability, define entries with case-insensitively unique keys.
	//
	// See https://linux.die.net/man/3/environ and https://en.wikipedia.org/wiki/Null-terminated_string
	WithEnv(key, value string) ModuleConfig

	// WithFS is a convenience that calls WithFSConfig with an FSConfig of the
	// input for the root ("/") guest path.
	WithFS(fs.FS) ModuleConfig

	// WithFSConfig configures the filesystem available to each guest
	// instantiated with this configuration. By default, no file access is
	// allowed, so functions like `path_open` result in unsupported errors
	// (e.g. syscall.ENOSYS).
	WithFSConfig(FSConfig) ModuleConfig

	// WithName configures the module name. Defaults to what was decoded from
	// the name section.
	WithName(string) ModuleConfig

	// WithStartFunctions configures the functions to call after the module is
	// instantiated. Defaults to "_start".
	//
	// # Notes
	//
	//   - If any function doesn't exist, it is skipped. However, all functions
	//	  that do exist are called in order.
	//   - Some start functions may exit the module during instantiate with a
	//	  sys.ExitError (e.g. emscripten), preventing use of exported functions.
	WithStartFunctions(...string) ModuleConfig

	// WithStderr configures where standard error (file descriptor 2) is written. Defaults to io.Discard.
	//
	// This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could
	// be used by functions imported from other modules.
	//
	// # Notes
	//
	//   - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close.
	//   - This does not default to os.Stderr as that both violates sandboxing and prevents concurrent modules.
	//
	// See https://linux.die.net/man/3/stderr
	WithStderr(io.Writer) ModuleConfig

	// WithStdin configures where standard input (file descriptor 0) is read. Defaults to return io.EOF.
	//
	// This reader is most commonly used by the functions like "fd_read" in "wasi_snapshot_preview1" although it could
	// be used by functions imported from other modules.
	//
	// # Notes
	//
	//   - The caller is responsible to close any io.Reader they supply: It is not closed on api.Module Close.
	//   - This does not default to os.Stdin as that both violates sandboxing and prevents concurrent modules.
	//
	// See https://linux.die.net/man/3/stdin
	WithStdin(io.Reader) ModuleConfig

	// WithStdout configures where standard output (file descriptor 1) is written. Defaults to io.Discard.
	//
	// This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could
	// be used by functions imported from other modules.
	//
	// # Notes
	//
	//   - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close.
	//   - This does not default to os.Stdout as that both violates sandboxing and prevents concurrent modules.
	//
	// See https://linux.die.net/man/3/stdout
	WithStdout(io.Writer) ModuleConfig

	// WithWalltime configures the wall clock, sometimes referred to as the
	// real time clock. Defaults to a fake result that increases by 1ms on
	// each reading.
	//
	// Here's an example that uses a custom clock:
	//	moduleConfig = moduleConfig.
	//		WithWalltime(func(context.Context) (sec int64, nsec int32) {
	//			return clock.walltime()
	//		}, sys.ClockResolution(time.Microsecond.Nanoseconds()))
	//
	// Note: This does not default to time.Now as that violates sandboxing. Use
	// WithSysWalltime for a usable implementation.
	WithWalltime(sys.Walltime, sys.ClockResolution) ModuleConfig

	// WithSysWalltime uses time.Now for sys.Walltime with a resolution of 1us
	// (1000ns).
	//
	// See WithWalltime
	WithSysWalltime() ModuleConfig

	// WithNanotime configures the monotonic clock, used to measure elapsed
	// time in nanoseconds. Defaults to a fake result that increases by 1ms
	// on each reading.
	//
	// Here's an example that uses a custom clock:
	//	moduleConfig = moduleConfig.
	//		WithNanotime(func(context.Context) int64 {
	//			return clock.nanotime()
	//		}, sys.ClockResolution(time.Microsecond.Nanoseconds()))
	//
	// # Notes:
	//   - This does not default to time.Since as that violates sandboxing.
	//   - Some compilers implement sleep by looping on sys.Nanotime (e.g. Go).
	//   - If you set this, you should probably set WithNanosleep also.
	//   - Use WithSysNanotime for a usable implementation.
	WithNanotime(sys.Nanotime, sys.ClockResolution) ModuleConfig

	// WithSysNanotime uses time.Now for sys.Nanotime with a resolution of 1us.
	//
	// See WithNanotime
	WithSysNanotime() ModuleConfig

	// WithNanosleep configures the how to pause the current goroutine for at
	// least the configured nanoseconds. Defaults to return immediately.
	//
	// This example uses a custom sleep function:
	//	moduleConfig = moduleConfig.
	//		WithNanosleep(func(ns int64) {
	//			rel := unix.NsecToTimespec(ns)
	//			remain := unix.Timespec{}
	//			for { // loop until no more time remaining
	//				err := unix.ClockNanosleep(unix.CLOCK_MONOTONIC, 0, &rel, &remain)
	//			--snip--
	//
	// # Notes:
	//   - This primarily supports `poll_oneoff` for relative clock events.
	//   - This does not default to time.Sleep as that violates sandboxing.
	//   - Some compilers implement sleep by looping on sys.Nanotime (e.g. Go).
	//   - If you set this, you should probably set WithNanotime also.
	//   - Use WithSysNanosleep for a usable implementation.
	WithNanosleep(sys.Nanosleep) ModuleConfig

	// WithOsyield yields the processor, typically to implement spin-wait
	// loops. Defaults to return immediately.
	//
	// # Notes:
	//   - This primarily supports `sched_yield` in WASI
	//   - This does not default to runtime.osyield as that violates sandboxing.
	WithOsyield(sys.Osyield) ModuleConfig

	// WithSysNanosleep uses time.Sleep for sys.Nanosleep.
	//
	// See WithNanosleep
	WithSysNanosleep() ModuleConfig

	// WithRandSource configures a source of random bytes. Defaults to return a
	// deterministic source. You might override this with crypto/rand.Reader
	//
	// This reader is most commonly used by the functions like "random_get" in
	// "wasi_snapshot_preview1", "seed" in AssemblyScript standard "env", and
	// "getRandomData" when runtime.GOOS is "js".
	//
	// Note: The caller is responsible to close any io.Reader they supply: It
	// is not closed on api.Module Close.
	WithRandSource(io.Reader) ModuleConfig
}

ModuleConfig configures resources needed by functions that have low-level interactions with the host operating system. Using this, resources such as STDIN can be isolated, so that the same module can be safely instantiated multiple times.

Here's an example:

// Initialize base configuration:
config := wazero.NewModuleConfig().WithStdout(buf).WithSysNanotime()

// Assign different configuration on each instantiation
mod, _ := r.InstantiateModule(ctx, compiled, config.WithName("rotate").WithArgs("rotate", "angle=90", "dir=cw"))

While wazero supports Windows as a platform, host functions using ModuleConfig follow a UNIX dialect. See RATIONALE.md for design background and relationship to WebAssembly System Interfaces (WASI).

Note: ModuleConfig is immutable. Each WithXXX function returns a new instance including the corresponding change.

func NewModuleConfig

func NewModuleConfig() ModuleConfig

NewModuleConfig returns a ModuleConfig that can be used for configuring module instantiation.

type Runtime

type Runtime interface {
	// Instantiate instantiates a module from the WebAssembly binary (%.wasm)
	// with default configuration.
	//
	// Here's an example:
	//	ctx := context.Background()
	//	r := wazero.NewRuntime(ctx)
	//	defer r.Close(ctx) // This closes everything this Runtime created.
	//
	//	mod, _ := r.Instantiate(ctx, wasm)
	//
	// See InstantiateWithConfig for configuration overrides.
	Instantiate(ctx context.Context, source []byte) (api.Module, error)

	// InstantiateWithConfig instantiates a module from the WebAssembly binary
	// (%.wasm) or errs if invalid.
	//
	// Here's an example:
	//	ctx := context.Background()
	//	r := wazero.NewRuntime(ctx)
	//	defer r.Close(ctx) // This closes everything this Runtime created.
	//
	//	mod, _ := r.InstantiateWithConfig(ctx, wasm,
	//		wazero.NewModuleConfig().WithName("rotate"))
	//
	// # Notes
	//
	//   - This is a convenience utility that chains CompileModule with
	//     InstantiateModule. To instantiate the same source multiple times,
	//     use CompileModule as InstantiateModule avoids redundant decoding
	//     and/or compilation.
	//   - If you aren't overriding defaults, use Instantiate.
	InstantiateWithConfig(ctx context.Context, source []byte, config ModuleConfig) (api.Module, error)

	// NewHostModuleBuilder lets you create modules out of functions defined in Go.
	//
	// Below defines and instantiates a module named "env" with one function:
	//
	//	ctx := context.Background()
	//	hello := func() {
	//		fmt.Fprintln(stdout, "hello!")
	//	}
	//	_, err := r.NewHostModuleBuilder("env").
	//		NewFunctionBuilder().WithFunc(hello).Export("hello").
	//		Instantiate(ctx, r)
	NewHostModuleBuilder(moduleName string) HostModuleBuilder

	// CompileModule decodes the WebAssembly binary (%.wasm) or errs if invalid.
	// Any pre-compilation done after decoding wasm is dependent on RuntimeConfig.
	//
	// There are two main reasons to use CompileModule instead of Instantiate:
	//   - Improve performance when the same module is instantiated multiple times under different names
	//   - Reduce the amount of errors that can occur during InstantiateModule.
	//
	// # Notes
	//
	//   - The resulting module name defaults to what was binary from the custom name section.
	//   - Any pre-compilation done after decoding the source is dependent on RuntimeConfig.
	//
	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#name-section%E2%91%A0
	CompileModule(ctx context.Context, binary []byte) (CompiledModule, error)

	// InstantiateModule instantiates the module or errs if the configuration was invalid.
	//
	// Here's an example:
	//	mod, _ := n.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("prod"))
	//
	// While CompiledModule is pre-validated, there are a few situations which can cause an error:
	//   - The module name is already in use.
	//   - The module has a table element initializer that resolves to an index outside the Table minimum size.
	//   - The module has a start function, and it failed to execute.
	InstantiateModule(ctx context.Context, compiled CompiledModule, config ModuleConfig) (api.Module, error)

	// CloseWithExitCode closes all the modules that have been initialized in this Runtime with the provided exit code.
	// An error is returned if any module returns an error when closed.
	//
	// Here's an example:
	//	ctx := context.Background()
	//	r := wazero.NewRuntime(ctx)
	//	defer r.CloseWithExitCode(ctx, 2) // This closes everything this Runtime created.
	//
	//	// Everything below here can be closed, but will anyway due to above.
	//	_, _ = wasi_snapshot_preview1.InstantiateSnapshotPreview1(ctx, r)
	//	mod, _ := r.Instantiate(ctx, wasm)
	CloseWithExitCode(ctx context.Context, exitCode uint32) error

	// Module returns an instantiated module in this runtime or nil if there aren't any.
	Module(moduleName string) api.Module

	// Closer closes all compiled code by delegating to CloseWithExitCode with an exit code of zero.
	api.Closer
}

Runtime allows embedding of WebAssembly modules.

The below is an example of basic initialization:

ctx := context.Background()
r := wazero.NewRuntime(ctx)
defer r.Close(ctx) // This closes everything this Runtime created.

mod, _ := r.Instantiate(ctx, wasm)

func NewRuntime

func NewRuntime(ctx context.Context) Runtime

NewRuntime returns a runtime with a configuration assigned by NewRuntimeConfig.

func NewRuntimeWithConfig

func NewRuntimeWithConfig(ctx context.Context, rConfig RuntimeConfig) Runtime

NewRuntimeWithConfig returns a runtime with the given configuration.

type RuntimeConfig

type RuntimeConfig interface {
	// WithCoreFeatures sets the WebAssembly Core specification features this
	// runtime supports. Defaults to api.CoreFeaturesV2.
	//
	// Example of disabling a specific feature:
	//	features := api.CoreFeaturesV2.SetEnabled(api.CoreFeatureMutableGlobal, false)
	//	rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(features)
	//
	// # Why default to version 2.0?
	//
	// Many compilers that target WebAssembly require features after
	// api.CoreFeaturesV1 by default. For example, TinyGo v0.24+ requires
	// api.CoreFeatureBulkMemoryOperations. To avoid runtime errors, wazero
	// defaults to api.CoreFeaturesV2, even though it is not yet a Web
	// Standard (REC).
	WithCoreFeatures(api.CoreFeatures) RuntimeConfig

	// WithMemoryLimitPages overrides the maximum pages allowed per memory. The
	// default is 65536, allowing 4GB total memory per instance. Setting a
	// value larger than default will panic.
	//
	// This example reduces the largest possible memory size from 4GB to 128KB:
	//	rConfig = wazero.NewRuntimeConfig().WithMemoryLimitPages(2)
	//
	// Note: Wasm has 32-bit memory and each page is 65536 (2^16) bytes. This
	// implies a max of 65536 (2^16) addressable pages.
	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem
	WithMemoryLimitPages(memoryLimitPages uint32) RuntimeConfig

	// WithMemoryCapacityFromMax eagerly allocates max memory, unless max is
	// not defined. The default is false, which means minimum memory is
	// allocated and any call to grow memory results in re-allocations.
	//
	// This example ensures any memory.grow instruction will never re-allocate:
	//	rConfig = wazero.NewRuntimeConfig().WithMemoryCapacityFromMax(true)
	//
	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem
	WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig

	// WithDebugInfoEnabled toggles DWARF based stack traces in the face of
	// runtime errors. Defaults to true.
	//
	// Those who wish to disable this, can like so:
	//
	//	r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfig().WithDebugInfoEnabled(false)
	//
	// When disabled, a stack trace message looks like:
	//
	//	wasm stack trace:
	//		.runtime._panic(i32)
	//		.myFunc()
	//		.main.main()
	//		.runtime.run()
	//		._start()
	//
	// When enabled, the stack trace includes source code information:
	//
	//	wasm stack trace:
	//		.runtime._panic(i32)
	//		  0x16e2: /opt/homebrew/Cellar/tinygo/0.26.0/src/runtime/runtime_tinygowasm.go:73:6
	//		.myFunc()
	//		  0x190b: /Users/XXXXX/wazero/internal/testing/dwarftestdata/testdata/main.go:19:7
	//		.main.main()
	//		  0x18ed: /Users/XXXXX/wazero/internal/testing/dwarftestdata/testdata/main.go:4:3
	//		.runtime.run()
	//		  0x18cc: /opt/homebrew/Cellar/tinygo/0.26.0/src/runtime/scheduler_none.go:26:10
	//		._start()
	//		  0x18b6: /opt/homebrew/Cellar/tinygo/0.26.0/src/runtime/runtime_wasm_wasi.go:22:5
	//
	// Note: This only takes into effect when the original Wasm binary has the
	// DWARF "custom sections" that are often stripped, depending on
	// optimization flags passed to the compiler.
	WithDebugInfoEnabled(bool) RuntimeConfig

	// WithCompilationCache configures how runtime caches the compiled modules. In the default configuration, compilation results are
	// only in-memory until Runtime.Close is closed, and not shareable by multiple Runtime.
	//
	// Below defines the shared cache across multiple instances of Runtime:
	//
	//	// Creates the new Cache and the runtime configuration with it.
	//	cache := wazero.NewCompilationCache()
	//	defer cache.Close()
	//	config := wazero.NewRuntimeConfig().WithCompilationCache(c)
	//
	//	// Creates two runtimes while sharing compilation caches.
	//	foo := wazero.NewRuntimeWithConfig(context.Background(), config)
	// 	bar := wazero.NewRuntimeWithConfig(context.Background(), config)
	WithCompilationCache(CompilationCache) RuntimeConfig

	// WithCustomSections toggles parsing of "custom sections". Defaults to false.
	//
	// When enabled, it is possible to retrieve custom sections from a CompiledModule:
	//
	//	config := wazero.NewRuntimeConfig().WithCustomSections(true)
	//	r := wazero.NewRuntimeWithConfig(ctx, config)
	//	c, err := r.CompileModule(ctx, wasm)
	//	customSections := c.CustomSections()
	WithCustomSections(bool) RuntimeConfig

	// WithCloseOnContextDone ensures the executions of functions to be closed under one of the following circumstances:
	//
	// 	- context.Context passed to the Call method of api.Function is canceled during execution. (i.e. ctx by context.WithCancel)
	// 	- context.Context passed to the Call method of api.Function reaches timeout during execution. (i.e. ctx by context.WithTimeout or context.WithDeadline)
	// 	- Close or CloseWithExitCode of api.Module is explicitly called during execution.
	//
	// This is especially useful when one wants to run untrusted Wasm binaries since otherwise, any invocation of
	// api.Function can potentially block the corresponding Goroutine forever. Moreover, it might block the
	// entire underlying OS thread which runs the api.Function call. See "Why it's safe to execute runtime-generated
	// machine codes against async Goroutine preemption" section in internal/engine/compiler/RATIONALE.md for detail.
	//
	// Note that this comes with a bit of extra cost when enabled. The reason is that internally this forces
	// interpreter and compiler runtimes to insert the periodical checks on the conditions above. For that reason,
	// this is disabled by default.
	//
	// See examples in context_done_example_test.go for the end-to-end demonstrations.
	//
	// When the invocations of api.Function are closed due to this, sys.ExitError is raised to the callers and
	// the api.Module from which the functions are derived is made closed.
	WithCloseOnContextDone(bool) RuntimeConfig
}

RuntimeConfig controls runtime behavior, with the default implementation as NewRuntimeConfig

The example below explicitly limits to Wasm Core 1.0 features as opposed to relying on defaults:

rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(api.CoreFeaturesV1)

Note: RuntimeConfig is immutable. Each WithXXX function returns a new instance including the corresponding change.

func NewRuntimeConfig

func NewRuntimeConfig() RuntimeConfig

NewRuntimeConfig returns a RuntimeConfig using the compiler if it is supported in this environment, or the interpreter otherwise.

func NewRuntimeConfigCompiler

func NewRuntimeConfigCompiler() RuntimeConfig

NewRuntimeConfigCompiler compiles WebAssembly modules into runtime.GOARCH-specific assembly for optimal performance.

The default implementation is AOT (Ahead of Time) compilation, applied at Runtime.CompileModule. This allows consistent runtime performance, as well the ability to reduce any first request penalty.

Note: While this is technically AOT, this does not imply any action on your part. wazero automatically performs ahead-of-time compilation as needed when Runtime.CompileModule is invoked.

Warning: This panics at runtime if the runtime.GOOS or runtime.GOARCH does not support Compiler. Use NewRuntimeConfig to safely detect and fallback to NewRuntimeConfigInterpreter if needed.

func NewRuntimeConfigInterpreter

func NewRuntimeConfigInterpreter() RuntimeConfig

NewRuntimeConfigInterpreter interprets WebAssembly modules instead of compiling them into assembly.

Directories

Path Synopsis
Package api includes constants and interfaces used by both end-users and internal implementations.
Package api includes constants and interfaces used by both end-users and internal implementations.
cmd
examples
Package experimental includes features we aren't yet sure about.
Package experimental includes features we aren't yet sure about.
imports
assemblyscript
Package assemblyscript contains Go-defined special functions imported by AssemblyScript under the module name "env".
Package assemblyscript contains Go-defined special functions imported by AssemblyScript under the module name "env".
emscripten
Package emscripten contains Go-defined special functions imported by Emscripten under the module name "env".
Package emscripten contains Go-defined special functions imported by Emscripten under the module name "env".
go
Package gojs allows you to run wasm binaries compiled by Go when `GOARCH=wasm GOOS=js`.
Package gojs allows you to run wasm binaries compiled by Go when `GOARCH=wasm GOOS=js`.
wasi_snapshot_preview1
Package wasi_snapshot_preview1 contains Go-defined functions to access system calls, such as opening a file, similar to Go's x/sys package.
Package wasi_snapshot_preview1 contains Go-defined functions to access system calls, such as opening a file, similar to Go's x/sys package.
internal
asm
fstest
Package fstest defines filesystem test cases that help validate host functions implementing WASI and `GOARCH=wasm GOOS=js`.
Package fstest defines filesystem test cases that help validate host functions implementing WASI and `GOARCH=wasm GOOS=js`.
gojs/custom
Package custom is similar to the WebAssembly Custom Sections.
Package custom is similar to the WebAssembly Custom Sections.
gojs/goarch
Package goarch isolates code from runtime.GOARCH=wasm in a way that avoids cyclic dependencies when re-used from other packages.
Package goarch isolates code from runtime.GOARCH=wasm in a way that avoids cyclic dependencies when re-used from other packages.
gojs/goos
Package goos isolates code from runtime.GOOS=js in a way that avoids cyclic dependencies when re-used from other packages.
Package goos isolates code from runtime.GOOS=js in a way that avoids cyclic dependencies when re-used from other packages.
gojs/run
Package run exists to avoid dependency cycles when keeping most of gojs code internal.
Package run exists to avoid dependency cycles when keeping most of gojs code internal.
logging
Package logging includes utilities used to log function calls.
Package logging includes utilities used to log function calls.
platform
Package platform includes runtime-specific code needed for the compiler or otherwise.
Package platform includes runtime-specific code needed for the compiler or otherwise.
sys
sysfs
Package sysfs includes a low-level filesystem interface and utilities needed for WebAssembly host functions (ABI) such as WASI and runtime.GOOS=js.
Package sysfs includes a low-level filesystem interface and utilities needed for WebAssembly host functions (ABI) such as WASI and runtime.GOOS=js.
testing/enginetest
Package enginetest contains tests common to any wasm.Engine implementation.
Package enginetest contains tests common to any wasm.Engine implementation.
testing/require
Package require includes test assertions that fail the test immediately.
Package require includes test assertions that fail the test immediately.
u32
u64
wasi_snapshot_preview1
Package wasi_snapshot_preview1 is a helper to remove package cycles re-using constants.
Package wasi_snapshot_preview1 is a helper to remove package cycles re-using constants.
wasmdebug
Package wasmdebug contains utilities used to give consistent search keys between stack traces and error messages.
Package wasmdebug contains utilities used to give consistent search keys between stack traces and error messages.
wasmruntime
Package wasmruntime contains internal symbols shared between modules for error handling.
Package wasmruntime contains internal symbols shared between modules for error handling.
wazeroir
Package wazeroir is a pkg to compile down the standard Wasm binary to wazero's specific IR (wazeroir).
Package wazeroir is a pkg to compile down the standard Wasm binary to wazero's specific IR (wazeroir).
Package sys includes constants and types used by both public and internal APIs.
Package sys includes constants and types used by both public and internal APIs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL