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