diff --git a/cmd/wazero/wazero.go b/cmd/wazero/wazero.go index 03577f5b..4291da65 100644 --- a/cmd/wazero/wazero.go +++ b/cmd/wazero/wazero.go @@ -226,13 +226,6 @@ func doRun(args []string, stdOut io.Writer, stdErr logging.Writer, exit func(cod exit(1) } - if workdirInherit { - if ctx, err = gojs.WithOSWorkDir(ctx); err != nil { - fmt.Fprintf(stdErr, "cannot read current working directory: %v\n", err) - exit(1) - } - } - rt := wazero.NewRuntimeWithConfig(ctx, rtc) defer rt.Close(ctx) @@ -274,7 +267,13 @@ func doRun(args []string, stdOut io.Writer, stdErr logging.Writer, exit func(cod } case modeGo: gojs.MustInstantiate(ctx, rt) - err = gojs.Run(ctx, rt, code, conf) + + config := gojs.NewConfig(conf) + if workdirInherit { + config = config.WithOSWorkdir() + } + + err = gojs.Run(ctx, rt, code, config) case modeDefault: _, err = rt.InstantiateModule(ctx, code, conf) } diff --git a/experimental/gojs/example/stars.go b/experimental/gojs/example/stars.go index 0acd4974..dca7a6ff 100644 --- a/experimental/gojs/example/stars.go +++ b/experimental/gojs/example/stars.go @@ -44,7 +44,7 @@ func main() { log.Printf("gojs.MustInstantiate took %dms", goJsInstantiate) // Combine the above into our baseline config, overriding defaults. - config := wazero.NewModuleConfig(). + moduleConfig := wazero.NewModuleConfig(). // By default, I/O streams are discarded, so you won't see output. WithStdout(os.Stdout).WithStderr(os.Stderr) @@ -63,7 +63,7 @@ func main() { log.Printf("CompileModule took %dms with %dKB cache", compilationTime, dirSize(compilationCacheDir)/1024) // Instead of making real HTTP calls, return fake data. - ctx = gojs.WithRoundTripper(ctx, &fakeGitHub{}) + config := gojs.NewConfig(moduleConfig).WithRoundTripper(&fakeGitHub{}) // Execute the "run" function, which corresponds to "main" in stars/main.go. start = time.Now() diff --git a/experimental/gojs/example/stars_test.go b/experimental/gojs/example/stars_test.go index e84e8c13..26fcf683 100644 --- a/experimental/gojs/example/stars_test.go +++ b/experimental/gojs/example/stars_test.go @@ -80,8 +80,8 @@ func Benchmark_main(b *testing.B) { gojs.MustInstantiate(ctx, r) // Instead of making real HTTP calls, return fake data. - ctx = gojs.WithRoundTripper(ctx, &fakeGitHub{}) - cfg := wazero.NewModuleConfig() + cfg := gojs.NewConfig(wazero.NewModuleConfig()). + WithRoundTripper(&fakeGitHub{}) b.Run("gojs.Run", func(b *testing.B) { b.ReportAllocs() diff --git a/experimental/gojs/gojs.go b/experimental/gojs/gojs.go index 358a78a5..c5353dcf 100644 --- a/experimental/gojs/gojs.go +++ b/experimental/gojs/gojs.go @@ -16,14 +16,12 @@ package gojs import ( "context" "net/http" - "os" - "path/filepath" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" . "github.com/tetratelabs/wazero/internal/gojs" + internalconfig "github.com/tetratelabs/wazero/internal/gojs/config" . "github.com/tetratelabs/wazero/internal/gojs/run" - "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -97,42 +95,60 @@ func (e *functionExporter) ExportFunctions(builder wazero.HostModuleBuilder) { hfExporter.ExportHostFunc(Debug) } -// WithRoundTripper sets the http.RoundTripper used to Run Wasm. -// -// For example, if the code compiled via `GOARCH=wasm GOOS=js` uses -// http.RoundTripper, you can avoid failures by assigning an implementation -// like so: -// -// ctx = gojs.WithRoundTripper(ctx, http.DefaultTransport) -// err = gojs.Run(ctx, r, compiled, config) -func WithRoundTripper(ctx context.Context, rt http.RoundTripper) context.Context { - return context.WithValue(ctx, RoundTripperKey{}, rt) +// Config extends wazero.ModuleConfig with GOOS=js specific extensions. +// Use NewConfig to create an instance. +type Config interface { + // WithOSWorkdir sets the initial working directory used to Run Wasm to + // the value of os.Getwd instead of the default of root "/". + // + // Here's an example that overrides this to the current directory: + // + // err = gojs.Run(ctx, r, compiled, gojs.NewConfig(moduleConfig). + // WithOSWorkdir()) + // + // Note: To use this feature requires mounting the real root directory via + // wazero.FSConfig `WithDirMount`. On windows, this root must be the same drive + // as the value of os.Getwd. For example, it would be an error to mount `C:\` + // as the guest path "", while the current directory is inside `D:\`. + WithOSWorkdir() Config + + // WithRoundTripper sets the http.RoundTripper used to Run Wasm. + // + // For example, if the code compiled via `GOARCH=wasm GOOS=js` uses + // http.RoundTripper, you can avoid failures by assigning an implementation + // like so: + // + // err = gojs.Run(ctx, r, compiled, gojs.NewConfig(moduleConfig). + // WithRoundTripper(ctx, http.DefaultTransport)) + WithRoundTripper(http.RoundTripper) Config } -// WithOSWorkDir sets the initial working directory used to Run Wasm to -// the value of os.Getwd instead of the default of root "/". -// -// Here's an example that overrides this to "/usr/local/go/src/os". -// -// ctx = gojs.WithOSWorkDir(ctx) -// err = gojs.Run(ctx, r, compiled, config) -// -// Note: To use this feature requires mounting the real root directory via -// wazero.FSConfig `WithDirMount`. On windows, this root must be the same drive -// as the value of os.Getwd. For example, it would be an error to mount `C:\` -// as the guest path "", while the current directory is inside `D:\`. -func WithOSWorkDir(ctx context.Context) (context.Context, error) { - workdir, err := os.Getwd() - if err != nil { - return nil, err - } +// NewConfig returns a Config that can be used for configuring module instantiation. +func NewConfig(moduleConfig wazero.ModuleConfig) Config { + return &cfg{moduleConfig: moduleConfig, internal: internalconfig.NewConfig()} +} - // Ensure if used on windows, the input path is translated to a POSIX one. - workdir = platform.ToPosixPath(workdir) - // Strip the volume of the path, for example C:\ - workdir = workdir[len(filepath.VolumeName(workdir)):] +type cfg struct { + moduleConfig wazero.ModuleConfig + internal *internalconfig.Config +} - return context.WithValue(ctx, WorkdirKey{}, workdir), nil +func (c *cfg) clone() *cfg { + return &cfg{moduleConfig: c.moduleConfig, internal: c.internal.Clone()} +} + +// WithOSWorkdir implements Config.WithOSWorkdir +func (c *cfg) WithOSWorkdir() Config { + ret := c.clone() + ret.internal.OsWorkdir = true + return ret +} + +// WithRoundTripper implements Config.WithRoundTripper +func (c *cfg) WithRoundTripper(rt http.RoundTripper) Config { + ret := c.clone() + ret.internal.Rt = rt + return ret } // Run instantiates a new module and calls "run" with the given config. @@ -142,20 +158,21 @@ func WithOSWorkDir(ctx context.Context) (context.Context, error) { // - ctx: context to use when instantiating the module and calling "run". // - r: runtime to instantiate both the host and guest (compiled) module in. // - compiled: guest binary compiled with `GOARCH=wasm GOOS=js` -// - config: the configuration such as args, env or filesystem to use. +// - config: the Config to use including wazero.ModuleConfig or extensions of +// it. // // # Example // // After compiling your Wasm binary with wazero.Runtime's `CompileModule`, run // it like below: // -// // Use compilation cache to reduce performance penalty of multiple runs. -// ctx = experimental.WithCompilationCacheDirName(ctx, ".build") +// // Instantiate host functions needed by gojs +// gojs.MustInstantiate(ctx, r) // -// // Instantiate the host functions used for each call. -// gojs.MustInstantiate(r) +// // Assign any configuration relevant for your compiled wasm. +// config := gojs.NewConfig(wazero.NewConfig()) // -// // Execute the "run" function, which corresponds to "main" in stars/main.go. +// // Run your wasm, notably handing any ExitError // err = gojs.Run(ctx, r, compiled, config) // if exitErr, ok := err.(*sys.ExitError); ok && exitErr.ExitCode() != 0 { // log.Panicln(err) @@ -165,12 +182,12 @@ func WithOSWorkDir(ctx context.Context) (context.Context, error) { // // # Notes // -// - Use wazero.RuntimeConfig `WithWasmCore2` to avoid needing to pick >1.0 -// features set by `GOWASM` or used internally by Run. -// - Wasm generated by `GOARCH=wasm GOOS=js` is very slow to compile. -// Use experimental.WithCompilationCacheDirName to improve performance. +// - Wasm generated by `GOARCH=wasm GOOS=js` is very slow to compile: Use +// wazero.RuntimeConfig with wazero.CompilationCache when re-running the +// same binary. // - The guest module is closed after being run. -func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule, config wazero.ModuleConfig) error { - _, err := RunAndReturnState(ctx, r, compiled, config) +func Run(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule, moduleConfig Config) error { + c := moduleConfig.(*cfg) + _, err := RunAndReturnState(ctx, r, compiled, c.moduleConfig, c.internal) return err } diff --git a/experimental/gojs/gojs_test.go b/experimental/gojs/gojs_test.go deleted file mode 100644 index bb1ef995..00000000 --- a/experimental/gojs/gojs_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package gojs - -import ( - "context" - "strings" - "testing" - - "github.com/tetratelabs/wazero/internal/gojs" - "github.com/tetratelabs/wazero/internal/testing/require" -) - -func TestWithOSWorkdir(t *testing.T) { - t.Parallel() - - ctx, err := WithOSWorkDir(context.Background()) - require.NoError(t, err) - actual := ctx.Value(gojs.WorkdirKey{}).(string) - - // Check c:\ or d:\ aren't retained. - require.Equal(t, -1, strings.IndexByte(actual, '\\')) - require.Equal(t, -1, strings.IndexByte(actual, ':')) -} diff --git a/internal/gojs/argsenv_test.go b/internal/gojs/argsenv_test.go index 63c28391..615837c1 100644 --- a/internal/gojs/argsenv_test.go +++ b/internal/gojs/argsenv_test.go @@ -4,14 +4,16 @@ import ( "testing" "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/internal/gojs/config" "github.com/tetratelabs/wazero/internal/testing/require" ) func Test_argsAndEnv(t *testing.T) { t.Parallel() - stdout, stderr, err := compileAndRun(testCtx, "argsenv", wazero.NewModuleConfig(). - WithEnv("c", "d").WithEnv("a", "b")) + stdout, stderr, err := compileAndRun(testCtx, "argsenv", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) { + return moduleConfig.WithEnv("c", "d").WithEnv("a", "b"), config.NewConfig() + }) require.EqualError(t, err, `module "" closed with exit_code(0)`) require.Zero(t, stderr) diff --git a/internal/gojs/builtin.go b/internal/gojs/builtin.go index 06fe4bd5..17cfbd37 100644 --- a/internal/gojs/builtin.go +++ b/internal/gojs/builtin.go @@ -26,7 +26,7 @@ func newJsGlobal(rt http.RoundTripper) *jsVal { "fs": jsfs, "Date": jsDateConstructor, }). - addFunction("fetch", httpFetch{}) + addFunction("fetch", &httpFetch{rt}) } var ( diff --git a/internal/gojs/compiler_test.go b/internal/gojs/compiler_test.go index dbe5e81d..c7f46b95 100644 --- a/internal/gojs/compiler_test.go +++ b/internal/gojs/compiler_test.go @@ -20,13 +20,20 @@ import ( "github.com/tetratelabs/wazero/experimental/gojs" "github.com/tetratelabs/wazero/internal/fstest" internalgojs "github.com/tetratelabs/wazero/internal/gojs" + "github.com/tetratelabs/wazero/internal/gojs/config" "github.com/tetratelabs/wazero/internal/gojs/run" "github.com/tetratelabs/wazero/internal/testing/binaryencoding" "github.com/tetratelabs/wazero/internal/wasm" binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary" ) -func compileAndRun(ctx context.Context, arg string, config wazero.ModuleConfig) (stdout, stderr string, err error) { +type newConfig func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) + +func defaultConfig(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) { + return moduleConfig, config.NewConfig() +} + +func compileAndRun(ctx context.Context, arg string, config newConfig) (stdout, stderr string, err error) { rt := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig(). // https://github.com/tetratelabs/wazero/issues/992 WithMemoryCapacityFromMax(true). @@ -34,7 +41,7 @@ func compileAndRun(ctx context.Context, arg string, config wazero.ModuleConfig) return compileAndRunWithRuntime(ctx, rt, arg, config) // use global runtime } -func compileAndRunWithRuntime(ctx context.Context, r wazero.Runtime, arg string, config wazero.ModuleConfig) (stdout, stderr string, err error) { +func compileAndRunWithRuntime(ctx context.Context, r wazero.Runtime, arg string, config newConfig) (stdout, stderr string, err error) { var stdoutBuf, stderrBuf bytes.Buffer builder := r.NewHostModuleBuilder("go") @@ -49,13 +56,15 @@ func compileAndRunWithRuntime(ctx context.Context, r wazero.Runtime, arg string, log.Panicln(err) } - var s *internalgojs.State - s, err = run.RunAndReturnState(ctx, r, compiled, config. + mc, c := config(wazero.NewModuleConfig(). WithStdout(&stdoutBuf). WithStderr(&stderrBuf). WithArgs("test", arg)) + + var s *internalgojs.State + s, err = run.RunAndReturnState(ctx, r, compiled, mc, c) if err == nil { - if !reflect.DeepEqual(s, internalgojs.NewState(ctx)) { + if !reflect.DeepEqual(s, internalgojs.NewState(c)) { log.Panicf("unexpected state: %v\n", s) } } diff --git a/internal/gojs/config/config.go b/internal/gojs/config/config.go new file mode 100644 index 00000000..dab63bfe --- /dev/null +++ b/internal/gojs/config/config.go @@ -0,0 +1,42 @@ +// Package config exists to avoid dependency cycles when keeping most of gojs +// code internal. +package config + +import ( + "net/http" + "os" + "path/filepath" + + "github.com/tetratelabs/wazero/internal/platform" +) + +type Config struct { + OsWorkdir bool + Rt http.RoundTripper + + // Workdir is the actual working directory value. + Workdir string +} + +func NewConfig() *Config { + return &Config{Workdir: "/"} +} + +func (c *Config) Clone() *Config { + ret := *c // copy except maps which share a ref + return &ret +} + +func (c *Config) Init() error { + if c.OsWorkdir { + workdir, err := os.Getwd() + if err != nil { + return err + } + // Ensure if used on windows, the input path is translated to a POSIX one. + workdir = platform.ToPosixPath(workdir) + // Strip the volume of the path, for example C:\ + c.Workdir = workdir[len(filepath.VolumeName(workdir)):] + } + return nil +} diff --git a/internal/gojs/config/config_test.go b/internal/gojs/config/config_test.go new file mode 100644 index 00000000..870231b7 --- /dev/null +++ b/internal/gojs/config/config_test.go @@ -0,0 +1,22 @@ +package config + +import ( + "strings" + "testing" + + "github.com/tetratelabs/wazero/internal/testing/require" +) + +func TestConfig_Init(t *testing.T) { + t.Parallel() + + t.Run("OsWorkdir", func(t *testing.T) { + c := &Config{OsWorkdir: true} + require.NoError(t, c.Init()) + actual := c.Workdir + + // Check c:\ or d:\ aren't retained. + require.Equal(t, -1, strings.IndexByte(actual, '\\')) + require.Equal(t, -1, strings.IndexByte(actual, ':')) + }) +} diff --git a/internal/gojs/crypto_test.go b/internal/gojs/crypto_test.go index 23d9191e..c4c58194 100644 --- a/internal/gojs/crypto_test.go +++ b/internal/gojs/crypto_test.go @@ -5,7 +5,6 @@ import ( "context" "testing" - "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/internal/testing/require" @@ -18,7 +17,7 @@ func Test_crypto(t *testing.T) { loggingCtx := context.WithValue(testCtx, experimental.FunctionListenerFactoryKey{}, logging.NewHostLoggingListenerFactory(&log, logging.LogScopeRandom)) - stdout, stderr, err := compileAndRun(loggingCtx, "crypto", wazero.NewModuleConfig()) + stdout, stderr, err := compileAndRun(loggingCtx, "crypto", defaultConfig) require.Zero(t, stderr) require.EqualError(t, err, `module "" closed with exit_code(0)`) diff --git a/internal/gojs/fs.go b/internal/gojs/fs.go index 35df7bba..bc60ecd1 100644 --- a/internal/gojs/fs.go +++ b/internal/gojs/fs.go @@ -539,8 +539,8 @@ type jsfsChown struct{} func (jsfsChown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) { path := resolvePath(ctx, args[0].(string)) - uid := goos.ValueToUint32(args[1]) - gid := goos.ValueToUint32(args[2]) + uid := goos.ValueToInt32(args[1]) + gid := goos.ValueToInt32(args[2]) callback := args[3].(funcWrapper) fsc := mod.(*wasm.CallContext).Sys.FS() diff --git a/internal/gojs/fs_test.go b/internal/gojs/fs_test.go index 19fb2581..b5dae811 100644 --- a/internal/gojs/fs_test.go +++ b/internal/gojs/fs_test.go @@ -7,6 +7,7 @@ import ( "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/internal/fstest" + "github.com/tetratelabs/wazero/internal/gojs/config" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -14,7 +15,9 @@ import ( func Test_fs(t *testing.T) { t.Parallel() - stdout, stderr, err := compileAndRun(testCtx, "fs", wazero.NewModuleConfig().WithFS(testFS)) + stdout, stderr, err := compileAndRun(testCtx, "fs", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) { + return defaultConfig(moduleConfig.WithFS(testFS)) + }) require.Zero(t, stderr) require.EqualError(t, err, `module "" closed with exit_code(0)`) @@ -44,7 +47,9 @@ func Test_testfs(t *testing.T) { require.NoError(t, fstest.WriteTestFiles(testfsDir)) fsConfig := wazero.NewFSConfig().WithDirMount(tmpDir, "/") - stdout, stderr, err := compileAndRun(testCtx, "testfs", wazero.NewModuleConfig().WithFSConfig(fsConfig)) + stdout, stderr, err := compileAndRun(testCtx, "testfs", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) { + return defaultConfig(moduleConfig.WithFSConfig(fsConfig)) + }) require.Zero(t, stderr) require.EqualError(t, err, `module "" closed with exit_code(0)`) @@ -59,7 +64,9 @@ func Test_writefs(t *testing.T) { // test expects to write under /tmp require.NoError(t, os.Mkdir(path.Join(tmpDir, "tmp"), 0o700)) - stdout, stderr, err := compileAndRun(testCtx, "writefs", wazero.NewModuleConfig().WithFSConfig(fsConfig)) + stdout, stderr, err := compileAndRun(testCtx, "writefs", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) { + return defaultConfig(moduleConfig.WithFSConfig(fsConfig)) + }) require.Zero(t, stderr) require.EqualError(t, err, `module "" closed with exit_code(0)`) diff --git a/internal/gojs/goos/goos.go b/internal/gojs/goos/goos.go index 3f7eb0ac..c56ab0d3 100644 --- a/internal/gojs/goos/goos.go +++ b/internal/gojs/goos/goos.go @@ -265,6 +265,15 @@ func ValueToUint32(arg interface{}) uint32 { return uint32(arg.(float64)) } +func ValueToInt32(arg interface{}) int32 { + if arg == RefValueZero || arg == Undefined { + return 0 + } else if u, ok := arg.(int); ok { + return int32(u) + } + return int32(uint32(arg.(float64))) +} + // GetFunction allows getting a JavaScript property by name. type GetFunction interface { Get(ctx context.Context, propertyKey string) interface{} diff --git a/internal/gojs/http.go b/internal/gojs/http.go index 11434a76..1026d0d7 100644 --- a/internal/gojs/http.go +++ b/internal/gojs/http.go @@ -15,15 +15,6 @@ import ( // headersConstructor = Get("Headers").New() // http.Roundtrip && "fetch" var headersConstructor = newJsVal(goos.RefHttpHeadersConstructor, "Headers") -type RoundTripperKey struct{} - -func getRoundTripper(ctx context.Context) http.RoundTripper { - if rt, ok := ctx.Value(RoundTripperKey{}).(http.RoundTripper); ok { - return rt - } - return nil -} - // httpFetch implements jsFn for http.RoundTripper // // Reference in roundtrip_js.go init @@ -33,10 +24,10 @@ func getRoundTripper(ctx context.Context) http.RoundTripper { // In http.Transport RoundTrip, this returns a promise // // fetchPromise := js.Global().Call("fetch", req.URL.String(), opt) -type httpFetch struct{} +type httpFetch struct{ rt http.RoundTripper } -func (httpFetch) invoke(ctx context.Context, _ api.Module, args ...interface{}) (interface{}, error) { - rt := getRoundTripper(ctx) +func (h *httpFetch) invoke(ctx context.Context, _ api.Module, args ...interface{}) (interface{}, error) { + rt := h.rt if rt == nil { panic("unexpected to reach here without roundtripper as property is nil checked") } diff --git a/internal/gojs/http_test.go b/internal/gojs/http_test.go index 86bd2999..c8cc2dbe 100644 --- a/internal/gojs/http_test.go +++ b/internal/gojs/http_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/tetratelabs/wazero" - "github.com/tetratelabs/wazero/experimental/gojs" + "github.com/tetratelabs/wazero/internal/gojs/config" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -21,7 +21,7 @@ func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { func Test_http(t *testing.T) { t.Parallel() - ctx := gojs.WithRoundTripper(testCtx, roundTripperFunc(func(req *http.Request) (*http.Response, error) { + rt := roundTripperFunc(func(req *http.Request) (*http.Response, error) { if req.URL.Path == "/error" { return nil, errors.New("error") } @@ -38,10 +38,13 @@ func Test_http(t *testing.T) { Body: io.NopCloser(strings.NewReader("abcdef")), ContentLength: 6, }, nil - })) + }) - stdout, stderr, err := compileAndRun(ctx, "http", wazero.NewModuleConfig(). - WithEnv("BASE_URL", "http://host")) + stdout, stderr, err := compileAndRun(testCtx, "http", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) { + return moduleConfig.WithEnv("BASE_URL", "http://host"), &config.Config{ + Rt: rt, + } + }) require.EqualError(t, err, `module "" closed with exit_code(0)`) require.Zero(t, stderr) diff --git a/internal/gojs/logging/logging.go b/internal/gojs/logging/logging.go index 9064ca26..744558d2 100644 --- a/internal/gojs/logging/logging.go +++ b/internal/gojs/logging/logging.go @@ -301,6 +301,11 @@ func writeVal(w logging.Writer, name string, val interface{}) { return } switch name { + case "uid", "gid": + w.WriteString(name) //nolint + w.WriteByte('=') //nolint + id := int(goos.ValueToInt32(val)) + w.WriteString(strconv.Itoa(id)) //nolint case "mask", "mode", "oldmask", "perm": w.WriteString(name) //nolint w.WriteByte('=') //nolint diff --git a/internal/gojs/misc_test.go b/internal/gojs/misc_test.go index ae1bfbf1..e3ca36a6 100644 --- a/internal/gojs/misc_test.go +++ b/internal/gojs/misc_test.go @@ -10,6 +10,7 @@ import ( "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/experimental/logging" + "github.com/tetratelabs/wazero/internal/gojs/config" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -20,7 +21,7 @@ func Test_exit(t *testing.T) { loggingCtx := context.WithValue(testCtx, experimental.FunctionListenerFactoryKey{}, logging.NewHostLoggingListenerFactory(&log, logging.LogScopeProc)) - stdout, stderr, err := compileAndRun(loggingCtx, "exit", wazero.NewModuleConfig()) + stdout, stderr, err := compileAndRun(loggingCtx, "exit", defaultConfig) require.EqualError(t, err, `module "" closed with exit_code(255)`) require.Zero(t, stderr) @@ -33,7 +34,7 @@ func Test_exit(t *testing.T) { func Test_goroutine(t *testing.T) { t.Parallel() - stdout, stderr, err := compileAndRun(testCtx, "goroutine", wazero.NewModuleConfig()) + stdout, stderr, err := compileAndRun(testCtx, "goroutine", defaultConfig) require.EqualError(t, err, `module "" closed with exit_code(0)`) require.Zero(t, stderr) @@ -49,7 +50,7 @@ func Test_mem(t *testing.T) { loggingCtx := context.WithValue(testCtx, experimental.FunctionListenerFactoryKey{}, logging.NewHostLoggingListenerFactory(&log, logging.LogScopeMemory)) - stdout, stderr, err := compileAndRun(loggingCtx, "mem", wazero.NewModuleConfig()) + stdout, stderr, err := compileAndRun(loggingCtx, "mem", defaultConfig) require.EqualError(t, err, `module "" closed with exit_code(0)`) require.Zero(t, stderr) @@ -65,8 +66,9 @@ func Test_stdio(t *testing.T) { t.Parallel() input := "stdin\n" - stdout, stderr, err := compileAndRun(testCtx, "stdio", wazero.NewModuleConfig(). - WithStdin(strings.NewReader(input))) + stdout, stderr, err := compileAndRun(testCtx, "stdio", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) { + return defaultConfig(moduleConfig.WithStdin(strings.NewReader(input))) + }) require.Equal(t, "stderr 6\n", stderr) require.EqualError(t, err, `module "" closed with exit_code(0)`) @@ -83,8 +85,9 @@ func Test_stdio_large(t *testing.T) { size := 2 * 1024 * 1024 // 2MB input := make([]byte, size) - stdout, stderr, err := compileAndRun(loggingCtx, "stdio", wazero.NewModuleConfig(). - WithStdin(bytes.NewReader(input))) + stdout, stderr, err := compileAndRun(loggingCtx, "stdio", func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) { + return defaultConfig(moduleConfig.WithStdin(bytes.NewReader(input))) + }) require.EqualError(t, err, `module "" closed with exit_code(0)`) require.Equal(t, fmt.Sprintf("stderr %d\n", size), stderr) @@ -102,7 +105,7 @@ func Test_stdio_large(t *testing.T) { func Test_gc(t *testing.T) { t.Parallel() - stdout, stderr, err := compileAndRun(testCtx, "gc", wazero.NewModuleConfig()) + stdout, stderr, err := compileAndRun(testCtx, "gc", defaultConfig) require.EqualError(t, err, `module "" closed with exit_code(0)`) require.Equal(t, "", stderr) diff --git a/internal/gojs/run/gojs.go b/internal/gojs/run/gojs.go index 06143187..1f9745a1 100644 --- a/internal/gojs/run/gojs.go +++ b/internal/gojs/run/gojs.go @@ -7,25 +7,35 @@ import ( "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/internal/gojs" + "github.com/tetratelabs/wazero/internal/gojs/config" ) -func RunAndReturnState(ctx context.Context, r wazero.Runtime, compiled wazero.CompiledModule, config wazero.ModuleConfig) (*gojs.State, error) { - // Instantiate the module compiled by go, noting it has no init function. +func RunAndReturnState( + ctx context.Context, + r wazero.Runtime, + compiled wazero.CompiledModule, + moduleConfig wazero.ModuleConfig, + config *config.Config, +) (*gojs.State, error) { + if err := config.Init(); err != nil { + return nil, err + } - mod, err := r.InstantiateModule(ctx, compiled, config) + // Instantiate the module compiled by go, noting it has no init function. + mod, err := r.InstantiateModule(ctx, compiled, moduleConfig) if err != nil { return nil, err } defer mod.Close(ctx) - // Extract the args and env from the module config and write it to memory. + // Extract the args and env from the module Config and write it to memory. argc, argv, err := gojs.WriteArgsAndEnviron(mod) if err != nil { return nil, err } // Create host-side state for JavaScript values and events. - s := gojs.NewState(ctx) + s := gojs.NewState(config) ctx = context.WithValue(ctx, gojs.StateKey{}, s) // Invoke the run function. diff --git a/internal/gojs/state.go b/internal/gojs/state.go index f089586f..e0380a53 100644 --- a/internal/gojs/state.go +++ b/internal/gojs/state.go @@ -6,24 +6,16 @@ import ( "math" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/internal/gojs/config" "github.com/tetratelabs/wazero/internal/gojs/goos" "github.com/tetratelabs/wazero/internal/gojs/values" ) -type WorkdirKey struct{} - -func getWorkdir(ctx context.Context) string { - if wd, ok := ctx.Value(WorkdirKey{}).(string); ok { - return wd - } - return "/" -} - -func NewState(ctx context.Context) *State { +func NewState(config *config.Config) *State { return &State{ values: values.NewValues(), - valueGlobal: newJsGlobal(getRoundTripper(ctx)), - cwd: getWorkdir(ctx), + valueGlobal: newJsGlobal(config.Rt), + cwd: config.Workdir, umask: 0o0022, _nextCallbackTimeoutID: 1, _scheduledTimeouts: map[uint32]chan bool{}, diff --git a/internal/gojs/syscall_test.go b/internal/gojs/syscall_test.go index 19d01738..e2b81df7 100644 --- a/internal/gojs/syscall_test.go +++ b/internal/gojs/syscall_test.go @@ -3,14 +3,13 @@ package gojs_test import ( "testing" - "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/internal/testing/require" ) func Test_syscall(t *testing.T) { t.Parallel() - stdout, stderr, err := compileAndRun(testCtx, "syscall", wazero.NewModuleConfig()) + stdout, stderr, err := compileAndRun(testCtx, "syscall", defaultConfig) require.EqualError(t, err, `module "" closed with exit_code(0)`) require.Zero(t, stderr) diff --git a/internal/gojs/time_test.go b/internal/gojs/time_test.go index be7fec58..3e83fbca 100644 --- a/internal/gojs/time_test.go +++ b/internal/gojs/time_test.go @@ -5,7 +5,6 @@ import ( "context" "testing" - "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/experimental/logging" "github.com/tetratelabs/wazero/internal/testing/require" @@ -18,7 +17,7 @@ func Test_time(t *testing.T) { loggingCtx := context.WithValue(testCtx, experimental.FunctionListenerFactoryKey{}, logging.NewHostLoggingListenerFactory(&log, logging.LogScopeClock)) - stdout, stderr, err := compileAndRun(loggingCtx, "time", wazero.NewModuleConfig()) + stdout, stderr, err := compileAndRun(loggingCtx, "time", defaultConfig) require.EqualError(t, err, `module "" closed with exit_code(0)`) require.Zero(t, stderr)