github.com/tetratelabs/wazero@v1.2.1/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  	"github.com/tetratelabs/wazero/internal/engine/compiler"
    15  	"github.com/tetratelabs/wazero/internal/engine/interpreter"
    16  	"github.com/tetratelabs/wazero/internal/filecache"
    17  	"github.com/tetratelabs/wazero/internal/fsapi"
    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 internal/engine/compiler/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 = compiler.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  	// # Notes
   494  	//
   495  	//   - If any function doesn't exist, it is skipped. However, all functions
   496  	//	  that do exist are called in order.
   497  	//   - Some start functions may exit the module during instantiate with a
   498  	//	  sys.ExitError (e.g. emscripten), preventing use of exported functions.
   499  	WithStartFunctions(...string) ModuleConfig
   500  
   501  	// WithStderr configures where standard error (file descriptor 2) is written. Defaults to io.Discard.
   502  	//
   503  	// This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could
   504  	// be used by functions imported from other modules.
   505  	//
   506  	// # Notes
   507  	//
   508  	//   - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close.
   509  	//   - This does not default to os.Stderr as that both violates sandboxing and prevents concurrent modules.
   510  	//
   511  	// See https://linux.die.net/man/3/stderr
   512  	WithStderr(io.Writer) ModuleConfig
   513  
   514  	// WithStdin configures where standard input (file descriptor 0) is read. Defaults to return io.EOF.
   515  	//
   516  	// This reader is most commonly used by the functions like "fd_read" 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.Reader they supply: It is not closed on api.Module Close.
   522  	//   - This does not default to os.Stdin as that both violates sandboxing and prevents concurrent modules.
   523  	//
   524  	// See https://linux.die.net/man/3/stdin
   525  	WithStdin(io.Reader) ModuleConfig
   526  
   527  	// WithStdout configures where standard output (file descriptor 1) is written. Defaults to io.Discard.
   528  	//
   529  	// This writer is most commonly used by the functions like "fd_write" 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.Writer they supply: It is not closed on api.Module Close.
   535  	//   - This does not default to os.Stdout as that both violates sandboxing and prevents concurrent modules.
   536  	//
   537  	// See https://linux.die.net/man/3/stdout
   538  	WithStdout(io.Writer) ModuleConfig
   539  
   540  	// WithWalltime configures the wall clock, sometimes referred to as the
   541  	// real time clock. sys.Walltime returns the current unix/epoch time,
   542  	// seconds since midnight UTC 1 January 1970, with a nanosecond fraction.
   543  	// This defaults to a fake result that increases by 1ms on each reading.
   544  	//
   545  	// Here's an example that uses a custom clock:
   546  	//	moduleConfig = moduleConfig.
   547  	//		WithWalltime(func(context.Context) (sec int64, nsec int32) {
   548  	//			return clock.walltime()
   549  	//		}, sys.ClockResolution(time.Microsecond.Nanoseconds()))
   550  	//
   551  	// # Notes:
   552  	//   - This does not default to time.Now as that violates sandboxing.
   553  	//   - This is used to implement host functions such as WASI
   554  	//     `clock_time_get` with the `realtime` clock ID.
   555  	//   - Use WithSysWalltime for a usable implementation.
   556  	WithWalltime(sys.Walltime, sys.ClockResolution) ModuleConfig
   557  
   558  	// WithSysWalltime uses time.Now for sys.Walltime with a resolution of 1us
   559  	// (1000ns).
   560  	//
   561  	// See WithWalltime
   562  	WithSysWalltime() ModuleConfig
   563  
   564  	// WithNanotime configures the monotonic clock, used to measure elapsed
   565  	// time in nanoseconds. Defaults to a fake result that increases by 1ms
   566  	// on each reading.
   567  	//
   568  	// Here's an example that uses a custom clock:
   569  	//	moduleConfig = moduleConfig.
   570  	//		WithNanotime(func(context.Context) int64 {
   571  	//			return clock.nanotime()
   572  	//		}, sys.ClockResolution(time.Microsecond.Nanoseconds()))
   573  	//
   574  	// # Notes:
   575  	//   - This does not default to time.Since as that violates sandboxing.
   576  	//   - This is used to implement host functions such as WASI
   577  	//     `clock_time_get` with the `monotonic` clock ID.
   578  	//   - Some compilers implement sleep by looping on sys.Nanotime (e.g. Go).
   579  	//   - If you set this, you should probably set WithNanosleep also.
   580  	//   - Use WithSysNanotime for a usable implementation.
   581  	WithNanotime(sys.Nanotime, sys.ClockResolution) ModuleConfig
   582  
   583  	// WithSysNanotime uses time.Now for sys.Nanotime with a resolution of 1us.
   584  	//
   585  	// See WithNanotime
   586  	WithSysNanotime() ModuleConfig
   587  
   588  	// WithNanosleep configures the how to pause the current goroutine for at
   589  	// least the configured nanoseconds. Defaults to return immediately.
   590  	//
   591  	// This example uses a custom sleep function:
   592  	//	moduleConfig = moduleConfig.
   593  	//		WithNanosleep(func(ns int64) {
   594  	//			rel := unix.NsecToTimespec(ns)
   595  	//			remain := unix.Timespec{}
   596  	//			for { // loop until no more time remaining
   597  	//				err := unix.ClockNanosleep(unix.CLOCK_MONOTONIC, 0, &rel, &remain)
   598  	//			--snip--
   599  	//
   600  	// # Notes:
   601  	//   - This does not default to time.Sleep as that violates sandboxing.
   602  	//   - This is used to implement host functions such as WASI `poll_oneoff`.
   603  	//   - Some compilers implement sleep by looping on sys.Nanotime (e.g. Go).
   604  	//   - If you set this, you should probably set WithNanotime also.
   605  	//   - Use WithSysNanosleep for a usable implementation.
   606  	WithNanosleep(sys.Nanosleep) ModuleConfig
   607  
   608  	// WithOsyield yields the processor, typically to implement spin-wait
   609  	// loops. Defaults to return immediately.
   610  	//
   611  	// # Notes:
   612  	//   - This primarily supports `sched_yield` in WASI
   613  	//   - This does not default to runtime.osyield as that violates sandboxing.
   614  	WithOsyield(sys.Osyield) ModuleConfig
   615  
   616  	// WithSysNanosleep uses time.Sleep for sys.Nanosleep.
   617  	//
   618  	// See WithNanosleep
   619  	WithSysNanosleep() ModuleConfig
   620  
   621  	// WithRandSource configures a source of random bytes. Defaults to return a
   622  	// deterministic source. You might override this with crypto/rand.Reader
   623  	//
   624  	// This reader is most commonly used by the functions like "random_get" in
   625  	// "wasi_snapshot_preview1", "seed" in AssemblyScript standard "env", and
   626  	// "getRandomData" when runtime.GOOS is "js".
   627  	//
   628  	// Note: The caller is responsible to close any io.Reader they supply: It
   629  	// is not closed on api.Module Close.
   630  	WithRandSource(io.Reader) ModuleConfig
   631  }
   632  
   633  type moduleConfig struct {
   634  	name               string
   635  	nameSet            bool
   636  	startFunctions     []string
   637  	stdin              io.Reader
   638  	stdout             io.Writer
   639  	stderr             io.Writer
   640  	randSource         io.Reader
   641  	walltime           sys.Walltime
   642  	walltimeResolution sys.ClockResolution
   643  	nanotime           sys.Nanotime
   644  	nanotimeResolution sys.ClockResolution
   645  	nanosleep          sys.Nanosleep
   646  	osyield            sys.Osyield
   647  	args               [][]byte
   648  	// environ is pair-indexed to retain order similar to os.Environ.
   649  	environ [][]byte
   650  	// environKeys allow overwriting of existing values.
   651  	environKeys map[string]int
   652  	// fsConfig is the file system configuration for ABI like WASI.
   653  	fsConfig FSConfig
   654  	// sockConfig is the network listener configuration for ABI like WASI.
   655  	sockConfig *internalsock.Config
   656  }
   657  
   658  // NewModuleConfig returns a ModuleConfig that can be used for configuring module instantiation.
   659  func NewModuleConfig() ModuleConfig {
   660  	return &moduleConfig{
   661  		startFunctions: []string{"_start"},
   662  		environKeys:    map[string]int{},
   663  	}
   664  }
   665  
   666  // clone makes a deep copy of this module config.
   667  func (c *moduleConfig) clone() *moduleConfig {
   668  	ret := *c // copy except maps which share a ref
   669  	ret.environKeys = make(map[string]int, len(c.environKeys))
   670  	for key, value := range c.environKeys {
   671  		ret.environKeys[key] = value
   672  	}
   673  	return &ret
   674  }
   675  
   676  // WithArgs implements ModuleConfig.WithArgs
   677  func (c *moduleConfig) WithArgs(args ...string) ModuleConfig {
   678  	ret := c.clone()
   679  	ret.args = toByteSlices(args)
   680  	return ret
   681  }
   682  
   683  func toByteSlices(strings []string) (result [][]byte) {
   684  	if len(strings) == 0 {
   685  		return
   686  	}
   687  	result = make([][]byte, len(strings))
   688  	for i, a := range strings {
   689  		result[i] = []byte(a)
   690  	}
   691  	return
   692  }
   693  
   694  // WithEnv implements ModuleConfig.WithEnv
   695  func (c *moduleConfig) WithEnv(key, value string) ModuleConfig {
   696  	ret := c.clone()
   697  	// Check to see if this key already exists and update it.
   698  	if i, ok := ret.environKeys[key]; ok {
   699  		ret.environ[i+1] = []byte(value) // environ is pair-indexed, so the value is 1 after the key.
   700  	} else {
   701  		ret.environKeys[key] = len(ret.environ)
   702  		ret.environ = append(ret.environ, []byte(key), []byte(value))
   703  	}
   704  	return ret
   705  }
   706  
   707  // WithFS implements ModuleConfig.WithFS
   708  func (c *moduleConfig) WithFS(fs fs.FS) ModuleConfig {
   709  	var config FSConfig
   710  	if fs != nil {
   711  		config = NewFSConfig().WithFSMount(fs, "")
   712  	}
   713  	return c.WithFSConfig(config)
   714  }
   715  
   716  // WithFSConfig implements ModuleConfig.WithFSConfig
   717  func (c *moduleConfig) WithFSConfig(config FSConfig) ModuleConfig {
   718  	ret := c.clone()
   719  	ret.fsConfig = config
   720  	return ret
   721  }
   722  
   723  // WithName implements ModuleConfig.WithName
   724  func (c *moduleConfig) WithName(name string) ModuleConfig {
   725  	ret := c.clone()
   726  	ret.nameSet = true
   727  	ret.name = name
   728  	return ret
   729  }
   730  
   731  // WithStartFunctions implements ModuleConfig.WithStartFunctions
   732  func (c *moduleConfig) WithStartFunctions(startFunctions ...string) ModuleConfig {
   733  	ret := c.clone()
   734  	ret.startFunctions = startFunctions
   735  	return ret
   736  }
   737  
   738  // WithStderr implements ModuleConfig.WithStderr
   739  func (c *moduleConfig) WithStderr(stderr io.Writer) ModuleConfig {
   740  	ret := c.clone()
   741  	ret.stderr = stderr
   742  	return ret
   743  }
   744  
   745  // WithStdin implements ModuleConfig.WithStdin
   746  func (c *moduleConfig) WithStdin(stdin io.Reader) ModuleConfig {
   747  	ret := c.clone()
   748  	ret.stdin = stdin
   749  	return ret
   750  }
   751  
   752  // WithStdout implements ModuleConfig.WithStdout
   753  func (c *moduleConfig) WithStdout(stdout io.Writer) ModuleConfig {
   754  	ret := c.clone()
   755  	ret.stdout = stdout
   756  	return ret
   757  }
   758  
   759  // WithWalltime implements ModuleConfig.WithWalltime
   760  func (c *moduleConfig) WithWalltime(walltime sys.Walltime, resolution sys.ClockResolution) ModuleConfig {
   761  	ret := c.clone()
   762  	ret.walltime = walltime
   763  	ret.walltimeResolution = resolution
   764  	return ret
   765  }
   766  
   767  // We choose arbitrary resolutions here because there's no perfect alternative. For example, according to the
   768  // source in time.go, windows monotonic resolution can be 15ms. This chooses arbitrarily 1us for wall time and
   769  // 1ns for monotonic. See RATIONALE.md for more context.
   770  
   771  // WithSysWalltime implements ModuleConfig.WithSysWalltime
   772  func (c *moduleConfig) WithSysWalltime() ModuleConfig {
   773  	return c.WithWalltime(platform.Walltime, sys.ClockResolution(time.Microsecond.Nanoseconds()))
   774  }
   775  
   776  // WithNanotime implements ModuleConfig.WithNanotime
   777  func (c *moduleConfig) WithNanotime(nanotime sys.Nanotime, resolution sys.ClockResolution) ModuleConfig {
   778  	ret := c.clone()
   779  	ret.nanotime = nanotime
   780  	ret.nanotimeResolution = resolution
   781  	return ret
   782  }
   783  
   784  // WithSysNanotime implements ModuleConfig.WithSysNanotime
   785  func (c *moduleConfig) WithSysNanotime() ModuleConfig {
   786  	return c.WithNanotime(platform.Nanotime, sys.ClockResolution(1))
   787  }
   788  
   789  // WithNanosleep implements ModuleConfig.WithNanosleep
   790  func (c *moduleConfig) WithNanosleep(nanosleep sys.Nanosleep) ModuleConfig {
   791  	ret := *c // copy
   792  	ret.nanosleep = nanosleep
   793  	return &ret
   794  }
   795  
   796  // WithOsyield implements ModuleConfig.WithOsyield
   797  func (c *moduleConfig) WithOsyield(osyield sys.Osyield) ModuleConfig {
   798  	ret := *c // copy
   799  	ret.osyield = osyield
   800  	return &ret
   801  }
   802  
   803  // WithSysNanosleep implements ModuleConfig.WithSysNanosleep
   804  func (c *moduleConfig) WithSysNanosleep() ModuleConfig {
   805  	return c.WithNanosleep(platform.Nanosleep)
   806  }
   807  
   808  // WithRandSource implements ModuleConfig.WithRandSource
   809  func (c *moduleConfig) WithRandSource(source io.Reader) ModuleConfig {
   810  	ret := c.clone()
   811  	ret.randSource = source
   812  	return ret
   813  }
   814  
   815  // toSysContext creates a baseline wasm.Context configured by ModuleConfig.
   816  func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) {
   817  	var environ [][]byte // Intentionally doesn't pre-allocate to reduce logic to default to nil.
   818  	// Same validation as syscall.Setenv for Linux
   819  	for i := 0; i < len(c.environ); i += 2 {
   820  		key, value := c.environ[i], c.environ[i+1]
   821  		keyLen := len(key)
   822  		if keyLen == 0 {
   823  			err = errors.New("environ invalid: empty key")
   824  			return
   825  		}
   826  		valueLen := len(value)
   827  		result := make([]byte, keyLen+valueLen+1)
   828  		j := 0
   829  		for ; j < keyLen; j++ {
   830  			if k := key[j]; k == '=' { // NUL enforced in NewContext
   831  				err = errors.New("environ invalid: key contains '=' character")
   832  				return
   833  			} else {
   834  				result[j] = k
   835  			}
   836  		}
   837  		result[j] = '='
   838  		copy(result[j+1:], value)
   839  		environ = append(environ, result)
   840  	}
   841  
   842  	var fs fsapi.FS
   843  	if f, ok := c.fsConfig.(*fsConfig); ok {
   844  		if fs, err = f.toFS(); err != nil {
   845  			return
   846  		}
   847  	}
   848  
   849  	var listeners []*net.TCPListener
   850  	if n := c.sockConfig; n != nil {
   851  		if listeners, err = n.BuildTCPListeners(); err != nil {
   852  			return
   853  		}
   854  	}
   855  
   856  	return internalsys.NewContext(
   857  		math.MaxUint32,
   858  		c.args,
   859  		environ,
   860  		c.stdin,
   861  		c.stdout,
   862  		c.stderr,
   863  		c.randSource,
   864  		c.walltime, c.walltimeResolution,
   865  		c.nanotime, c.nanotimeResolution,
   866  		c.nanosleep, c.osyield,
   867  		fs,
   868  		listeners,
   869  	)
   870  }