wa-lang.org/wazero@v1.0.2/config.go (about) 1 package wazero 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "io/fs" 9 "math" 10 "time" 11 12 "wa-lang.org/wazero/api" 13 "wa-lang.org/wazero/internal/engine/compiler" 14 "wa-lang.org/wazero/internal/engine/interpreter" 15 "wa-lang.org/wazero/internal/platform" 16 internalsys "wa-lang.org/wazero/internal/sys" 17 "wa-lang.org/wazero/internal/wasm" 18 "wa-lang.org/wazero/sys" 19 ) 20 21 // RuntimeConfig controls runtime behavior, with the default implementation as 22 // NewRuntimeConfig 23 // 24 // The example below explicitly limits to Wasm Core 1.0 features as opposed to 25 // relying on defaults: 26 // 27 // rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(api.CoreFeaturesV1) 28 // 29 // Note: RuntimeConfig is immutable. Each WithXXX function returns a new 30 // instance including the corresponding change. 31 type RuntimeConfig interface { 32 // WithCoreFeatures sets the WebAssembly Core specification features this 33 // runtime supports. Defaults to api.CoreFeaturesV2. 34 // 35 // Example of disabling a specific feature: 36 // features := api.CoreFeaturesV2.SetEnabled(api.CoreFeatureMutableGlobal, false) 37 // rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(features) 38 // 39 // # Why default to version 2.0? 40 // 41 // Many compilers that target WebAssembly require features after 42 // api.CoreFeaturesV1 by default. For example, TinyGo v0.24+ requires 43 // api.CoreFeatureBulkMemoryOperations. To avoid runtime errors, wazero 44 // defaults to api.CoreFeaturesV2, even though it is not yet a Web 45 // Standard (REC). 46 WithCoreFeatures(api.CoreFeatures) RuntimeConfig 47 48 // WithMemoryLimitPages overrides the maximum pages allowed per memory. The 49 // default is 65536, allowing 4GB total memory per instance. Setting a 50 // value larger than default will panic. 51 // 52 // This example reduces the largest possible memory size from 4GB to 128KB: 53 // rConfig = wazero.NewRuntimeConfig().WithMemoryLimitPages(2) 54 // 55 // Note: Wasm has 32-bit memory and each page is 65536 (2^16) bytes. This 56 // implies a max of 65536 (2^16) addressable pages. 57 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem 58 WithMemoryLimitPages(memoryLimitPages uint32) RuntimeConfig 59 60 // WithMemoryCapacityFromMax eagerly allocates max memory, unless max is 61 // not defined. The default is false, which means minimum memory is 62 // allocated and any call to grow memory results in re-allocations. 63 // 64 // This example ensures any memory.grow instruction will never re-allocate: 65 // rConfig = wazero.NewRuntimeConfig().WithMemoryCapacityFromMax(true) 66 // 67 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem 68 WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig 69 } 70 71 // NewRuntimeConfig returns a RuntimeConfig using the compiler if it is supported in this environment, 72 // or the interpreter otherwise. 73 func NewRuntimeConfig() RuntimeConfig { 74 return newRuntimeConfig() 75 } 76 77 type runtimeConfig struct { 78 enabledFeatures api.CoreFeatures 79 memoryLimitPages uint32 80 memoryCapacityFromMax bool 81 isInterpreter bool 82 newEngine func(context.Context, api.CoreFeatures) wasm.Engine 83 } 84 85 // engineLessConfig helps avoid copy/pasting the wrong defaults. 86 var engineLessConfig = &runtimeConfig{ 87 enabledFeatures: api.CoreFeaturesV2, 88 memoryLimitPages: wasm.MemoryLimitPages, 89 memoryCapacityFromMax: false, 90 } 91 92 // NewRuntimeConfigCompiler compiles WebAssembly modules into 93 // runtime.GOARCH-specific assembly for optimal performance. 94 // 95 // The default implementation is AOT (Ahead of Time) compilation, applied at 96 // Runtime.CompileModule. This allows consistent runtime performance, as well 97 // the ability to reduce any first request penalty. 98 // 99 // Note: While this is technically AOT, this does not imply any action on your 100 // part. wazero automatically performs ahead-of-time compilation as needed when 101 // Runtime.CompileModule is invoked. 102 // 103 // Warning: This panics at runtime if the runtime.GOOS or runtime.GOARCH does not 104 // support Compiler. Use NewRuntimeConfig to safely detect and fallback to 105 // NewRuntimeConfigInterpreter if needed. 106 func NewRuntimeConfigCompiler() RuntimeConfig { 107 ret := engineLessConfig.clone() 108 ret.newEngine = compiler.NewEngine 109 return ret 110 } 111 112 // NewRuntimeConfigInterpreter interprets WebAssembly modules instead of compiling them into assembly. 113 func NewRuntimeConfigInterpreter() RuntimeConfig { 114 ret := engineLessConfig.clone() 115 ret.isInterpreter = true 116 ret.newEngine = interpreter.NewEngine 117 return ret 118 } 119 120 // clone makes a deep copy of this runtime config. 121 func (c *runtimeConfig) clone() *runtimeConfig { 122 ret := *c // copy except maps which share a ref 123 return &ret 124 } 125 126 // WithCoreFeatures implements RuntimeConfig.WithCoreFeatures 127 func (c *runtimeConfig) WithCoreFeatures(features api.CoreFeatures) RuntimeConfig { 128 ret := c.clone() 129 ret.enabledFeatures = features 130 return ret 131 } 132 133 // WithMemoryLimitPages implements RuntimeConfig.WithMemoryLimitPages 134 func (c *runtimeConfig) WithMemoryLimitPages(memoryLimitPages uint32) RuntimeConfig { 135 ret := c.clone() 136 // This panics instead of returning an error as it is unlikely. 137 if memoryLimitPages > wasm.MemoryLimitPages { 138 panic(fmt.Errorf("memoryLimitPages invalid: %d > %d", memoryLimitPages, wasm.MemoryLimitPages)) 139 } 140 ret.memoryLimitPages = memoryLimitPages 141 return ret 142 } 143 144 // WithMemoryCapacityFromMax implements RuntimeConfig.WithMemoryCapacityFromMax 145 func (c *runtimeConfig) WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig { 146 ret := c.clone() 147 ret.memoryCapacityFromMax = memoryCapacityFromMax 148 return ret 149 } 150 151 // CompiledModule is a WebAssembly module ready to be instantiated (Runtime.InstantiateModule) as an api.Module. 152 // 153 // In WebAssembly terminology, this is a decoded, validated, and possibly also compiled module. wazero avoids using 154 // the name "Module" for both before and after instantiation as the name conflation has caused confusion. 155 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#semantic-phases%E2%91%A0 156 // 157 // Note: Closing the wazero.Runtime closes any CompiledModule it compiled. 158 type CompiledModule interface { 159 // Name returns the module name encoded into the binary or empty if not. 160 Name() string 161 162 // ImportedFunctions returns all the imported functions 163 // (api.FunctionDefinition) in this module or nil if there are none. 164 // 165 // Note: Unlike ExportedFunctions, there is no unique constraint on 166 // imports. 167 ImportedFunctions() []api.FunctionDefinition 168 169 // ExportedFunctions returns all the exported functions 170 // (api.FunctionDefinition) in this module keyed on export name. 171 ExportedFunctions() map[string]api.FunctionDefinition 172 173 // ImportedMemories returns all the imported memories 174 // (api.MemoryDefinition) in this module or nil if there are none. 175 // 176 // ## Notes 177 // - As of WebAssembly Core Specification 2.0, there can be at most one 178 // memory. 179 // - Unlike ExportedMemories, there is no unique constraint on imports. 180 ImportedMemories() []api.MemoryDefinition 181 182 // ExportedMemories returns all the exported memories 183 // (api.MemoryDefinition) in this module keyed on export name. 184 // 185 // Note: As of WebAssembly Core Specification 2.0, there can be at most one 186 // memory. 187 ExportedMemories() map[string]api.MemoryDefinition 188 189 // Close releases all the allocated resources for this CompiledModule. 190 // 191 // Note: It is safe to call Close while having outstanding calls from an 192 // api.Module instantiated from this. 193 Close(context.Context) error 194 } 195 196 // compile-time check to ensure compiledModule implements CompiledModule 197 var _ CompiledModule = &compiledModule{} 198 199 type compiledModule struct { 200 module *wasm.Module 201 // compiledEngine holds an engine on which `module` is compiled. 202 compiledEngine wasm.Engine 203 // closeWithModule prevents leaking compiled code when a module is compiled implicitly. 204 closeWithModule bool 205 } 206 207 // Name implements CompiledModule.Name 208 func (c *compiledModule) Name() (moduleName string) { 209 if ns := c.module.NameSection; ns != nil { 210 moduleName = ns.ModuleName 211 } 212 return 213 } 214 215 // Close implements CompiledModule.Close 216 func (c *compiledModule) Close(context.Context) error { 217 c.compiledEngine.DeleteCompiledModule(c.module) 218 // It is possible the underlying may need to return an error later, but in any case this matches api.Module.Close. 219 return nil 220 } 221 222 // ImportedFunctions implements CompiledModule.ImportedFunctions 223 func (c *compiledModule) ImportedFunctions() []api.FunctionDefinition { 224 return c.module.ImportedFunctions() 225 } 226 227 // ExportedFunctions implements CompiledModule.ExportedFunctions 228 func (c *compiledModule) ExportedFunctions() map[string]api.FunctionDefinition { 229 return c.module.ExportedFunctions() 230 } 231 232 // ImportedMemories implements CompiledModule.ImportedMemories 233 func (c *compiledModule) ImportedMemories() []api.MemoryDefinition { 234 return c.module.ImportedMemories() 235 } 236 237 // ExportedMemories implements CompiledModule.ExportedMemories 238 func (c *compiledModule) ExportedMemories() map[string]api.MemoryDefinition { 239 return c.module.ExportedMemories() 240 } 241 242 // ModuleConfig configures resources needed by functions that have low-level interactions with the host operating 243 // system. Using this, resources such as STDIN can be isolated, so that the same module can be safely instantiated 244 // multiple times. 245 // 246 // Here's an example: 247 // 248 // // Initialize base configuration: 249 // config := wazero.NewModuleConfig().WithStdout(buf).WithSysNanotime() 250 // 251 // // Assign different configuration on each instantiation 252 // module, _ := r.InstantiateModule(ctx, compiled, config.WithName("rotate").WithArgs("rotate", "angle=90", "dir=cw")) 253 // 254 // While wazero supports Windows as a platform, host functions using ModuleConfig follow a UNIX dialect. 255 // See RATIONALE.md for design background and relationship to WebAssembly System Interfaces (WASI). 256 // 257 // Note: ModuleConfig is immutable. Each WithXXX function returns a new instance including the corresponding change. 258 type ModuleConfig interface { 259 // WithArgs assigns command-line arguments visible to an imported function that reads an arg vector (argv). Defaults to 260 // none. Runtime.InstantiateModule errs if any arg is empty. 261 // 262 // These values are commonly read by the functions like "args_get" in "wasi_snapshot_preview1" although they could be 263 // read by functions imported from other modules. 264 // 265 // Similar to os.Args and exec.Cmd Env, many implementations would expect a program name to be argv[0]. However, neither 266 // WebAssembly nor WebAssembly System Interfaces (WASI) define this. Regardless, you may choose to set the first 267 // argument to the same value set via WithName. 268 // 269 // Note: This does not default to os.Args as that violates sandboxing. 270 // 271 // See https://linux.die.net/man/3/argv and https://en.wikipedia.org/wiki/Null-terminated_string 272 WithArgs(...string) ModuleConfig 273 274 // WithEnv sets an environment variable visible to a Module that imports functions. Defaults to none. 275 // Runtime.InstantiateModule errs if the key is empty or contains a NULL(0) or equals("") character. 276 // 277 // Validation is the same as os.Setenv on Linux and replaces any existing value. Unlike exec.Cmd Env, this does not 278 // default to the current process environment as that would violate sandboxing. This also does not preserve order. 279 // 280 // Environment variables are commonly read by the functions like "environ_get" in "wasi_snapshot_preview1" although 281 // they could be read by functions imported from other modules. 282 // 283 // While similar to process configuration, there are no assumptions that can be made about anything OS-specific. For 284 // example, neither WebAssembly nor WebAssembly System Interfaces (WASI) define concerns processes have, such as 285 // case-sensitivity on environment keys. For portability, define entries with case-insensitively unique keys. 286 // 287 // See https://linux.die.net/man/3/environ and https://en.wikipedia.org/wiki/Null-terminated_string 288 WithEnv(key, value string) ModuleConfig 289 290 // WithFS assigns the file system to use for any paths beginning at "/". 291 // Defaults return fs.ErrNotExist. 292 // 293 // This example sets a read-only, embedded file-system: 294 // 295 // //go:embed testdata/index.html 296 // var testdataIndex embed.FS 297 // 298 // rooted, err := fs.Sub(testdataIndex, "testdata") 299 // require.NoError(t, err) 300 // 301 // // "index.html" is accessible as "/index.html". 302 // config := wazero.NewModuleConfig().WithFS(rooted) 303 // 304 // This example sets a mutable file-system: 305 // 306 // // Files relative to "/work/appA" are accessible as "/". 307 // config := wazero.NewModuleConfig().WithFS(os.DirFS("/work/appA")) 308 // 309 // Isolation 310 // 311 // os.DirFS documentation includes important notes about isolation, which 312 // also applies to fs.Sub. As of Go 1.19, the built-in file-systems are not 313 // jailed (chroot). See https://github.com/golang/go/issues/42322 314 // 315 // Working Directory "." 316 // 317 // Relative path resolution, such as "./config.yml" to "/config.yml" or 318 // otherwise, is compiler-specific. See /RATIONALE.md for notes. 319 WithFS(fs.FS) ModuleConfig 320 321 // WithName configures the module name. Defaults to what was decoded from the name section. 322 WithName(string) ModuleConfig 323 324 // WithStartFunctions configures the functions to call after the module is 325 // instantiated. Defaults to "_start". 326 // 327 // # Notes 328 // 329 // - If any function doesn't exist, it is skipped. However, all functions 330 // that do exist are called in order. 331 // - Some start functions may exit the module during instantiate with a 332 // sys.ExitError (e.g. emscripten), preventing use of exported functions. 333 WithStartFunctions(...string) ModuleConfig 334 335 // WithStderr configures where standard error (file descriptor 2) is written. Defaults to io.Discard. 336 // 337 // This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could 338 // be used by functions imported from other modules. 339 // 340 // # Notes 341 // 342 // - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close. 343 // - This does not default to os.Stderr as that both violates sandboxing and prevents concurrent modules. 344 // 345 // See https://linux.die.net/man/3/stderr 346 WithStderr(io.Writer) ModuleConfig 347 348 // WithStdin configures where standard input (file descriptor 0) is read. Defaults to return io.EOF. 349 // 350 // This reader is most commonly used by the functions like "fd_read" in "wasi_snapshot_preview1" although it could 351 // be used by functions imported from other modules. 352 // 353 // # Notes 354 // 355 // - The caller is responsible to close any io.Reader they supply: It is not closed on api.Module Close. 356 // - This does not default to os.Stdin as that both violates sandboxing and prevents concurrent modules. 357 // 358 // See https://linux.die.net/man/3/stdin 359 WithStdin(io.Reader) ModuleConfig 360 361 // WithStdout configures where standard output (file descriptor 1) is written. Defaults to io.Discard. 362 // 363 // This writer is most commonly used by the functions like "fd_write" in "wasi_snapshot_preview1" although it could 364 // be used by functions imported from other modules. 365 // 366 // # Notes 367 // 368 // - The caller is responsible to close any io.Writer they supply: It is not closed on api.Module Close. 369 // - This does not default to os.Stdout as that both violates sandboxing and prevents concurrent modules. 370 // 371 // See https://linux.die.net/man/3/stdout 372 WithStdout(io.Writer) ModuleConfig 373 374 // WithWalltime configures the wall clock, sometimes referred to as the 375 // real time clock. Defaults to a fake result that increases by 1ms on 376 // each reading. 377 // 378 // Here's an example that uses a custom clock: 379 // moduleConfig = moduleConfig. 380 // WithWalltime(func(context.Context) (sec int64, nsec int32) { 381 // return clock.walltime() 382 // }, sys.ClockResolution(time.Microsecond.Nanoseconds())) 383 // 384 // Note: This does not default to time.Now as that violates sandboxing. Use 385 // WithSysWalltime for a usable implementation. 386 WithWalltime(sys.Walltime, sys.ClockResolution) ModuleConfig 387 388 // WithSysWalltime uses time.Now for sys.Walltime with a resolution of 1us 389 // (1000ns). 390 // 391 // See WithWalltime 392 WithSysWalltime() ModuleConfig 393 394 // WithNanotime configures the monotonic clock, used to measure elapsed 395 // time in nanoseconds. Defaults to a fake result that increases by 1ms 396 // on each reading. 397 // 398 // Here's an example that uses a custom clock: 399 // moduleConfig = moduleConfig. 400 // WithNanotime(func(context.Context) int64 { 401 // return clock.nanotime() 402 // }, sys.ClockResolution(time.Microsecond.Nanoseconds())) 403 // 404 // # Notes: 405 // - This does not default to time.Since as that violates sandboxing. 406 // - Some compilers implement sleep by looping on sys.Nanotime (e.g. Go). 407 // - If you set this, you should probably set WithNanosleep also. 408 // - Use WithSysNanotime for a usable implementation. 409 WithNanotime(sys.Nanotime, sys.ClockResolution) ModuleConfig 410 411 // WithSysNanotime uses time.Now for sys.Nanotime with a resolution of 1us. 412 // 413 // See WithNanotime 414 WithSysNanotime() ModuleConfig 415 416 // WithNanosleep configures the how to pause the current goroutine for at 417 // least the configured nanoseconds. Defaults to return immediately. 418 // 419 // This example uses a custom sleep function: 420 // moduleConfig = moduleConfig. 421 // WithNanosleep(func(ctx context.Context, ns int64) { 422 // rel := unix.NsecToTimespec(ns) 423 // remain := unix.Timespec{} 424 // for { // loop until no more time remaining 425 // err := unix.ClockNanosleep(unix.CLOCK_MONOTONIC, 0, &rel, &remain) 426 // --snip-- 427 // 428 // # Notes: 429 // - This primarily supports `poll_oneoff` for relative clock events. 430 // - This does not default to time.Sleep as that violates sandboxing. 431 // - Some compilers implement sleep by looping on sys.Nanotime (e.g. Go). 432 // - If you set this, you should probably set WithNanotime also. 433 // - Use WithSysNanosleep for a usable implementation. 434 WithNanosleep(sys.Nanosleep) ModuleConfig 435 436 // WithSysNanosleep uses time.Sleep for sys.Nanosleep. 437 // 438 // See WithNanosleep 439 WithSysNanosleep() ModuleConfig 440 441 // WithRandSource configures a source of random bytes. Defaults to return a 442 // deterministic source. You might override this with crypto/rand.Reader 443 // 444 // This reader is most commonly used by the functions like "random_get" in 445 // "wasi_snapshot_preview1", "seed" in AssemblyScript standard "env", and 446 // "getRandomData" when runtime.GOOS is "js". 447 // 448 // Note: The caller is responsible to close any io.Reader they supply: It 449 // is not closed on api.Module Close. 450 WithRandSource(io.Reader) ModuleConfig 451 } 452 453 type moduleConfig struct { 454 name string 455 startFunctions []string 456 stdin io.Reader 457 stdout io.Writer 458 stderr io.Writer 459 randSource io.Reader 460 walltime *sys.Walltime 461 walltimeResolution sys.ClockResolution 462 nanotime *sys.Nanotime 463 nanotimeResolution sys.ClockResolution 464 nanosleep *sys.Nanosleep 465 args []string 466 // environ is pair-indexed to retain order similar to os.Environ. 467 environ []string 468 // environKeys allow overwriting of existing values. 469 environKeys map[string]int 470 // fs is the file system to open files with 471 fs fs.FS 472 } 473 474 // NewModuleConfig returns a ModuleConfig that can be used for configuring module instantiation. 475 func NewModuleConfig() ModuleConfig { 476 return &moduleConfig{ 477 startFunctions: []string{"_start"}, 478 environKeys: map[string]int{}, 479 } 480 } 481 482 // clone makes a deep copy of this module config. 483 func (c *moduleConfig) clone() *moduleConfig { 484 ret := *c // copy except maps which share a ref 485 ret.environKeys = make(map[string]int, len(c.environKeys)) 486 for key, value := range c.environKeys { 487 ret.environKeys[key] = value 488 } 489 return &ret 490 } 491 492 // WithArgs implements ModuleConfig.WithArgs 493 func (c *moduleConfig) WithArgs(args ...string) ModuleConfig { 494 ret := c.clone() 495 ret.args = args 496 return ret 497 } 498 499 // WithEnv implements ModuleConfig.WithEnv 500 func (c *moduleConfig) WithEnv(key, value string) ModuleConfig { 501 ret := c.clone() 502 // Check to see if this key already exists and update it. 503 if i, ok := ret.environKeys[key]; ok { 504 ret.environ[i+1] = value // environ is pair-indexed, so the value is 1 after the key. 505 } else { 506 ret.environKeys[key] = len(ret.environ) 507 ret.environ = append(ret.environ, key, value) 508 } 509 return ret 510 } 511 512 // WithFS implements ModuleConfig.WithFS 513 func (c *moduleConfig) WithFS(fs fs.FS) ModuleConfig { 514 ret := c.clone() 515 ret.fs = fs 516 return ret 517 } 518 519 // WithName implements ModuleConfig.WithName 520 func (c *moduleConfig) WithName(name string) ModuleConfig { 521 ret := c.clone() 522 ret.name = name 523 return ret 524 } 525 526 // WithStartFunctions implements ModuleConfig.WithStartFunctions 527 func (c *moduleConfig) WithStartFunctions(startFunctions ...string) ModuleConfig { 528 ret := c.clone() 529 ret.startFunctions = startFunctions 530 return ret 531 } 532 533 // WithStderr implements ModuleConfig.WithStderr 534 func (c *moduleConfig) WithStderr(stderr io.Writer) ModuleConfig { 535 ret := c.clone() 536 ret.stderr = stderr 537 return ret 538 } 539 540 // WithStdin implements ModuleConfig.WithStdin 541 func (c *moduleConfig) WithStdin(stdin io.Reader) ModuleConfig { 542 ret := c.clone() 543 ret.stdin = stdin 544 return ret 545 } 546 547 // WithStdout implements ModuleConfig.WithStdout 548 func (c *moduleConfig) WithStdout(stdout io.Writer) ModuleConfig { 549 ret := c.clone() 550 ret.stdout = stdout 551 return ret 552 } 553 554 // WithWalltime implements ModuleConfig.WithWalltime 555 func (c *moduleConfig) WithWalltime(walltime sys.Walltime, resolution sys.ClockResolution) ModuleConfig { 556 ret := c.clone() 557 ret.walltime = &walltime 558 ret.walltimeResolution = resolution 559 return ret 560 } 561 562 // We choose arbitrary resolutions here because there's no perfect alternative. For example, according to the 563 // source in time.go, windows monotonic resolution can be 15ms. This chooses arbitrarily 1us for wall time and 564 // 1ns for monotonic. See RATIONALE.md for more context. 565 566 // WithSysWalltime implements ModuleConfig.WithSysWalltime 567 func (c *moduleConfig) WithSysWalltime() ModuleConfig { 568 return c.WithWalltime(platform.Walltime, sys.ClockResolution(time.Microsecond.Nanoseconds())) 569 } 570 571 // WithNanotime implements ModuleConfig.WithNanotime 572 func (c *moduleConfig) WithNanotime(nanotime sys.Nanotime, resolution sys.ClockResolution) ModuleConfig { 573 ret := c.clone() 574 ret.nanotime = &nanotime 575 ret.nanotimeResolution = resolution 576 return ret 577 } 578 579 // WithSysNanotime implements ModuleConfig.WithSysNanotime 580 func (c *moduleConfig) WithSysNanotime() ModuleConfig { 581 return c.WithNanotime(platform.Nanotime, sys.ClockResolution(1)) 582 } 583 584 // WithNanosleep implements ModuleConfig.WithNanosleep 585 func (c *moduleConfig) WithNanosleep(nanosleep sys.Nanosleep) ModuleConfig { 586 ret := *c // copy 587 ret.nanosleep = &nanosleep 588 return &ret 589 } 590 591 // WithSysNanosleep implements ModuleConfig.WithSysNanosleep 592 func (c *moduleConfig) WithSysNanosleep() ModuleConfig { 593 return c.WithNanosleep(platform.Nanosleep) 594 } 595 596 // WithRandSource implements ModuleConfig.WithRandSource 597 func (c *moduleConfig) WithRandSource(source io.Reader) ModuleConfig { 598 ret := c.clone() 599 ret.randSource = source 600 return ret 601 } 602 603 // toSysContext creates a baseline wasm.Context configured by ModuleConfig. 604 func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) { 605 var environ []string // Intentionally doesn't pre-allocate to reduce logic to default to nil. 606 // Same validation as syscall.Setenv for Linux 607 for i := 0; i < len(c.environ); i += 2 { 608 key, value := c.environ[i], c.environ[i+1] 609 if len(key) == 0 { 610 err = errors.New("environ invalid: empty key") 611 return 612 } 613 for j := 0; j < len(key); j++ { 614 if key[j] == '=' { // NUL enforced in NewContext 615 err = errors.New("environ invalid: key contains '=' character") 616 return 617 } 618 } 619 environ = append(environ, key+"="+value) 620 } 621 622 return internalsys.NewContext( 623 math.MaxUint32, 624 c.args, 625 environ, 626 c.stdin, 627 c.stdout, 628 c.stderr, 629 c.randSource, 630 c.walltime, c.walltimeResolution, 631 c.nanotime, c.nanotimeResolution, 632 c.nanosleep, 633 c.fs, 634 ) 635 }