github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/config.go (about)

     1  package wazero
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/fs"
     9  	"math"
    10  	"net"
    11  	"time"
    12  
    13  	"github.com/bananabytelabs/wazero/api"
    14  	experimentalsys "github.com/bananabytelabs/wazero/experimental/sys"
    15  	"github.com/bananabytelabs/wazero/internal/engine/compiler"
    16  	"github.com/bananabytelabs/wazero/internal/engine/interpreter"
    17  	"github.com/bananabytelabs/wazero/internal/engine/wazevo"
    18  	"github.com/bananabytelabs/wazero/internal/filecache"
    19  	"github.com/bananabytelabs/wazero/internal/internalapi"
    20  	"github.com/bananabytelabs/wazero/internal/platform"
    21  	internalsock "github.com/bananabytelabs/wazero/internal/sock"
    22  	internalsys "github.com/bananabytelabs/wazero/internal/sys"
    23  	"github.com/bananabytelabs/wazero/internal/wasm"
    24  	"github.com/bananabytelabs/wazero/sys"
    25  )
    26  
    27  // RuntimeConfig controls runtime behavior, with the default implementation as
    28  // NewRuntimeConfig
    29  //
    30  // The example below explicitly limits to Wasm Core 1.0 features as opposed to
    31  // relying on defaults:
    32  //
    33  //	rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(api.CoreFeaturesV1)
    34  //
    35  // # Notes
    36  //
    37  //   - This is an interface for decoupling, not third-party implementations.
    38  //     All implementations are in wazero.
    39  //   - RuntimeConfig is immutable. Each WithXXX function returns a new instance
    40  //     including the corresponding change.
    41  type RuntimeConfig interface {
    42  	// WithCoreFeatures sets the WebAssembly Core specification features this
    43  	// runtime supports. Defaults to api.CoreFeaturesV2.
    44  	//
    45  	// Example of disabling a specific feature:
    46  	//	features := api.CoreFeaturesV2.SetEnabled(api.CoreFeatureMutableGlobal, false)
    47  	//	rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(features)
    48  	//
    49  	// # Why default to version 2.0?
    50  	//
    51  	// Many compilers that target WebAssembly require features after
    52  	// api.CoreFeaturesV1 by default. For example, TinyGo v0.24+ requires
    53  	// api.CoreFeatureBulkMemoryOperations. To avoid runtime errors, wazero
    54  	// defaults to api.CoreFeaturesV2, even though it is not yet a Web
    55  	// Standard (REC).
    56  	WithCoreFeatures(api.CoreFeatures) RuntimeConfig
    57  
    58  	// WithMemoryLimitPages overrides the maximum pages allowed per memory. The
    59  	// default is 65536, allowing 4GB total memory per instance if the maximum is
    60  	// not encoded in a Wasm binary. Setting a value larger than default will panic.
    61  	//
    62  	// This example reduces the largest possible memory size from 4GB to 128KB:
    63  	//	rConfig = wazero.NewRuntimeConfig().WithMemoryLimitPages(2)
    64  	//
    65  	// Note: Wasm has 32-bit memory and each page is 65536 (2^16) bytes. This
    66  	// implies a max of 65536 (2^16) addressable pages.
    67  	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem
    68  	WithMemoryLimitPages(memoryLimitPages uint32) RuntimeConfig
    69  
    70  	// WithMemoryCapacityFromMax eagerly allocates max memory, unless max is
    71  	// not defined. The default is false, which means minimum memory is
    72  	// allocated and any call to grow memory results in re-allocations.
    73  	//
    74  	// This example ensures any memory.grow instruction will never re-allocate:
    75  	//	rConfig = wazero.NewRuntimeConfig().WithMemoryCapacityFromMax(true)
    76  	//
    77  	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem
    78  	//
    79  	// Note: if the memory maximum is not encoded in a Wasm binary, this
    80  	// results in allocating 4GB. See the doc on WithMemoryLimitPages for detail.
    81  	WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig
    82  
    83  	// WithDebugInfoEnabled toggles DWARF based stack traces in the face of
    84  	// runtime errors. Defaults to true.
    85  	//
    86  	// Those who wish to disable this, can like so:
    87  	//
    88  	//	r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfig().WithDebugInfoEnabled(false)
    89  	//
    90  	// When disabled, a stack trace message looks like:
    91  	//
    92  	//	wasm stack trace:
    93  	//		.runtime._panic(i32)
    94  	//		.myFunc()
    95  	//		.main.main()
    96  	//		.runtime.run()
    97  	//		._start()
    98  	//
    99  	// When enabled, the stack trace includes source code information:
   100  	//
   101  	//	wasm stack trace:
   102  	//		.runtime._panic(i32)
   103  	//		  0x16e2: /opt/homebrew/Cellar/tinygo/0.26.0/src/runtime/runtime_tinygowasm.go:73:6
   104  	//		.myFunc()
   105  	//		  0x190b: /Users/XXXXX/wazero/internal/testing/dwarftestdata/testdata/main.go:19:7
   106  	//		.main.main()
   107  	//		  0x18ed: /Users/XXXXX/wazero/internal/testing/dwarftestdata/testdata/main.go:4:3
   108  	//		.runtime.run()
   109  	//		  0x18cc: /opt/homebrew/Cellar/tinygo/0.26.0/src/runtime/scheduler_none.go:26:10
   110  	//		._start()
   111  	//		  0x18b6: /opt/homebrew/Cellar/tinygo/0.26.0/src/runtime/runtime_wasm_wasi.go:22:5
   112  	//
   113  	// Note: This only takes into effect when the original Wasm binary has the
   114  	// DWARF "custom sections" that are often stripped, depending on
   115  	// optimization flags passed to the compiler.
   116  	WithDebugInfoEnabled(bool) RuntimeConfig
   117  
   118  	// WithCompilationCache configures how runtime caches the compiled modules. In the default configuration, compilation results are
   119  	// only in-memory until Runtime.Close is closed, and not shareable by multiple Runtime.
   120  	//
   121  	// Below defines the shared cache across multiple instances of Runtime:
   122  	//
   123  	//	// Creates the new Cache and the runtime configuration with it.
   124  	//	cache := wazero.NewCompilationCache()
   125  	//	defer cache.Close()
   126  	//	config := wazero.NewRuntimeConfig().WithCompilationCache(c)
   127  	//
   128  	//	// Creates two runtimes while sharing compilation caches.
   129  	//	foo := wazero.NewRuntimeWithConfig(context.Background(), config)
   130  	// 	bar := wazero.NewRuntimeWithConfig(context.Background(), config)
   131  	//
   132  	// # Cache Key
   133  	//
   134  	// Cached files are keyed on the version of wazero. This is obtained from go.mod of your application,
   135  	// and we use it to verify the compatibility of caches against the currently-running wazero.
   136  	// However, if you use this in tests of a package not named as `main`, then wazero cannot obtain the correct
   137  	// version of wazero due to the known issue of debug.BuildInfo function: https://github.com/golang/go/issues/33976.
   138  	// As a consequence, your cache won't contain the correct version information and always be treated as `dev` version.
   139  	// To avoid this issue, you can pass -ldflags "-X github.com/bananabytelabs/wazero/internal/version.version=foo" when running tests.
   140  	WithCompilationCache(CompilationCache) RuntimeConfig
   141  
   142  	// WithCustomSections toggles parsing of "custom sections". Defaults to false.
   143  	//
   144  	// When enabled, it is possible to retrieve custom sections from a CompiledModule:
   145  	//
   146  	//	config := wazero.NewRuntimeConfig().WithCustomSections(true)
   147  	//	r := wazero.NewRuntimeWithConfig(ctx, config)
   148  	//	c, err := r.CompileModule(ctx, wasm)
   149  	//	customSections := c.CustomSections()
   150  	WithCustomSections(bool) RuntimeConfig
   151  
   152  	// WithCloseOnContextDone ensures the executions of functions to be closed under one of the following circumstances:
   153  	//
   154  	// 	- context.Context passed to the Call method of api.Function is canceled during execution. (i.e. ctx by context.WithCancel)
   155  	// 	- context.Context passed to the Call method of api.Function reaches timeout during execution. (i.e. ctx by context.WithTimeout or context.WithDeadline)
   156  	// 	- Close or CloseWithExitCode of api.Module is explicitly called during execution.
   157  	//
   158  	// This is especially useful when one wants to run untrusted Wasm binaries since otherwise, any invocation of
   159  	// api.Function can potentially block the corresponding Goroutine forever. Moreover, it might block the
   160  	// entire underlying OS thread which runs the api.Function call. See "Why it's safe to execute runtime-generated
   161  	// machine codes against async Goroutine preemption" section in internal/engine/compiler/RATIONALE.md for detail.
   162  	//
   163  	// Note that this comes with a bit of extra cost when enabled. The reason is that internally this forces
   164  	// interpreter and compiler runtimes to insert the periodical checks on the conditions above. For that reason,
   165  	// this is disabled by default.
   166  	//
   167  	// See examples in context_done_example_test.go for the end-to-end demonstrations.
   168  	//
   169  	// When the invocations of api.Function are closed due to this, sys.ExitError is raised to the callers and
   170  	// the api.Module from which the functions are derived is made closed.
   171  	WithCloseOnContextDone(bool) RuntimeConfig
   172  }
   173  
   174  // NewRuntimeConfig returns a RuntimeConfig using the compiler if it is supported in this environment,
   175  // or the interpreter otherwise.
   176  func NewRuntimeConfig() RuntimeConfig {
   177  	return newRuntimeConfig()
   178  }
   179  
   180  type newEngine func(context.Context, api.CoreFeatures, filecache.Cache) wasm.Engine
   181  
   182  type runtimeConfig struct {
   183  	enabledFeatures       api.CoreFeatures
   184  	memoryLimitPages      uint32
   185  	memoryCapacityFromMax bool
   186  	engineKind            engineKind
   187  	dwarfDisabled         bool // negative as defaults to enabled
   188  	newEngine             newEngine
   189  	cache                 CompilationCache
   190  	storeCustomSections   bool
   191  	ensureTermination     bool
   192  }
   193  
   194  // EnableOptimizingCompiler implements experimental/opt/enabler.EnableOptimizingCompiler.
   195  func (c *runtimeConfig) EnableOptimizingCompiler() {
   196  	c.newEngine = wazevo.NewEngine
   197  }
   198  
   199  // engineLessConfig helps avoid copy/pasting the wrong defaults.
   200  var engineLessConfig = &runtimeConfig{
   201  	enabledFeatures:       api.CoreFeaturesV2,
   202  	memoryLimitPages:      wasm.MemoryLimitPages,
   203  	memoryCapacityFromMax: false,
   204  	dwarfDisabled:         false,
   205  }
   206  
   207  type engineKind int
   208  
   209  const (
   210  	engineKindCompiler engineKind = iota
   211  	engineKindInterpreter
   212  	engineKindCount
   213  )
   214  
   215  // NewRuntimeConfigCompiler compiles WebAssembly modules into
   216  // runtime.GOARCH-specific assembly for optimal performance.
   217  //
   218  // The default implementation is AOT (Ahead of Time) compilation, applied at
   219  // Runtime.CompileModule. This allows consistent runtime performance, as well
   220  // the ability to reduce any first request penalty.
   221  //
   222  // Note: While this is technically AOT, this does not imply any action on your
   223  // part. wazero automatically performs ahead-of-time compilation as needed when
   224  // Runtime.CompileModule is invoked.
   225  //
   226  // Warning: This panics at runtime if the runtime.GOOS or runtime.GOARCH does not
   227  // support Compiler. Use NewRuntimeConfig to safely detect and fallback to
   228  // NewRuntimeConfigInterpreter if needed.
   229  func NewRuntimeConfigCompiler() RuntimeConfig {
   230  	ret := engineLessConfig.clone()
   231  	ret.engineKind = engineKindCompiler
   232  	ret.newEngine = compiler.NewEngine
   233  	return ret
   234  }
   235  
   236  // NewRuntimeConfigInterpreter interprets WebAssembly modules instead of compiling them into assembly.
   237  func NewRuntimeConfigInterpreter() RuntimeConfig {
   238  	ret := engineLessConfig.clone()
   239  	ret.engineKind = engineKindInterpreter
   240  	ret.newEngine = interpreter.NewEngine
   241  	return ret
   242  }
   243  
   244  // clone makes a deep copy of this runtime config.
   245  func (c *runtimeConfig) clone() *runtimeConfig {
   246  	ret := *c // copy except maps which share a ref
   247  	return &ret
   248  }
   249  
   250  // WithCoreFeatures implements RuntimeConfig.WithCoreFeatures
   251  func (c *runtimeConfig) WithCoreFeatures(features api.CoreFeatures) RuntimeConfig {
   252  	ret := c.clone()
   253  	ret.enabledFeatures = features
   254  	return ret
   255  }
   256  
   257  // WithCloseOnContextDone implements RuntimeConfig.WithCloseOnContextDone
   258  func (c *runtimeConfig) WithCloseOnContextDone(ensure bool) RuntimeConfig {
   259  	ret := c.clone()
   260  	ret.ensureTermination = ensure
   261  	return ret
   262  }
   263  
   264  // WithMemoryLimitPages implements RuntimeConfig.WithMemoryLimitPages
   265  func (c *runtimeConfig) WithMemoryLimitPages(memoryLimitPages uint32) RuntimeConfig {
   266  	ret := c.clone()
   267  	// This panics instead of returning an error as it is unlikely.
   268  	if memoryLimitPages > wasm.MemoryLimitPages {
   269  		panic(fmt.Errorf("memoryLimitPages invalid: %d > %d", memoryLimitPages, wasm.MemoryLimitPages))
   270  	}
   271  	ret.memoryLimitPages = memoryLimitPages
   272  	return ret
   273  }
   274  
   275  // WithCompilationCache implements RuntimeConfig.WithCompilationCache
   276  func (c *runtimeConfig) WithCompilationCache(ca CompilationCache) RuntimeConfig {
   277  	ret := c.clone()
   278  	ret.cache = ca
   279  	return ret
   280  }
   281  
   282  // WithMemoryCapacityFromMax implements RuntimeConfig.WithMemoryCapacityFromMax
   283  func (c *runtimeConfig) WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig {
   284  	ret := c.clone()
   285  	ret.memoryCapacityFromMax = memoryCapacityFromMax
   286  	return ret
   287  }
   288  
   289  // WithDebugInfoEnabled implements RuntimeConfig.WithDebugInfoEnabled
   290  func (c *runtimeConfig) WithDebugInfoEnabled(dwarfEnabled bool) RuntimeConfig {
   291  	ret := c.clone()
   292  	ret.dwarfDisabled = !dwarfEnabled
   293  	return ret
   294  }
   295  
   296  // WithCustomSections implements RuntimeConfig.WithCustomSections
   297  func (c *runtimeConfig) WithCustomSections(storeCustomSections bool) RuntimeConfig {
   298  	ret := c.clone()
   299  	ret.storeCustomSections = storeCustomSections
   300  	return ret
   301  }
   302  
   303  // CompiledModule is a WebAssembly module ready to be instantiated (Runtime.InstantiateModule) as an api.Module.
   304  //
   305  // In WebAssembly terminology, this is a decoded, validated, and possibly also compiled module. wazero avoids using
   306  // the name "Module" for both before and after instantiation as the name conflation has caused confusion.
   307  // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#semantic-phases%E2%91%A0
   308  //
   309  // # Notes
   310  //
   311  //   - This is an interface for decoupling, not third-party implementations.
   312  //     All implementations are in wazero.
   313  //   - Closing the wazero.Runtime closes any CompiledModule it compiled.
   314  type CompiledModule interface {
   315  	// Name returns the module name encoded into the binary or empty if not.
   316  	Name() string
   317  
   318  	// ImportedFunctions returns all the imported functions
   319  	// (api.FunctionDefinition) in this module or nil if there are none.
   320  	//
   321  	// Note: Unlike ExportedFunctions, there is no unique constraint on
   322  	// imports.
   323  	ImportedFunctions() []api.FunctionDefinition
   324  
   325  	// ExportedFunctions returns all the exported functions
   326  	// (api.FunctionDefinition) in this module keyed on export name.
   327  	ExportedFunctions() map[string]api.FunctionDefinition
   328  
   329  	// ImportedMemories returns all the imported memories
   330  	// (api.MemoryDefinition) in this module or nil if there are none.
   331  	//
   332  	// ## Notes
   333  	//   - As of WebAssembly Core Specification 2.0, there can be at most one
   334  	//     memory.
   335  	//   - Unlike ExportedMemories, there is no unique constraint on imports.
   336  	ImportedMemories() []api.MemoryDefinition
   337  
   338  	// ExportedMemories returns all the exported memories
   339  	// (api.MemoryDefinition) in this module keyed on export name.
   340  	//
   341  	// Note: As of WebAssembly Core Specification 2.0, there can be at most one
   342  	// memory.
   343  	ExportedMemories() map[string]api.MemoryDefinition
   344  
   345  	// CustomSections returns all the custom sections
   346  	// (api.CustomSection) in this module keyed on the section name.
   347  	CustomSections() []api.CustomSection
   348  
   349  	// Close releases all the allocated resources for this CompiledModule.
   350  	//
   351  	// Note: It is safe to call Close while having outstanding calls from an
   352  	// api.Module instantiated from this.
   353  	Close(context.Context) error
   354  }
   355  
   356  // compile-time check to ensure compiledModule implements CompiledModule
   357  var _ CompiledModule = &compiledModule{}
   358  
   359  type compiledModule struct {
   360  	module *wasm.Module
   361  	// compiledEngine holds an engine on which `module` is compiled.
   362  	compiledEngine wasm.Engine
   363  	// closeWithModule prevents leaking compiled code when a module is compiled implicitly.
   364  	closeWithModule bool
   365  	typeIDs         []wasm.FunctionTypeID
   366  }
   367  
   368  // Name implements CompiledModule.Name
   369  func (c *compiledModule) Name() (moduleName string) {
   370  	if ns := c.module.NameSection; ns != nil {
   371  		moduleName = ns.ModuleName
   372  	}
   373  	return
   374  }
   375  
   376  // Close implements CompiledModule.Close
   377  func (c *compiledModule) Close(context.Context) error {
   378  	c.compiledEngine.DeleteCompiledModule(c.module)
   379  	// It is possible the underlying may need to return an error later, but in any case this matches api.Module.Close.
   380  	return nil
   381  }
   382  
   383  // ImportedFunctions implements CompiledModule.ImportedFunctions
   384  func (c *compiledModule) ImportedFunctions() []api.FunctionDefinition {
   385  	return c.module.ImportedFunctions()
   386  }
   387  
   388  // ExportedFunctions implements CompiledModule.ExportedFunctions
   389  func (c *compiledModule) ExportedFunctions() map[string]api.FunctionDefinition {
   390  	return c.module.ExportedFunctions()
   391  }
   392  
   393  // ImportedMemories implements CompiledModule.ImportedMemories
   394  func (c *compiledModule) ImportedMemories() []api.MemoryDefinition {
   395  	return c.module.ImportedMemories()
   396  }
   397  
   398  // ExportedMemories implements CompiledModule.ExportedMemories
   399  func (c *compiledModule) ExportedMemories() map[string]api.MemoryDefinition {
   400  	return c.module.ExportedMemories()
   401  }
   402  
   403  // CustomSections implements CompiledModule.CustomSections
   404  func (c *compiledModule) CustomSections() []api.CustomSection {
   405  	ret := make([]api.CustomSection, len(c.module.CustomSections))
   406  	for i, d := range c.module.CustomSections {
   407  		ret[i] = &customSection{data: d.Data, name: d.Name}
   408  	}
   409  	return ret
   410  }
   411  
   412  // customSection implements wasm.CustomSection
   413  type customSection struct {
   414  	internalapi.WazeroOnlyType
   415  	name string
   416  	data []byte
   417  }
   418  
   419  // Name implements wasm.CustomSection.Name
   420  func (c *customSection) Name() string {
   421  	return c.name
   422  }
   423  
   424  // Data implements wasm.CustomSection.Data
   425  func (c *customSection) Data() []byte {
   426  	return c.data
   427  }
   428  
   429  // ModuleConfig configures resources needed by functions that have low-level interactions with the host operating
   430  // system. Using this, resources such as STDIN can be isolated, so that the same module can be safely instantiated
   431  // multiple times.
   432  //
   433  // Here's an example:
   434  //
   435  //	// Initialize base configuration:
   436  //	config := wazero.NewModuleConfig().WithStdout(buf).WithSysNanotime()
   437  //
   438  //	// Assign different configuration on each instantiation
   439  //	mod, _ := r.InstantiateModule(ctx, compiled, config.WithName("rotate").WithArgs("rotate", "angle=90", "dir=cw"))
   440  //
   441  // While wazero supports Windows as a platform, host functions using ModuleConfig follow a UNIX dialect.
   442  // See RATIONALE.md for design background and relationship to WebAssembly System Interfaces (WASI).
   443  //
   444  // # Notes
   445  //
   446  //   - This is an interface for decoupling, not third-party implementations.
   447  //     All implementations are in wazero.
   448  //   - ModuleConfig is immutable. Each WithXXX function returns a new instance
   449  //     including the corresponding change.
   450  type ModuleConfig interface {
   451  	// WithArgs assigns command-line arguments visible to an imported function that reads an arg vector (argv). Defaults to
   452  	// none. Runtime.InstantiateModule errs if any arg is empty.
   453  	//
   454  	// These values are commonly read by the functions like "args_get" in "wasi_snapshot_preview1" although they could be
   455  	// read by functions imported from other modules.
   456  	//
   457  	// Similar to os.Args and exec.Cmd Env, many implementations would expect a program name to be argv[0]. However, neither
   458  	// WebAssembly nor WebAssembly System Interfaces (WASI) define this. Regardless, you may choose to set the first
   459  	// argument to the same value set via WithName.
   460  	//
   461  	// Note: This does not default to os.Args as that violates sandboxing.
   462  	//
   463  	// See https://linux.die.net/man/3/argv and https://en.wikipedia.org/wiki/Null-terminated_string
   464  	WithArgs(...string) ModuleConfig
   465  
   466  	// WithEnv sets an environment variable visible to a Module that imports functions. Defaults to none.
   467  	// Runtime.InstantiateModule errs if the key is empty or contains a NULL(0) or equals("") character.
   468  	//
   469  	// Validation is the same as os.Setenv on Linux and replaces any existing value. Unlike exec.Cmd Env, this does not
   470  	// default to the current process environment as that would violate sandboxing. This also does not preserve order.
   471  	//
   472  	// Environment variables are commonly read by the functions like "environ_get" in "wasi_snapshot_preview1" although
   473  	// they could be read by functions imported from other modules.
   474  	//
   475  	// While similar to process configuration, there are no assumptions that can be made about anything OS-specific. For
   476  	// example, neither WebAssembly nor WebAssembly System Interfaces (WASI) define concerns processes have, such as
   477  	// case-sensitivity on environment keys. For portability, define entries with case-insensitively unique keys.
   478  	//
   479  	// See https://linux.die.net/man/3/environ and https://en.wikipedia.org/wiki/Null-terminated_string
   480  	WithEnv(key, value string) ModuleConfig
   481  
   482  	// WithFS is a convenience that calls WithFSConfig with an FSConfig of the
   483  	// input for the root ("/") guest path.
   484  	WithFS(fs.FS) ModuleConfig
   485  
   486  	// WithFSConfig configures the filesystem available to each guest
   487  	// instantiated with this configuration. By default, no file access is
   488  	// allowed, so functions like `path_open` result in unsupported errors
   489  	// (e.g. syscall.ENOSYS).
   490  	WithFSConfig(FSConfig) ModuleConfig
   491  
   492  	// WithName configures the module name. Defaults to what was decoded from
   493  	// the name section. Empty string ("") clears any name.
   494  	WithName(string) ModuleConfig
   495  
   496  	// WithStartFunctions configures the functions to call after the module is
   497  	// instantiated. Defaults to "_start".
   498  	//
   499  	// Clearing the default is supported, via `WithStartFunctions()`.
   500  	//
   501  	// # Notes
   502  	//
   503  	//   - If a start function doesn't exist, it is skipped. However, any that
   504  	//     do exist are called in order.
   505  	//   - Start functions are not intended to be called multiple times.
   506  	//     Functions that should be called multiple times should be invoked
   507  	//     manually via api.Module's `ExportedFunction` method.
   508  	//   - Start functions commonly exit the module during instantiation,
   509  	//     preventing use of any functions later. This is the case in "wasip1",
   510  	//     which defines the default value "_start".
   511  	//   - See /RATIONALE.md for motivation of this feature.
   512  	WithStartFunctions(...string) ModuleConfig
   513  
   514  	// WithStderr configures where standard error (file descriptor 2) is written. Defaults to io.Discard.
   515  	//
   516  	// This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could
   517  	// be used by functions imported from other modules.
   518  	//
   519  	// # Notes
   520  	//
   521  	//   - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close.
   522  	//   - This does not default to os.Stderr as that both violates sandboxing and prevents concurrent modules.
   523  	//
   524  	// See https://linux.die.net/man/3/stderr
   525  	WithStderr(io.Writer) ModuleConfig
   526  
   527  	// WithStdin configures where standard input (file descriptor 0) is read. Defaults to return io.EOF.
   528  	//
   529  	// This reader is most commonly used by the functions like "fd_read" in "wasi_snapshot_preview1" although it could
   530  	// be used by functions imported from other modules.
   531  	//
   532  	// # Notes
   533  	//
   534  	//   - The caller is responsible to close any io.Reader they supply: It is not closed on api.Module Close.
   535  	//   - This does not default to os.Stdin as that both violates sandboxing and prevents concurrent modules.
   536  	//
   537  	// See https://linux.die.net/man/3/stdin
   538  	WithStdin(io.Reader) ModuleConfig
   539  
   540  	// WithStdout configures where standard output (file descriptor 1) is written. Defaults to io.Discard.
   541  	//
   542  	// This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could
   543  	// be used by functions imported from other modules.
   544  	//
   545  	// # Notes
   546  	//
   547  	//   - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close.
   548  	//   - This does not default to os.Stdout as that both violates sandboxing and prevents concurrent modules.
   549  	//
   550  	// See https://linux.die.net/man/3/stdout
   551  	WithStdout(io.Writer) ModuleConfig
   552  
   553  	// WithWalltime configures the wall clock, sometimes referred to as the
   554  	// real time clock. sys.Walltime returns the current unix/epoch time,
   555  	// seconds since midnight UTC 1 January 1970, with a nanosecond fraction.
   556  	// This defaults to a fake result that increases by 1ms on each reading.
   557  	//
   558  	// Here's an example that uses a custom clock:
   559  	//	moduleConfig = moduleConfig.
   560  	//		WithWalltime(func(context.Context) (sec int64, nsec int32) {
   561  	//			return clock.walltime()
   562  	//		}, sys.ClockResolution(time.Microsecond.Nanoseconds()))
   563  	//
   564  	// # Notes:
   565  	//   - This does not default to time.Now as that violates sandboxing.
   566  	//   - This is used to implement host functions such as WASI
   567  	//     `clock_time_get` with the `realtime` clock ID.
   568  	//   - Use WithSysWalltime for a usable implementation.
   569  	WithWalltime(sys.Walltime, sys.ClockResolution) ModuleConfig
   570  
   571  	// WithSysWalltime uses time.Now for sys.Walltime with a resolution of 1us
   572  	// (1000ns).
   573  	//
   574  	// See WithWalltime
   575  	WithSysWalltime() ModuleConfig
   576  
   577  	// WithNanotime configures the monotonic clock, used to measure elapsed
   578  	// time in nanoseconds. Defaults to a fake result that increases by 1ms
   579  	// on each reading.
   580  	//
   581  	// Here's an example that uses a custom clock:
   582  	//	moduleConfig = moduleConfig.
   583  	//		WithNanotime(func(context.Context) int64 {
   584  	//			return clock.nanotime()
   585  	//		}, sys.ClockResolution(time.Microsecond.Nanoseconds()))
   586  	//
   587  	// # Notes:
   588  	//   - This does not default to time.Since as that violates sandboxing.
   589  	//   - This is used to implement host functions such as WASI
   590  	//     `clock_time_get` with the `monotonic` clock ID.
   591  	//   - Some compilers implement sleep by looping on sys.Nanotime (e.g. Go).
   592  	//   - If you set this, you should probably set WithNanosleep also.
   593  	//   - Use WithSysNanotime for a usable implementation.
   594  	WithNanotime(sys.Nanotime, sys.ClockResolution) ModuleConfig
   595  
   596  	// WithSysNanotime uses time.Now for sys.Nanotime with a resolution of 1us.
   597  	//
   598  	// See WithNanotime
   599  	WithSysNanotime() ModuleConfig
   600  
   601  	// WithNanosleep configures the how to pause the current goroutine for at
   602  	// least the configured nanoseconds. Defaults to return immediately.
   603  	//
   604  	// This example uses a custom sleep function:
   605  	//	moduleConfig = moduleConfig.
   606  	//		WithNanosleep(func(ns int64) {
   607  	//			rel := unix.NsecToTimespec(ns)
   608  	//			remain := unix.Timespec{}
   609  	//			for { // loop until no more time remaining
   610  	//				err := unix.ClockNanosleep(unix.CLOCK_MONOTONIC, 0, &rel, &remain)
   611  	//			--snip--
   612  	//
   613  	// # Notes:
   614  	//   - This does not default to time.Sleep as that violates sandboxing.
   615  	//   - This is used to implement host functions such as WASI `poll_oneoff`.
   616  	//   - Some compilers implement sleep by looping on sys.Nanotime (e.g. Go).
   617  	//   - If you set this, you should probably set WithNanotime also.
   618  	//   - Use WithSysNanosleep for a usable implementation.
   619  	WithNanosleep(sys.Nanosleep) ModuleConfig
   620  
   621  	// WithOsyield yields the processor, typically to implement spin-wait
   622  	// loops. Defaults to return immediately.
   623  	//
   624  	// # Notes:
   625  	//   - This primarily supports `sched_yield` in WASI
   626  	//   - This does not default to runtime.osyield as that violates sandboxing.
   627  	WithOsyield(sys.Osyield) ModuleConfig
   628  
   629  	// WithSysNanosleep uses time.Sleep for sys.Nanosleep.
   630  	//
   631  	// See WithNanosleep
   632  	WithSysNanosleep() ModuleConfig
   633  
   634  	// WithRandSource configures a source of random bytes. Defaults to return a
   635  	// deterministic source. You might override this with crypto/rand.Reader
   636  	//
   637  	// This reader is most commonly used by the functions like "random_get" in
   638  	// "wasi_snapshot_preview1", "seed" in AssemblyScript standard "env", and
   639  	// "getRandomData" when runtime.GOOS is "js".
   640  	//
   641  	// Note: The caller is responsible to close any io.Reader they supply: It
   642  	// is not closed on api.Module Close.
   643  	WithRandSource(io.Reader) ModuleConfig
   644  }
   645  
   646  type moduleConfig struct {
   647  	name               string
   648  	nameSet            bool
   649  	startFunctions     []string
   650  	stdin              io.Reader
   651  	stdout             io.Writer
   652  	stderr             io.Writer
   653  	randSource         io.Reader
   654  	walltime           sys.Walltime
   655  	walltimeResolution sys.ClockResolution
   656  	nanotime           sys.Nanotime
   657  	nanotimeResolution sys.ClockResolution
   658  	nanosleep          sys.Nanosleep
   659  	osyield            sys.Osyield
   660  	args               [][]byte
   661  	// environ is pair-indexed to retain order similar to os.Environ.
   662  	environ [][]byte
   663  	// environKeys allow overwriting of existing values.
   664  	environKeys map[string]int
   665  	// fsConfig is the file system configuration for ABI like WASI.
   666  	fsConfig FSConfig
   667  	// sockConfig is the network listener configuration for ABI like WASI.
   668  	sockConfig *internalsock.Config
   669  }
   670  
   671  // NewModuleConfig returns a ModuleConfig that can be used for configuring module instantiation.
   672  func NewModuleConfig() ModuleConfig {
   673  	return &moduleConfig{
   674  		startFunctions: []string{"_start"},
   675  		environKeys:    map[string]int{},
   676  	}
   677  }
   678  
   679  // clone makes a deep copy of this module config.
   680  func (c *moduleConfig) clone() *moduleConfig {
   681  	ret := *c // copy except maps which share a ref
   682  	ret.environKeys = make(map[string]int, len(c.environKeys))
   683  	for key, value := range c.environKeys {
   684  		ret.environKeys[key] = value
   685  	}
   686  	return &ret
   687  }
   688  
   689  // WithArgs implements ModuleConfig.WithArgs
   690  func (c *moduleConfig) WithArgs(args ...string) ModuleConfig {
   691  	ret := c.clone()
   692  	ret.args = toByteSlices(args)
   693  	return ret
   694  }
   695  
   696  func toByteSlices(strings []string) (result [][]byte) {
   697  	if len(strings) == 0 {
   698  		return
   699  	}
   700  	result = make([][]byte, len(strings))
   701  	for i, a := range strings {
   702  		result[i] = []byte(a)
   703  	}
   704  	return
   705  }
   706  
   707  // WithEnv implements ModuleConfig.WithEnv
   708  func (c *moduleConfig) WithEnv(key, value string) ModuleConfig {
   709  	ret := c.clone()
   710  	// Check to see if this key already exists and update it.
   711  	if i, ok := ret.environKeys[key]; ok {
   712  		ret.environ[i+1] = []byte(value) // environ is pair-indexed, so the value is 1 after the key.
   713  	} else {
   714  		ret.environKeys[key] = len(ret.environ)
   715  		ret.environ = append(ret.environ, []byte(key), []byte(value))
   716  	}
   717  	return ret
   718  }
   719  
   720  // WithFS implements ModuleConfig.WithFS
   721  func (c *moduleConfig) WithFS(fs fs.FS) ModuleConfig {
   722  	var config FSConfig
   723  	if fs != nil {
   724  		config = NewFSConfig().WithFSMount(fs, "")
   725  	}
   726  	return c.WithFSConfig(config)
   727  }
   728  
   729  // WithFSConfig implements ModuleConfig.WithFSConfig
   730  func (c *moduleConfig) WithFSConfig(config FSConfig) ModuleConfig {
   731  	ret := c.clone()
   732  	ret.fsConfig = config
   733  	return ret
   734  }
   735  
   736  // WithName implements ModuleConfig.WithName
   737  func (c *moduleConfig) WithName(name string) ModuleConfig {
   738  	ret := c.clone()
   739  	ret.nameSet = true
   740  	ret.name = name
   741  	return ret
   742  }
   743  
   744  // WithStartFunctions implements ModuleConfig.WithStartFunctions
   745  func (c *moduleConfig) WithStartFunctions(startFunctions ...string) ModuleConfig {
   746  	ret := c.clone()
   747  	ret.startFunctions = startFunctions
   748  	return ret
   749  }
   750  
   751  // WithStderr implements ModuleConfig.WithStderr
   752  func (c *moduleConfig) WithStderr(stderr io.Writer) ModuleConfig {
   753  	ret := c.clone()
   754  	ret.stderr = stderr
   755  	return ret
   756  }
   757  
   758  // WithStdin implements ModuleConfig.WithStdin
   759  func (c *moduleConfig) WithStdin(stdin io.Reader) ModuleConfig {
   760  	ret := c.clone()
   761  	ret.stdin = stdin
   762  	return ret
   763  }
   764  
   765  // WithStdout implements ModuleConfig.WithStdout
   766  func (c *moduleConfig) WithStdout(stdout io.Writer) ModuleConfig {
   767  	ret := c.clone()
   768  	ret.stdout = stdout
   769  	return ret
   770  }
   771  
   772  // WithWalltime implements ModuleConfig.WithWalltime
   773  func (c *moduleConfig) WithWalltime(walltime sys.Walltime, resolution sys.ClockResolution) ModuleConfig {
   774  	ret := c.clone()
   775  	ret.walltime = walltime
   776  	ret.walltimeResolution = resolution
   777  	return ret
   778  }
   779  
   780  // We choose arbitrary resolutions here because there's no perfect alternative. For example, according to the
   781  // source in time.go, windows monotonic resolution can be 15ms. This chooses arbitrarily 1us for wall time and
   782  // 1ns for monotonic. See RATIONALE.md for more context.
   783  
   784  // WithSysWalltime implements ModuleConfig.WithSysWalltime
   785  func (c *moduleConfig) WithSysWalltime() ModuleConfig {
   786  	return c.WithWalltime(platform.Walltime, sys.ClockResolution(time.Microsecond.Nanoseconds()))
   787  }
   788  
   789  // WithNanotime implements ModuleConfig.WithNanotime
   790  func (c *moduleConfig) WithNanotime(nanotime sys.Nanotime, resolution sys.ClockResolution) ModuleConfig {
   791  	ret := c.clone()
   792  	ret.nanotime = nanotime
   793  	ret.nanotimeResolution = resolution
   794  	return ret
   795  }
   796  
   797  // WithSysNanotime implements ModuleConfig.WithSysNanotime
   798  func (c *moduleConfig) WithSysNanotime() ModuleConfig {
   799  	return c.WithNanotime(platform.Nanotime, sys.ClockResolution(1))
   800  }
   801  
   802  // WithNanosleep implements ModuleConfig.WithNanosleep
   803  func (c *moduleConfig) WithNanosleep(nanosleep sys.Nanosleep) ModuleConfig {
   804  	ret := *c // copy
   805  	ret.nanosleep = nanosleep
   806  	return &ret
   807  }
   808  
   809  // WithOsyield implements ModuleConfig.WithOsyield
   810  func (c *moduleConfig) WithOsyield(osyield sys.Osyield) ModuleConfig {
   811  	ret := *c // copy
   812  	ret.osyield = osyield
   813  	return &ret
   814  }
   815  
   816  // WithSysNanosleep implements ModuleConfig.WithSysNanosleep
   817  func (c *moduleConfig) WithSysNanosleep() ModuleConfig {
   818  	return c.WithNanosleep(platform.Nanosleep)
   819  }
   820  
   821  // WithRandSource implements ModuleConfig.WithRandSource
   822  func (c *moduleConfig) WithRandSource(source io.Reader) ModuleConfig {
   823  	ret := c.clone()
   824  	ret.randSource = source
   825  	return ret
   826  }
   827  
   828  // toSysContext creates a baseline wasm.Context configured by ModuleConfig.
   829  func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) {
   830  	var environ [][]byte // Intentionally doesn't pre-allocate to reduce logic to default to nil.
   831  	// Same validation as syscall.Setenv for Linux
   832  	for i := 0; i < len(c.environ); i += 2 {
   833  		key, value := c.environ[i], c.environ[i+1]
   834  		keyLen := len(key)
   835  		if keyLen == 0 {
   836  			err = errors.New("environ invalid: empty key")
   837  			return
   838  		}
   839  		valueLen := len(value)
   840  		result := make([]byte, keyLen+valueLen+1)
   841  		j := 0
   842  		for ; j < keyLen; j++ {
   843  			if k := key[j]; k == '=' { // NUL enforced in NewContext
   844  				err = errors.New("environ invalid: key contains '=' character")
   845  				return
   846  			} else {
   847  				result[j] = k
   848  			}
   849  		}
   850  		result[j] = '='
   851  		copy(result[j+1:], value)
   852  		environ = append(environ, result)
   853  	}
   854  
   855  	var fs []experimentalsys.FS
   856  	var guestPaths []string
   857  	if f, ok := c.fsConfig.(*fsConfig); ok {
   858  		fs, guestPaths = f.preopens()
   859  	}
   860  
   861  	var listeners []*net.TCPListener
   862  	if n := c.sockConfig; n != nil {
   863  		if listeners, err = n.BuildTCPListeners(); err != nil {
   864  			return
   865  		}
   866  	}
   867  
   868  	return internalsys.NewContext(
   869  		math.MaxUint32,
   870  		c.args,
   871  		environ,
   872  		c.stdin,
   873  		c.stdout,
   874  		c.stderr,
   875  		c.randSource,
   876  		c.walltime, c.walltimeResolution,
   877  		c.nanotime, c.nanotimeResolution,
   878  		c.nanosleep, c.osyield,
   879  		fs, guestPaths,
   880  		listeners,
   881  	)
   882  }