summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/config.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-05-27 15:46:15 +0000
committerLibravatar GitHub <noreply@github.com>2024-05-27 17:46:15 +0200
commit1e7b32490dfdccddd04f46d4b0416b48d749d51b (patch)
tree62a11365933a5a11e0800af64cbdf9172e5e6e7a /vendor/github.com/tetratelabs/wazero/config.go
parent[chore] Small styling + link issues (#2933) (diff)
downloadgotosocial-1e7b32490dfdccddd04f46d4b0416b48d749d51b.tar.xz
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/config.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/config.go876
1 files changed, 876 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/config.go b/vendor/github.com/tetratelabs/wazero/config.go
new file mode 100644
index 000000000..819a76df5
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/config.go
@@ -0,0 +1,876 @@
+package wazero
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "io/fs"
+ "math"
+ "net"
+ "time"
+
+ "github.com/tetratelabs/wazero/api"
+ experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
+ "github.com/tetratelabs/wazero/internal/engine/interpreter"
+ "github.com/tetratelabs/wazero/internal/engine/wazevo"
+ "github.com/tetratelabs/wazero/internal/filecache"
+ "github.com/tetratelabs/wazero/internal/internalapi"
+ "github.com/tetratelabs/wazero/internal/platform"
+ internalsock "github.com/tetratelabs/wazero/internal/sock"
+ internalsys "github.com/tetratelabs/wazero/internal/sys"
+ "github.com/tetratelabs/wazero/internal/wasm"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// 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)
+//
+// # Notes
+//
+// - This is an interface for decoupling, not third-party implementations.
+// All implementations are in wazero.
+// - RuntimeConfig is immutable. Each WithXXX function returns a new instance
+// including the corresponding change.
+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 if the maximum is
+ // not encoded in a Wasm binary. 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
+ //
+ // Note: if the memory maximum is not encoded in a Wasm binary, this
+ // results in allocating 4GB. See the doc on WithMemoryLimitPages for detail.
+ 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)
+ //
+ // # Cache Key
+ //
+ // Cached files are keyed on the version of wazero. This is obtained from go.mod of your application,
+ // and we use it to verify the compatibility of caches against the currently-running wazero.
+ // However, if you use this in tests of a package not named as `main`, then wazero cannot obtain the correct
+ // version of wazero due to the known issue of debug.BuildInfo function: https://github.com/golang/go/issues/33976.
+ // As a consequence, your cache won't contain the correct version information and always be treated as `dev` version.
+ // To avoid this issue, you can pass -ldflags "-X github.com/tetratelabs/wazero/internal/version.version=foo" when running tests.
+ 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 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
+}
+
+// NewRuntimeConfig returns a RuntimeConfig using the compiler if it is supported in this environment,
+// or the interpreter otherwise.
+func NewRuntimeConfig() RuntimeConfig {
+ return newRuntimeConfig()
+}
+
+type newEngine func(context.Context, api.CoreFeatures, filecache.Cache) wasm.Engine
+
+type runtimeConfig struct {
+ enabledFeatures api.CoreFeatures
+ memoryLimitPages uint32
+ memoryCapacityFromMax bool
+ engineKind engineKind
+ dwarfDisabled bool // negative as defaults to enabled
+ newEngine newEngine
+ cache CompilationCache
+ storeCustomSections bool
+ ensureTermination bool
+}
+
+// engineLessConfig helps avoid copy/pasting the wrong defaults.
+var engineLessConfig = &runtimeConfig{
+ enabledFeatures: api.CoreFeaturesV2,
+ memoryLimitPages: wasm.MemoryLimitPages,
+ memoryCapacityFromMax: false,
+ dwarfDisabled: false,
+}
+
+type engineKind int
+
+const (
+ engineKindCompiler engineKind = iota
+ engineKindInterpreter
+ engineKindCount
+)
+
+// 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 NewRuntimeConfigCompiler() RuntimeConfig {
+ ret := engineLessConfig.clone()
+ ret.engineKind = engineKindCompiler
+ ret.newEngine = wazevo.NewEngine
+ return ret
+}
+
+// NewRuntimeConfigInterpreter interprets WebAssembly modules instead of compiling them into assembly.
+func NewRuntimeConfigInterpreter() RuntimeConfig {
+ ret := engineLessConfig.clone()
+ ret.engineKind = engineKindInterpreter
+ ret.newEngine = interpreter.NewEngine
+ return ret
+}
+
+// clone makes a deep copy of this runtime config.
+func (c *runtimeConfig) clone() *runtimeConfig {
+ ret := *c // copy except maps which share a ref
+ return &ret
+}
+
+// WithCoreFeatures implements RuntimeConfig.WithCoreFeatures
+func (c *runtimeConfig) WithCoreFeatures(features api.CoreFeatures) RuntimeConfig {
+ ret := c.clone()
+ ret.enabledFeatures = features
+ return ret
+}
+
+// WithCloseOnContextDone implements RuntimeConfig.WithCloseOnContextDone
+func (c *runtimeConfig) WithCloseOnContextDone(ensure bool) RuntimeConfig {
+ ret := c.clone()
+ ret.ensureTermination = ensure
+ return ret
+}
+
+// WithMemoryLimitPages implements RuntimeConfig.WithMemoryLimitPages
+func (c *runtimeConfig) WithMemoryLimitPages(memoryLimitPages uint32) RuntimeConfig {
+ ret := c.clone()
+ // This panics instead of returning an error as it is unlikely.
+ if memoryLimitPages > wasm.MemoryLimitPages {
+ panic(fmt.Errorf("memoryLimitPages invalid: %d > %d", memoryLimitPages, wasm.MemoryLimitPages))
+ }
+ ret.memoryLimitPages = memoryLimitPages
+ return ret
+}
+
+// WithCompilationCache implements RuntimeConfig.WithCompilationCache
+func (c *runtimeConfig) WithCompilationCache(ca CompilationCache) RuntimeConfig {
+ ret := c.clone()
+ ret.cache = ca
+ return ret
+}
+
+// WithMemoryCapacityFromMax implements RuntimeConfig.WithMemoryCapacityFromMax
+func (c *runtimeConfig) WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig {
+ ret := c.clone()
+ ret.memoryCapacityFromMax = memoryCapacityFromMax
+ return ret
+}
+
+// WithDebugInfoEnabled implements RuntimeConfig.WithDebugInfoEnabled
+func (c *runtimeConfig) WithDebugInfoEnabled(dwarfEnabled bool) RuntimeConfig {
+ ret := c.clone()
+ ret.dwarfDisabled = !dwarfEnabled
+ return ret
+}
+
+// WithCustomSections implements RuntimeConfig.WithCustomSections
+func (c *runtimeConfig) WithCustomSections(storeCustomSections bool) RuntimeConfig {
+ ret := c.clone()
+ ret.storeCustomSections = storeCustomSections
+ return ret
+}
+
+// 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
+//
+// # Notes
+//
+// - This is an interface for decoupling, not third-party implementations.
+// All implementations are in wazero.
+// - Closing the wazero.Runtime closes any CompiledModule it compiled.
+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
+}
+
+// compile-time check to ensure compiledModule implements CompiledModule
+var _ CompiledModule = &compiledModule{}
+
+type compiledModule struct {
+ module *wasm.Module
+ // compiledEngine holds an engine on which `module` is compiled.
+ compiledEngine wasm.Engine
+ // closeWithModule prevents leaking compiled code when a module is compiled implicitly.
+ closeWithModule bool
+ typeIDs []wasm.FunctionTypeID
+}
+
+// Name implements CompiledModule.Name
+func (c *compiledModule) Name() (moduleName string) {
+ if ns := c.module.NameSection; ns != nil {
+ moduleName = ns.ModuleName
+ }
+ return
+}
+
+// Close implements CompiledModule.Close
+func (c *compiledModule) Close(context.Context) error {
+ c.compiledEngine.DeleteCompiledModule(c.module)
+ // It is possible the underlying may need to return an error later, but in any case this matches api.Module.Close.
+ return nil
+}
+
+// ImportedFunctions implements CompiledModule.ImportedFunctions
+func (c *compiledModule) ImportedFunctions() []api.FunctionDefinition {
+ return c.module.ImportedFunctions()
+}
+
+// ExportedFunctions implements CompiledModule.ExportedFunctions
+func (c *compiledModule) ExportedFunctions() map[string]api.FunctionDefinition {
+ return c.module.ExportedFunctions()
+}
+
+// ImportedMemories implements CompiledModule.ImportedMemories
+func (c *compiledModule) ImportedMemories() []api.MemoryDefinition {
+ return c.module.ImportedMemories()
+}
+
+// ExportedMemories implements CompiledModule.ExportedMemories
+func (c *compiledModule) ExportedMemories() map[string]api.MemoryDefinition {
+ return c.module.ExportedMemories()
+}
+
+// CustomSections implements CompiledModule.CustomSections
+func (c *compiledModule) CustomSections() []api.CustomSection {
+ ret := make([]api.CustomSection, len(c.module.CustomSections))
+ for i, d := range c.module.CustomSections {
+ ret[i] = &customSection{data: d.Data, name: d.Name}
+ }
+ return ret
+}
+
+// customSection implements wasm.CustomSection
+type customSection struct {
+ internalapi.WazeroOnlyType
+ name string
+ data []byte
+}
+
+// Name implements wasm.CustomSection.Name
+func (c *customSection) Name() string {
+ return c.name
+}
+
+// Data implements wasm.CustomSection.Data
+func (c *customSection) Data() []byte {
+ return c.data
+}
+
+// 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).
+//
+// # Notes
+//
+// - This is an interface for decoupling, not third-party implementations.
+// All implementations are in wazero.
+// - ModuleConfig is immutable. Each WithXXX function returns a new instance
+// including the corresponding change.
+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. Empty string ("") clears any name.
+ WithName(string) ModuleConfig
+
+ // WithStartFunctions configures the functions to call after the module is
+ // instantiated. Defaults to "_start".
+ //
+ // Clearing the default is supported, via `WithStartFunctions()`.
+ //
+ // # Notes
+ //
+ // - If a start function doesn't exist, it is skipped. However, any that
+ // do exist are called in order.
+ // - Start functions are not intended to be called multiple times.
+ // Functions that should be called multiple times should be invoked
+ // manually via api.Module's `ExportedFunction` method.
+ // - Start functions commonly exit the module during instantiation,
+ // preventing use of any functions later. This is the case in "wasip1",
+ // which defines the default value "_start".
+ // - See /RATIONALE.md for motivation of this feature.
+ 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. sys.Walltime returns the current unix/epoch time,
+ // seconds since midnight UTC 1 January 1970, with a nanosecond fraction.
+ // This 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()))
+ //
+ // # Notes:
+ // - This does not default to time.Now as that violates sandboxing.
+ // - This is used to implement host functions such as WASI
+ // `clock_time_get` with the `realtime` clock ID.
+ // - 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.
+ // - This is used to implement host functions such as WASI
+ // `clock_time_get` with the `monotonic` clock ID.
+ // - 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 does not default to time.Sleep as that violates sandboxing.
+ // - This is used to implement host functions such as WASI `poll_oneoff`.
+ // - 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
+}
+
+type moduleConfig struct {
+ name string
+ nameSet bool
+ startFunctions []string
+ stdin io.Reader
+ stdout io.Writer
+ stderr io.Writer
+ randSource io.Reader
+ walltime sys.Walltime
+ walltimeResolution sys.ClockResolution
+ nanotime sys.Nanotime
+ nanotimeResolution sys.ClockResolution
+ nanosleep sys.Nanosleep
+ osyield sys.Osyield
+ args [][]byte
+ // environ is pair-indexed to retain order similar to os.Environ.
+ environ [][]byte
+ // environKeys allow overwriting of existing values.
+ environKeys map[string]int
+ // fsConfig is the file system configuration for ABI like WASI.
+ fsConfig FSConfig
+ // sockConfig is the network listener configuration for ABI like WASI.
+ sockConfig *internalsock.Config
+}
+
+// NewModuleConfig returns a ModuleConfig that can be used for configuring module instantiation.
+func NewModuleConfig() ModuleConfig {
+ return &moduleConfig{
+ startFunctions: []string{"_start"},
+ environKeys: map[string]int{},
+ }
+}
+
+// clone makes a deep copy of this module config.
+func (c *moduleConfig) clone() *moduleConfig {
+ ret := *c // copy except maps which share a ref
+ ret.environKeys = make(map[string]int, len(c.environKeys))
+ for key, value := range c.environKeys {
+ ret.environKeys[key] = value
+ }
+ return &ret
+}
+
+// WithArgs implements ModuleConfig.WithArgs
+func (c *moduleConfig) WithArgs(args ...string) ModuleConfig {
+ ret := c.clone()
+ ret.args = toByteSlices(args)
+ return ret
+}
+
+func toByteSlices(strings []string) (result [][]byte) {
+ if len(strings) == 0 {
+ return
+ }
+ result = make([][]byte, len(strings))
+ for i, a := range strings {
+ result[i] = []byte(a)
+ }
+ return
+}
+
+// WithEnv implements ModuleConfig.WithEnv
+func (c *moduleConfig) WithEnv(key, value string) ModuleConfig {
+ ret := c.clone()
+ // Check to see if this key already exists and update it.
+ if i, ok := ret.environKeys[key]; ok {
+ ret.environ[i+1] = []byte(value) // environ is pair-indexed, so the value is 1 after the key.
+ } else {
+ ret.environKeys[key] = len(ret.environ)
+ ret.environ = append(ret.environ, []byte(key), []byte(value))
+ }
+ return ret
+}
+
+// WithFS implements ModuleConfig.WithFS
+func (c *moduleConfig) WithFS(fs fs.FS) ModuleConfig {
+ var config FSConfig
+ if fs != nil {
+ config = NewFSConfig().WithFSMount(fs, "")
+ }
+ return c.WithFSConfig(config)
+}
+
+// WithFSConfig implements ModuleConfig.WithFSConfig
+func (c *moduleConfig) WithFSConfig(config FSConfig) ModuleConfig {
+ ret := c.clone()
+ ret.fsConfig = config
+ return ret
+}
+
+// WithName implements ModuleConfig.WithName
+func (c *moduleConfig) WithName(name string) ModuleConfig {
+ ret := c.clone()
+ ret.nameSet = true
+ ret.name = name
+ return ret
+}
+
+// WithStartFunctions implements ModuleConfig.WithStartFunctions
+func (c *moduleConfig) WithStartFunctions(startFunctions ...string) ModuleConfig {
+ ret := c.clone()
+ ret.startFunctions = startFunctions
+ return ret
+}
+
+// WithStderr implements ModuleConfig.WithStderr
+func (c *moduleConfig) WithStderr(stderr io.Writer) ModuleConfig {
+ ret := c.clone()
+ ret.stderr = stderr
+ return ret
+}
+
+// WithStdin implements ModuleConfig.WithStdin
+func (c *moduleConfig) WithStdin(stdin io.Reader) ModuleConfig {
+ ret := c.clone()
+ ret.stdin = stdin
+ return ret
+}
+
+// WithStdout implements ModuleConfig.WithStdout
+func (c *moduleConfig) WithStdout(stdout io.Writer) ModuleConfig {
+ ret := c.clone()
+ ret.stdout = stdout
+ return ret
+}
+
+// WithWalltime implements ModuleConfig.WithWalltime
+func (c *moduleConfig) WithWalltime(walltime sys.Walltime, resolution sys.ClockResolution) ModuleConfig {
+ ret := c.clone()
+ ret.walltime = walltime
+ ret.walltimeResolution = resolution
+ return ret
+}
+
+// We choose arbitrary resolutions here because there's no perfect alternative. For example, according to the
+// source in time.go, windows monotonic resolution can be 15ms. This chooses arbitrarily 1us for wall time and
+// 1ns for monotonic. See RATIONALE.md for more context.
+
+// WithSysWalltime implements ModuleConfig.WithSysWalltime
+func (c *moduleConfig) WithSysWalltime() ModuleConfig {
+ return c.WithWalltime(platform.Walltime, sys.ClockResolution(time.Microsecond.Nanoseconds()))
+}
+
+// WithNanotime implements ModuleConfig.WithNanotime
+func (c *moduleConfig) WithNanotime(nanotime sys.Nanotime, resolution sys.ClockResolution) ModuleConfig {
+ ret := c.clone()
+ ret.nanotime = nanotime
+ ret.nanotimeResolution = resolution
+ return ret
+}
+
+// WithSysNanotime implements ModuleConfig.WithSysNanotime
+func (c *moduleConfig) WithSysNanotime() ModuleConfig {
+ return c.WithNanotime(platform.Nanotime, sys.ClockResolution(1))
+}
+
+// WithNanosleep implements ModuleConfig.WithNanosleep
+func (c *moduleConfig) WithNanosleep(nanosleep sys.Nanosleep) ModuleConfig {
+ ret := *c // copy
+ ret.nanosleep = nanosleep
+ return &ret
+}
+
+// WithOsyield implements ModuleConfig.WithOsyield
+func (c *moduleConfig) WithOsyield(osyield sys.Osyield) ModuleConfig {
+ ret := *c // copy
+ ret.osyield = osyield
+ return &ret
+}
+
+// WithSysNanosleep implements ModuleConfig.WithSysNanosleep
+func (c *moduleConfig) WithSysNanosleep() ModuleConfig {
+ return c.WithNanosleep(platform.Nanosleep)
+}
+
+// WithRandSource implements ModuleConfig.WithRandSource
+func (c *moduleConfig) WithRandSource(source io.Reader) ModuleConfig {
+ ret := c.clone()
+ ret.randSource = source
+ return ret
+}
+
+// toSysContext creates a baseline wasm.Context configured by ModuleConfig.
+func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) {
+ var environ [][]byte // Intentionally doesn't pre-allocate to reduce logic to default to nil.
+ // Same validation as syscall.Setenv for Linux
+ for i := 0; i < len(c.environ); i += 2 {
+ key, value := c.environ[i], c.environ[i+1]
+ keyLen := len(key)
+ if keyLen == 0 {
+ err = errors.New("environ invalid: empty key")
+ return
+ }
+ valueLen := len(value)
+ result := make([]byte, keyLen+valueLen+1)
+ j := 0
+ for ; j < keyLen; j++ {
+ if k := key[j]; k == '=' { // NUL enforced in NewContext
+ err = errors.New("environ invalid: key contains '=' character")
+ return
+ } else {
+ result[j] = k
+ }
+ }
+ result[j] = '='
+ copy(result[j+1:], value)
+ environ = append(environ, result)
+ }
+
+ var fs []experimentalsys.FS
+ var guestPaths []string
+ if f, ok := c.fsConfig.(*fsConfig); ok {
+ fs, guestPaths = f.preopens()
+ }
+
+ var listeners []*net.TCPListener
+ if n := c.sockConfig; n != nil {
+ if listeners, err = n.BuildTCPListeners(); err != nil {
+ return
+ }
+ }
+
+ return internalsys.NewContext(
+ math.MaxUint32,
+ c.args,
+ environ,
+ c.stdin,
+ c.stdout,
+ c.stderr,
+ c.randSource,
+ c.walltime, c.walltimeResolution,
+ c.nanotime, c.nanotimeResolution,
+ c.nanosleep, c.osyield,
+ fs, guestPaths,
+ listeners,
+ )
+}