package wazero import ( "context" "errors" "io" "io/fs" "math" "time" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/internal/engine/compiler" "github.com/tetratelabs/wazero/internal/engine/interpreter" "github.com/tetratelabs/wazero/internal/platform" internalsys "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/wasm" "github.com/tetratelabs/wazero/sys" ) // RuntimeConfig controls runtime behavior, with the default implementation as NewRuntimeConfig // // Ex. To explicitly limit to Wasm Core 1.0 features as opposed to relying on defaults: // // rConfig = wazero.NewRuntimeConfig().WithWasmCore1() // // Note: RuntimeConfig is immutable. Each WithXXX function returns a new instance including the corresponding change. type RuntimeConfig interface { // WithFeatureBulkMemoryOperations adds instructions modify ranges of memory or table entries // ("bulk-memory-operations"). This defaults to false as the feature was not finished in WebAssembly 1.0. // // Here are the notable effects: // * Adds `memory.fill`, `memory.init`, `memory.copy` and `data.drop` instructions. // * Adds `table.init`, `table.copy` and `elem.drop` instructions. // * Introduces a "passive" form of element and data segments. // * Stops checking "active" element and data segment boundaries at compile-time, meaning they can error at runtime. // // Note: "bulk-memory-operations" is mixed with the "reference-types" proposal // due to the WebAssembly Working Group merging them "mutually dependent". // Therefore, enabling this feature results in enabling WithFeatureReferenceTypes, and vice-versa. // // See https://github.com/WebAssembly/spec/blob/main/proposals/bulk-memory-operations/Overview.md // https://github.com/WebAssembly/spec/blob/main/proposals/reference-types/Overview.md and // https://github.com/WebAssembly/spec/pull/1287 WithFeatureBulkMemoryOperations(bool) RuntimeConfig // WithFeatureMultiValue enables multiple values ("multi-value"). This defaults to false as the feature was not // finished in WebAssembly 1.0 (20191205). // // Here are the notable effects: // * Function (`func`) types allow more than one result // * Block types (`block`, `loop` and `if`) can be arbitrary function types // // See https://github.com/WebAssembly/spec/blob/main/proposals/multi-value/Overview.md WithFeatureMultiValue(bool) RuntimeConfig // WithFeatureMutableGlobal allows globals to be mutable. This defaults to true as the feature was finished in // WebAssembly 1.0 (20191205). // // When false, an api.Global can never be cast to an api.MutableGlobal, and any wasm that includes global vars // will fail to parse. WithFeatureMutableGlobal(bool) RuntimeConfig // WithFeatureNonTrappingFloatToIntConversion enables non-trapping float-to-int conversions. // ("nontrapping-float-to-int-conversion"). This defaults to false as the feature was not in WebAssembly 1.0. // // The only effect of enabling is allowing the following instructions, which return 0 on NaN instead of panicking. // * `i32.trunc_sat_f32_s` // * `i32.trunc_sat_f32_u` // * `i32.trunc_sat_f64_s` // * `i32.trunc_sat_f64_u` // * `i64.trunc_sat_f32_s` // * `i64.trunc_sat_f32_u` // * `i64.trunc_sat_f64_s` // * `i64.trunc_sat_f64_u` // // See https://github.com/WebAssembly/spec/blob/main/proposals/nontrapping-float-to-int-conversion/Overview.md WithFeatureNonTrappingFloatToIntConversion(bool) RuntimeConfig // WithFeatureReferenceTypes enables various instructions and features related to table and new reference types. // // * Introduction of new value types: `funcref` and `externref`. // * Support for the following new instructions: // * `ref.null` // * `ref.func` // * `ref.is_null` // * `table.fill` // * `table.get` // * `table.grow` // * `table.set` // * `table.size` // * Support for multiple tables per module: // * `call_indirect`, `table.init`, `table.copy` and `elem.drop` instructions can take non-zero table index. // * Element segments can take non-zero table index. // // Note: "reference-types" is mixed with the "bulk-memory-operations" proposal // due to the WebAssembly Working Group merging them "mutually dependent". // Therefore, enabling this feature results in enabling WithFeatureBulkMemoryOperations, and vice-versa. // // See https://github.com/WebAssembly/spec/blob/main/proposals/bulk-memory-operations/Overview.md // https://github.com/WebAssembly/spec/blob/main/proposals/reference-types/Overview.md and // https://github.com/WebAssembly/spec/pull/1287 WithFeatureReferenceTypes(enabled bool) RuntimeConfig // WithFeatureSignExtensionOps enables sign extension instructions ("sign-extension-ops"). This defaults to false // as the feature was not in WebAssembly 1.0. // // Here are the notable effects: // * Adds instructions `i32.extend8_s`, `i32.extend16_s`, `i64.extend8_s`, `i64.extend16_s` and `i64.extend32_s` // // See https://github.com/WebAssembly/spec/blob/main/proposals/sign-extension-ops/Overview.md WithFeatureSignExtensionOps(bool) RuntimeConfig // WithFeatureSIMD enables the vector value type and vector instructions (aka SIMD). This defaults to false // as the feature was not in WebAssembly 1.0. // // See https://github.com/WebAssembly/spec/blob/main/proposals/simd/SIMD.md WithFeatureSIMD(bool) RuntimeConfig // WithWasmCore1 enables features included in the WebAssembly Core Specification 1.0. Selecting this // overwrites any currently accumulated features with only those included in this W3C recommendation. // // This is default because as of mid 2022, this is the only version that is a Web Standard (W3C Recommendation). // // You can select the latest draft of the WebAssembly Core Specification 2.0 instead via WithWasmCore2. You can // also enable or disable individual features via `WithXXX` methods. Ex. // rConfig = wazero.NewRuntimeConfig().WithWasmCore1().WithFeatureMutableGlobal(false) // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/ WithWasmCore1() RuntimeConfig // WithWasmCore2 enables features included in the WebAssembly Core Specification 2.0 (20220419). Selecting this // overwrites any currently accumulated features with only those included in this W3C working draft. // // This is not default because it is not yet incomplete and also not yet a Web Standard (W3C Recommendation). // // Even after selecting this, you can enable or disable individual features via `WithXXX` methods. Ex. // rConfig = wazero.NewRuntimeConfig().WithWasmCore2().WithFeatureMutableGlobal(false) // // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/ WithWasmCore2() RuntimeConfig } // NewRuntimeConfig returns a RuntimeConfig using the compiler if it is supported in this environment, // or the interpreter otherwise. func NewRuntimeConfig() RuntimeConfig { return newRuntimeConfig() } type runtimeConfig struct { enabledFeatures wasm.Features isInterpreter bool newEngine func(wasm.Features) wasm.Engine } // engineLessConfig helps avoid copy/pasting the wrong defaults. var engineLessConfig = &runtimeConfig{ enabledFeatures: wasm.Features20191205, } // NewRuntimeConfigCompiler compiles WebAssembly modules into // runtime.GOARCH-specific assembly for optimal performance. // // The default implementation is AOT (Ahead of Time) compilation, applied at // Runtime.CompileModule. This allows consistent runtime performance, as well // the ability to reduce any first request penalty. // // Note: While this is technically AOT, this does not imply any action on your // part. wazero automatically performs ahead-of-time compilation as needed when // Runtime.CompileModule is invoked. // // Warning: This panics at runtime if the runtime.GOOS or runtime.GOARCH does not // support Compiler. Use NewRuntimeConfig to safely detect and fallback to // NewRuntimeConfigInterpreter if needed. func NewRuntimeConfigCompiler() RuntimeConfig { ret := engineLessConfig.clone() ret.newEngine = compiler.NewEngine return ret } // NewRuntimeConfigInterpreter interprets WebAssembly modules instead of compiling them into assembly. func NewRuntimeConfigInterpreter() RuntimeConfig { ret := engineLessConfig.clone() ret.isInterpreter = true ret.newEngine = interpreter.NewEngine return ret } // clone makes a deep copy of this runtime config. func (c *runtimeConfig) clone() *runtimeConfig { ret := *c // copy except maps which share a ref return &ret } // WithFeatureBulkMemoryOperations implements RuntimeConfig.WithFeatureBulkMemoryOperations func (c *runtimeConfig) WithFeatureBulkMemoryOperations(enabled bool) RuntimeConfig { ret := c.clone() ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureBulkMemoryOperations, enabled) // bulk-memory-operations proposal is mutually-dependant with reference-types proposal. ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureReferenceTypes, enabled) return ret } // WithFeatureMultiValue implements RuntimeConfig.WithFeatureMultiValue func (c *runtimeConfig) WithFeatureMultiValue(enabled bool) RuntimeConfig { ret := c.clone() ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureMultiValue, enabled) return ret } // WithFeatureMutableGlobal implements RuntimeConfig.WithFeatureMutableGlobal func (c *runtimeConfig) WithFeatureMutableGlobal(enabled bool) RuntimeConfig { ret := c.clone() ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureMutableGlobal, enabled) return ret } // WithFeatureNonTrappingFloatToIntConversion implements RuntimeConfig.WithFeatureNonTrappingFloatToIntConversion func (c *runtimeConfig) WithFeatureNonTrappingFloatToIntConversion(enabled bool) RuntimeConfig { ret := c.clone() ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureNonTrappingFloatToIntConversion, enabled) return ret } // WithFeatureReferenceTypes implements RuntimeConfig.WithFeatureReferenceTypes func (c *runtimeConfig) WithFeatureReferenceTypes(enabled bool) RuntimeConfig { ret := c.clone() ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureReferenceTypes, enabled) // reference-types proposal is mutually-dependant with bulk-memory-operations proposal. ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureBulkMemoryOperations, enabled) return ret } // WithFeatureSignExtensionOps implements RuntimeConfig.WithFeatureSignExtensionOps func (c *runtimeConfig) WithFeatureSignExtensionOps(enabled bool) RuntimeConfig { ret := c.clone() ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureSignExtensionOps, enabled) return ret } // WithFeatureSIMD implements RuntimeConfig.WithFeatureSIMD func (c *runtimeConfig) WithFeatureSIMD(enabled bool) RuntimeConfig { ret := c.clone() ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureSIMD, enabled) return ret } // WithWasmCore1 implements RuntimeConfig.WithWasmCore1 func (c *runtimeConfig) WithWasmCore1() RuntimeConfig { ret := c.clone() ret.enabledFeatures = wasm.Features20191205 return ret } // WithWasmCore2 implements RuntimeConfig.WithWasmCore2 func (c *runtimeConfig) WithWasmCore2() RuntimeConfig { ret := c.clone() ret.enabledFeatures = wasm.Features20220419 return ret } // CompiledModule is a WebAssembly 1.0 module ready to be instantiated (Runtime.InstantiateModule) as an api.Module. // // In WebAssembly terminology, this is a decoded, validated, and possibly also compiled module. wazero avoids using // the name "Module" for both before and after instantiation as the name conflation has caused confusion. // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#semantic-phases%E2%91%A0 // // Note: Closing the wazero.Runtime closes any CompiledModule it compiled. type CompiledModule interface { // Name returns the module name encoded into the binary or empty if not. Name() string // ImportedFunctions returns all the imported functions // (api.FunctionDefinition) in this module or nil if there are none. // // Note: Unlike ExportedFunctions, there is no unique constraint on // imports. ImportedFunctions() []api.FunctionDefinition // ExportedFunctions returns all the exported functions // (api.FunctionDefinition) in this module keyed on export name. ExportedFunctions() map[string]api.FunctionDefinition // Close releases all the allocated resources for this CompiledModule. // // Note: It is safe to call Close while having outstanding calls from an // api.Module instantiated from this. Close(context.Context) error } type compiledModule struct { module *wasm.Module // compiledEngine holds an engine on which `module` is compiled. compiledEngine wasm.Engine // listeners are present if the code was compiled with a listener listeners []experimental.FunctionListener // closeWithModule prevents leaking compiled code when a module is compiled implicitly. closeWithModule bool } // Name implements CompiledModule.Name func (c *compiledModule) Name() (moduleName string) { if ns := c.module.NameSection; ns != nil { moduleName = ns.ModuleName } return } // Close implements CompiledModule.Close func (c *compiledModule) Close(_ context.Context) error { // Note: If you use the context.Context param, don't forget to coerce nil to context.Background()! c.compiledEngine.DeleteCompiledModule(c.module) // It is possible the underlying may need to return an error later, but in any case this matches api.Module.Close. return nil } // ImportedFunctions implements CompiledModule.ImportedFunctions func (c *compiledModule) ImportedFunctions() []api.FunctionDefinition { return c.module.ImportedFunctions() } // ExportedFunctions implements CompiledModule.ExportedFunctions func (c *compiledModule) ExportedFunctions() map[string]api.FunctionDefinition { return c.module.ExportedFunctions() } // CompileConfig allows you to override what was decoded from wasm, prior to compilation (ModuleBuilder.Compile or // Runtime.CompileModule). // // For example, WithMemorySizer allows you to override memoryc size that doesn't match your requirements. // // Note: CompileConfig is immutable. Each WithXXX function returns a new instance including the corresponding change. type CompileConfig interface { // WithMemorySizer are the allocation parameters used for a Wasm memory. // The default is to set cap=min and max=65536 if unset. A nil function is invalid and ignored. WithMemorySizer(api.MemorySizer) CompileConfig } type compileConfig struct { memorySizer api.MemorySizer } // NewCompileConfig returns a CompileConfig that can be used for configuring module compilation. func NewCompileConfig() CompileConfig { return &compileConfig{ memorySizer: wasm.MemorySizer, } } // clone makes a deep copy of this compile config. func (c *compileConfig) clone() *compileConfig { ret := *c // copy except maps which share a ref return &ret } // WithMemorySizer implements CompileConfig.WithMemorySizer func (c *compileConfig) WithMemorySizer(memorySizer api.MemorySizer) CompileConfig { if memorySizer == nil { return c } ret := c.clone() ret.memorySizer = memorySizer return ret } // ModuleConfig configures resources needed by functions that have low-level interactions with the host operating // system. Using this, resources such as STDIN can be isolated, so that the same module can be safely instantiated // multiple times. // // Ex. // // // Initialize base configuration: // config := wazero.NewModuleConfig().WithStdout(buf).WithSysNanotime() // // // Assign different configuration on each instantiation // module, _ := r.InstantiateModule(ctx, compiled, config.WithName("rotate").WithArgs("rotate", "angle=90", "dir=cw")) // // While wazero supports Windows as a platform, host functions using ModuleConfig follow a UNIX dialect. // See RATIONALE.md for design background and relationship to WebAssembly System Interfaces (WASI). // // Note: ModuleConfig is immutable. Each WithXXX function returns a new instance including the corresponding change. type ModuleConfig interface { // WithArgs assigns command-line arguments visible to an imported function that reads an arg vector (argv). Defaults to // none. Runtime.InstantiateModule errs if any arg is empty. // // These values are commonly read by the functions like "args_get" in "wasi_snapshot_preview1" although they could be // read by functions imported from other modules. // // Similar to os.Args and exec.Cmd Env, many implementations would expect a program name to be argv[0]. However, neither // WebAssembly nor WebAssembly System Interfaces (WASI) define this. Regardless, you may choose to set the first // argument to the same value set via WithName. // // Note: This does not default to os.Args as that violates sandboxing. // // See https://linux.die.net/man/3/argv and https://en.wikipedia.org/wiki/Null-terminated_string WithArgs(...string) ModuleConfig // WithEnv sets an environment variable visible to a Module that imports functions. Defaults to none. // Runtime.InstantiateModule errs if the key is empty or contains a NULL(0) or equals("") character. // // Validation is the same as os.Setenv on Linux and replaces any existing value. Unlike exec.Cmd Env, this does not // default to the current process environment as that would violate sandboxing. This also does not preserve order. // // Environment variables are commonly read by the functions like "environ_get" in "wasi_snapshot_preview1" although // they could be read by functions imported from other modules. // // While similar to process configuration, there are no assumptions that can be made about anything OS-specific. For // example, neither WebAssembly nor WebAssembly System Interfaces (WASI) define concerns processes have, such as // case-sensitivity on environment keys. For portability, define entries with case-insensitively unique keys. // // See https://linux.die.net/man/3/environ and https://en.wikipedia.org/wiki/Null-terminated_string WithEnv(key, value string) ModuleConfig // WithFS assigns the file system to use for any paths beginning at "/". // Defaults return fs.ErrNotExist. // // Ex. This sets a read-only, embedded file-system: // // //go:embed testdata/index.html // var testdataIndex embed.FS // // rooted, err := fs.Sub(testdataIndex, "testdata") // require.NoError(t, err) // // // "index.html" is accessible as "/index.html". // config := wazero.NewModuleConfig().WithFS(rooted) // // Ex. This sets a mutable file-system: // // // Files relative to "/work/appA" are accessible as "/". // config := wazero.NewModuleConfig().WithFS(os.DirFS("/work/appA")) // // Isolation // // os.DirFS documentation includes important notes about isolation, which // also applies to fs.Sub. As of Go 1.19, the built-in file-systems are not // jailed (chroot). See https://github.com/golang/go/issues/42322 // // Working Directory "." // // Relative path resolution, such as "./config.yml" to "/config.yml" or // otherwise, is compiler-specific. See /RATIONALE.md for notes. WithFS(fs.FS) ModuleConfig // WithName configures the module name. Defaults to what was decoded or overridden via CompileConfig.WithModuleName. WithName(string) ModuleConfig // WithStartFunctions configures the functions to call after the module is // instantiated. Defaults to "_start". // // Notes // // * If any function doesn't exist, it is skipped. However, all functions // that do exist are called in order. // * Some start functions may exit the module during instantiate with a // sys.ExitError (ex. emscripten), preventing use of exported functions. WithStartFunctions(...string) ModuleConfig // WithStderr configures where standard error (file descriptor 2) is written. Defaults to io.Discard. // // This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could // be used by functions imported from other modules. // // Notes // // * The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close. // * This does not default to os.Stderr as that both violates sandboxing and prevents concurrent modules. // // See https://linux.die.net/man/3/stderr WithStderr(io.Writer) ModuleConfig // WithStdin configures where standard input (file descriptor 0) is read. Defaults to return io.EOF. // // This reader is most commonly used by the functions like "fd_read" in "wasi_snapshot_preview1" although it could // be used by functions imported from other modules. // // Notes // // * The caller is responsible to close any io.Reader they supply: It is not closed on api.Module Close. // * This does not default to os.Stdin as that both violates sandboxing and prevents concurrent modules. // // See https://linux.die.net/man/3/stdin WithStdin(io.Reader) ModuleConfig // WithStdout configures where standard output (file descriptor 1) is written. Defaults to io.Discard. // // This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could // be used by functions imported from other modules. // // Notes // // * The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close. // * This does not default to os.Stdout as that both violates sandboxing and prevents concurrent modules. // // See https://linux.die.net/man/3/stdout WithStdout(io.Writer) ModuleConfig // WithWalltime configures the wall clock, sometimes referred to as the // real time clock. Defaults to a fake result that increases by 1ms on // each reading. // // Ex. To override with your own clock: // moduleConfig = moduleConfig. // WithWalltime(func(context.Context) (sec int64, nsec int32) { // return clock.walltime() // }, sys.ClockResolution(time.Microsecond.Nanoseconds())) // // Note: This does not default to time.Now as that violates sandboxing. Use // WithSysWalltime for a usable implementation. WithWalltime(sys.Walltime, sys.ClockResolution) ModuleConfig // WithSysWalltime uses time.Now for sys.Walltime with a resolution of 1us // (1000ns). // // See WithWalltime WithSysWalltime() ModuleConfig // WithNanotime configures the monotonic clock, used to measure elapsed // time in nanoseconds. Defaults to a fake result that increases by 1ms // on each reading. // // Ex. To override with your own clock: // moduleConfig = moduleConfig. // WithNanotime(func(context.Context) int64 { // return clock.nanotime() // }, sys.ClockResolution(time.Microsecond.Nanoseconds())) // // Notes: // * This does not default to time.Since as that violates sandboxing. // * Some compilers implement sleep by looping on sys.Nanotime (ex. Go). // * If you set this, you should probably set WithNanosleep also. // * Use WithSysNanotime for a usable implementation. WithNanotime(sys.Nanotime, sys.ClockResolution) ModuleConfig // WithSysNanotime uses time.Now for sys.Nanotime with a resolution of 1us. // // See WithNanotime WithSysNanotime() ModuleConfig // WithNanosleep configures the how to pause the current goroutine for at // least the configured nanoseconds. Defaults to return immediately. // // Ex. To override with your own sleep function: // moduleConfig = moduleConfig. // WithNanosleep(func(ctx context.Context, ns int64) { // rel := unix.NsecToTimespec(ns) // remain := unix.Timespec{} // for { // loop until no more time remaining // err := unix.ClockNanosleep(unix.CLOCK_MONOTONIC, 0, &rel, &remain) // --snip-- // // Notes: // * This primarily supports `poll_oneoff` for relative clock events. // * This does not default to time.Sleep as that violates sandboxing. // * Some compilers implement sleep by looping on sys.Nanotime (ex. Go). // * If you set this, you should probably set WithNanotime also. // * Use WithSysNanosleep for a usable implementation. WithNanosleep(sys.Nanosleep) ModuleConfig // WithSysNanosleep uses time.Sleep for sys.Nanosleep. // // See WithNanosleep WithSysNanosleep() ModuleConfig // WithRandSource configures a source of random bytes. Defaults to crypto/rand.Reader. // // This reader is most commonly used by the functions like "random_get" in "wasi_snapshot_preview1" or "seed" in // AssemblyScript standard "env" although it could be used by functions imported from other modules. // // Note: The caller is responsible to close any io.Reader they supply: It is not closed on api.Module Close. WithRandSource(io.Reader) ModuleConfig } type moduleConfig struct { name string startFunctions []string stdin io.Reader stdout io.Writer stderr io.Writer randSource io.Reader walltime *sys.Walltime walltimeResolution sys.ClockResolution nanotime *sys.Nanotime nanotimeResolution sys.ClockResolution nanosleep *sys.Nanosleep args []string // environ is pair-indexed to retain order similar to os.Environ. environ []string // environKeys allow overwriting of existing values. environKeys map[string]int // fs is the file system to open files with fs fs.FS } // NewModuleConfig returns a ModuleConfig that can be used for configuring module instantiation. func NewModuleConfig() ModuleConfig { return &moduleConfig{ startFunctions: []string{"_start"}, environKeys: map[string]int{}, } } // clone makes a deep copy of this module config. func (c *moduleConfig) clone() *moduleConfig { ret := *c // copy except maps which share a ref ret.environKeys = make(map[string]int, len(c.environKeys)) for key, value := range c.environKeys { ret.environKeys[key] = value } return &ret } // WithArgs implements ModuleConfig.WithArgs func (c *moduleConfig) WithArgs(args ...string) ModuleConfig { ret := c.clone() ret.args = args return ret } // WithEnv implements ModuleConfig.WithEnv func (c *moduleConfig) WithEnv(key, value string) ModuleConfig { ret := c.clone() // Check to see if this key already exists and update it. if i, ok := ret.environKeys[key]; ok { ret.environ[i+1] = value // environ is pair-indexed, so the value is 1 after the key. } else { ret.environKeys[key] = len(ret.environ) ret.environ = append(ret.environ, key, value) } return ret } // WithFS implements ModuleConfig.WithFS func (c *moduleConfig) WithFS(fs fs.FS) ModuleConfig { ret := c.clone() ret.fs = fs return ret } // WithName implements ModuleConfig.WithName func (c *moduleConfig) WithName(name string) ModuleConfig { ret := c.clone() ret.name = name return ret } // WithStartFunctions implements ModuleConfig.WithStartFunctions func (c *moduleConfig) WithStartFunctions(startFunctions ...string) ModuleConfig { ret := c.clone() ret.startFunctions = startFunctions return ret } // WithStderr implements ModuleConfig.WithStderr func (c *moduleConfig) WithStderr(stderr io.Writer) ModuleConfig { ret := c.clone() ret.stderr = stderr return ret } // WithStdin implements ModuleConfig.WithStdin func (c *moduleConfig) WithStdin(stdin io.Reader) ModuleConfig { ret := c.clone() ret.stdin = stdin return ret } // WithStdout implements ModuleConfig.WithStdout func (c *moduleConfig) WithStdout(stdout io.Writer) ModuleConfig { ret := c.clone() ret.stdout = stdout return ret } // WithWalltime implements ModuleConfig.WithWalltime func (c *moduleConfig) WithWalltime(walltime sys.Walltime, resolution sys.ClockResolution) ModuleConfig { ret := c.clone() ret.walltime = &walltime ret.walltimeResolution = resolution return ret } // We choose arbitrary resolutions here because there's no perfect alternative. For example, according to the // source in time.go, windows monotonic resolution can be 15ms. This chooses arbitrarily 1us for wall time and // 1ns for monotonic. See RATIONALE.md for more context. // WithSysWalltime implements ModuleConfig.WithSysWalltime func (c *moduleConfig) WithSysWalltime() ModuleConfig { return c.WithWalltime(platform.Walltime, sys.ClockResolution(time.Microsecond.Nanoseconds())) } // WithNanotime implements ModuleConfig.WithNanotime func (c *moduleConfig) WithNanotime(nanotime sys.Nanotime, resolution sys.ClockResolution) ModuleConfig { ret := c.clone() ret.nanotime = &nanotime ret.nanotimeResolution = resolution return ret } // WithSysNanotime implements ModuleConfig.WithSysNanotime func (c *moduleConfig) WithSysNanotime() ModuleConfig { return c.WithNanotime(platform.Nanotime, sys.ClockResolution(1)) } // WithNanosleep implements ModuleConfig.WithNanosleep func (c *moduleConfig) WithNanosleep(nanosleep sys.Nanosleep) ModuleConfig { ret := *c // copy ret.nanosleep = &nanosleep return &ret } // WithSysNanosleep implements ModuleConfig.WithSysNanosleep func (c *moduleConfig) WithSysNanosleep() ModuleConfig { return c.WithNanosleep(platform.Nanosleep) } // WithRandSource implements ModuleConfig.WithRandSource func (c *moduleConfig) WithRandSource(source io.Reader) ModuleConfig { ret := c.clone() ret.randSource = source return ret } // toSysContext creates a baseline wasm.Context configured by ModuleConfig. func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) { var environ []string // Intentionally doesn't pre-allocate to reduce logic to default to nil. // Same validation as syscall.Setenv for Linux for i := 0; i < len(c.environ); i += 2 { key, value := c.environ[i], c.environ[i+1] if len(key) == 0 { err = errors.New("environ invalid: empty key") return } for j := 0; j < len(key); j++ { if key[j] == '=' { // NUL enforced in NewContext err = errors.New("environ invalid: key contains '=' character") return } } environ = append(environ, key+"="+value) } return internalsys.NewContext( math.MaxUint32, c.args, environ, c.stdin, c.stdout, c.stderr, c.randSource, c.walltime, c.walltimeResolution, c.nanotime, c.nanotimeResolution, c.nanosleep, c.fs, ) }