From fe1cde140d0cdd612ba87d4fb83393ed8af5f13a Mon Sep 17 00:00:00 2001 From: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com> Date: Thu, 30 Jun 2022 07:33:24 +0800 Subject: [PATCH] Removes redundant error handling (#668) This consolidates to use EBADF in places go uses it in syscalls to reduce where we formally returned both bool and err. This also removes the redundant panic type handling as go will already panic with a similar message. Signed-off-by: Adrian Cole --- assemblyscript/assemblyscript.go | 19 ++---- assemblyscript/assemblyscript_test.go | 4 +- builder.go | 5 +- experimental/fs_example_test.go | 2 +- internal/integration_test/vs/runtime.go | 2 +- internal/sys/fs.go | 58 +++++++++++++--- internal/sys/sys.go | 6 +- internal/wasm/call_context.go | 4 +- namespace.go | 13 +--- runtime.go | 12 +--- runtime_test.go | 39 ----------- wasi_snapshot_preview1/clock.go | 5 +- wasi_snapshot_preview1/poll.go | 10 ++- wasi_snapshot_preview1/poll_test.go | 3 +- wasi_snapshot_preview1/wasi.go | 89 +++++++------------------ wasi_snapshot_preview1/wasi_test.go | 4 +- 16 files changed, 103 insertions(+), 172 deletions(-) diff --git a/assemblyscript/assemblyscript.go b/assemblyscript/assemblyscript.go index aff0eb6a..69baacbd 100644 --- a/assemblyscript/assemblyscript.go +++ b/assemblyscript/assemblyscript.go @@ -27,7 +27,6 @@ import ( "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/ieee754" - internalsys "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -156,7 +155,7 @@ func (a *assemblyscript) abort( columnNumber uint32, ) { if !a.abortMessageDisabled { - sysCtx := getSysCtx(mod) + sysCtx := mod.(*wasm.CallContext).Sys msg, err := readAssemblyScriptString(ctx, mod, message) if err != nil { return @@ -184,9 +183,9 @@ func (a *assemblyscript) trace( case traceDisabled: return case traceStdout: - writer = getSysCtx(mod).Stdout() + writer = mod.(*wasm.CallContext).Sys.Stdout() case traceStderr: - writer = getSysCtx(mod).Stderr() + writer = mod.(*wasm.CallContext).Sys.Stderr() } msg, err := readAssemblyScriptString(ctx, mod, message) @@ -234,10 +233,10 @@ func formatFloat(f float64) string { // // See https://github.com/AssemblyScript/assemblyscript/blob/fa14b3b03bd4607efa52aaff3132bea0c03a7989/std/assembly/wasi/index.ts#L111 func (a *assemblyscript) seed(mod api.Module) float64 { - randSource := getSysCtx(mod).RandSource() + randSource := mod.(*wasm.CallContext).Sys.RandSource() v, err := ieee754.DecodeFloat64(randSource) if err != nil { - panic(fmt.Errorf("error reading Module.RandSource: %w", err)) + panic(fmt.Errorf("error reading random seed: %w", err)) } return v } @@ -269,11 +268,3 @@ func decodeUTF16(b []byte) string { return string(utf16.Decode(u16s)) } - -func getSysCtx(mod api.Module) *internalsys.Context { - if internal, ok := mod.(*wasm.CallContext); !ok { - panic(fmt.Errorf("unsupported wasm.Module implementation: %v", mod)) - } else { - return internal.Sys - } -} diff --git a/assemblyscript/assemblyscript_test.go b/assemblyscript/assemblyscript_test.go index 2266b215..0c4e6d85 100644 --- a/assemblyscript/assemblyscript_test.go +++ b/assemblyscript/assemblyscript_test.go @@ -371,14 +371,14 @@ func TestSeed_error(t *testing.T) { { name: "not 8 bytes", source: bytes.NewReader([]byte{0, 1}), - expectedErr: `error reading Module.RandSource: unexpected EOF (recovered by wazero) + expectedErr: `error reading random seed: unexpected EOF (recovered by wazero) wasm stack trace: env.seed() f64`, }, { name: "error reading", source: iotest.ErrReader(errors.New("ice cream")), - expectedErr: `error reading Module.RandSource: ice cream (recovered by wazero) + expectedErr: `error reading random seed: ice cream (recovered by wazero) wasm stack trace: env.seed() f64`, }, diff --git a/builder.go b/builder.go index 74aa50e1..60bac687 100644 --- a/builder.go +++ b/builder.go @@ -281,10 +281,7 @@ func (b *moduleBuilder) ExportGlobalF64(name string, v float64) ModuleBuilder { // Compile implements ModuleBuilder.Compile func (b *moduleBuilder) Compile(ctx context.Context, cConfig CompileConfig) (CompiledModule, error) { - config, ok := cConfig.(*compileConfig) - if !ok { - panic(fmt.Errorf("unsupported wazero.CompileConfig implementation: %#v", cConfig)) - } + config := cConfig.(*compileConfig) // Verify the maximum limit here, so we don't have to pass it to wasm.NewHostModule for name, mem := range b.nameToMemory { diff --git a/experimental/fs_example_test.go b/experimental/fs_example_test.go index ebaaf079..608d024c 100644 --- a/experimental/fs_example_test.go +++ b/experimental/fs_example_test.go @@ -57,7 +57,7 @@ func Example_withFS() { // Try to read the path! if path, ok := mod.Memory().Read(ctx, uint32(pathOffset), uint32(pathLen)); !ok { - log.Panicf("Memory.Read(%d,%d) out of range of memory size %d", pathOffset, pathLen, mod.Memory().Size(ctx)) + log.Panicln("out of memory reading path") } else { fmt.Println(string(path)) } diff --git a/internal/integration_test/vs/runtime.go b/internal/integration_test/vs/runtime.go index 4214db46..9292a30d 100644 --- a/internal/integration_test/vs/runtime.go +++ b/internal/integration_test/vs/runtime.go @@ -70,7 +70,7 @@ func (r *wazeroRuntime) Name() string { func (r *wazeroRuntime) log(ctx context.Context, m api.Module, offset, byteCount uint32) { buf, ok := m.Memory().Read(ctx, offset, byteCount) if !ok { - panic(fmt.Errorf("Memory.Read(%d, %d) out of range", offset, byteCount)) + panic("out of memory reading log buffer") } if err := r.logFn(buf); err != nil { panic(err) diff --git a/internal/sys/fs.go b/internal/sys/fs.go index 9249f831..df47485a 100644 --- a/internal/sys/fs.go +++ b/internal/sys/fs.go @@ -2,10 +2,17 @@ package sys import ( "context" - "errors" + "io" "io/fs" "math" "sync/atomic" + "syscall" +) + +const ( + FdStdin = iota + FdStdout + FdStderr ) // FSKey is a context.Context Value key. It allows overriding fs.FS for WASI. @@ -81,7 +88,7 @@ func (c *FSContext) nextFD() uint32 { return atomic.AddUint32(&c.lastFD, 1) } -// OpenedFile returns a file and true if it was opened or nil and false, if not. +// OpenedFile returns a file and true if it was opened or nil and false, if syscall.EBADF. func (c *FSContext) OpenedFile(_ context.Context, fd uint32) (*FileEntry, bool) { f, ok := c.openedFiles[fd] return f, ok @@ -107,27 +114,27 @@ func (c *FSContext) OpenFile(_ context.Context, name string /* TODO: flags int, newFD := c.nextFD() if newFD == 0 { _ = f.Close() - return 0, &fs.PathError{Op: "open", Path: name, Err: errors.New("out of file descriptors")} + return 0, syscall.EBADF } c.openedFiles[newFD] = &FileEntry{Path: name, File: f} return newFD, nil } -// CloseFile returns true if a file was opened and closed without error, or false if not. -func (c *FSContext) CloseFile(_ context.Context, fd uint32) (bool, error) { +// CloseFile returns true if a file was opened and closed without error, or false if syscall.EBADF. +func (c *FSContext) CloseFile(_ context.Context, fd uint32) bool { f, ok := c.openedFiles[fd] if !ok { - return false, nil + return false } delete(c.openedFiles, fd) - if f.File == nil { // TODO: currently, this means it is a pre-opened filesystem, but this may change later. - return true, nil + if f.File == nil { // The root entry + return true } if err := f.File.Close(); err != nil { - return false, err + return false } - return true, nil + return true } // Close implements io.Closer @@ -143,3 +150,34 @@ func (c *FSContext) Close(_ context.Context) (err error) { } return } + +// FdWriter returns a valid writer for the given file descriptor or nil if syscall.EBADF. +func FdWriter(ctx context.Context, sysCtx *Context, fd uint32) io.Writer { + switch fd { + case FdStdout: + return sysCtx.Stdout() + case FdStderr: + return sysCtx.Stderr() + default: + // Check to see if the file descriptor is available + if f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok || f.File == nil { + return nil + } else if writer, ok := f.File.(io.Writer); !ok { + // Go's syscall.Write also returns EBADF if the FD is present, but not writeable + return nil + } else { + return writer + } + } +} + +// FdReader returns a valid reader for the given file descriptor or nil if syscall.EBADF. +func FdReader(ctx context.Context, sysCtx *Context, fd uint32) io.Reader { + if fd == FdStdin { + return sysCtx.Stdin() + } else if f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok { + return nil + } else { + return f.File + } +} diff --git a/internal/sys/sys.go b/internal/sys/sys.go index c33651bf..663773e0 100644 --- a/internal/sys/sys.go +++ b/internal/sys/sys.go @@ -113,11 +113,7 @@ func (c *Context) Nanosleep(ctx context.Context, ns int64) { func (c *Context) FS(ctx context.Context) *FSContext { // Override Context when it is passed via context if fsValue := ctx.Value(FSKey{}); fsValue != nil { - fsCtx, ok := fsValue.(*FSContext) - if !ok { - panic(fmt.Errorf("unsupported fs key: %v", fsValue)) - } - return fsCtx + return fsValue.(*FSContext) } return c.fsc } diff --git a/internal/wasm/call_context.go b/internal/wasm/call_context.go index b14a6680..f00cfdce 100644 --- a/internal/wasm/call_context.go +++ b/internal/wasm/call_context.go @@ -13,9 +13,9 @@ import ( // compile time check to ensure CallContext implements api.Module var _ api.Module = &CallContext{} -func NewCallContext(ns *Namespace, instance *ModuleInstance, Sys *internalsys.Context) *CallContext { +func NewCallContext(ns *Namespace, instance *ModuleInstance, sys *internalsys.Context) *CallContext { zero := uint64(0) - return &CallContext{memory: instance.Memory, module: instance, ns: ns, Sys: Sys, closed: &zero} + return &CallContext{memory: instance.Memory, module: instance, ns: ns, Sys: sys, closed: &zero} } // CallContext is a function call context bound to a module. This is important as one module's functions can call diff --git a/namespace.go b/namespace.go index 3d0638e5..8844be60 100644 --- a/namespace.go +++ b/namespace.go @@ -67,15 +67,8 @@ func (ns *namespace) InstantiateModule( compiled CompiledModule, mConfig ModuleConfig, ) (mod api.Module, err error) { - code, ok := compiled.(*compiledModule) - if !ok { - panic(fmt.Errorf("unsupported wazero.CompiledModule implementation: %#v", compiled)) - } - - config, ok := mConfig.(*moduleConfig) - if !ok { - panic(fmt.Errorf("unsupported wazero.ModuleConfig implementation: %#v", mConfig)) - } + code := compiled.(*compiledModule) + config := mConfig.(*moduleConfig) var sysCtx *internalsys.Context if sysCtx, err = config.toSysContext(); err != nil { @@ -117,7 +110,7 @@ func (ns *namespace) InstantiateModule( } if _, err = start.Call(ctx); err != nil { _ = mod.Close(ctx) // Don't leak the module on error. - if _, ok = err.(*sys.ExitError); ok { + if _, ok := err.(*sys.ExitError); ok { return // Don't wrap an exit error } err = fmt.Errorf("module[%s] function[%s] failed: %w", name, fn, err) diff --git a/runtime.go b/runtime.go index 11056caf..5ac81e59 100644 --- a/runtime.go +++ b/runtime.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "errors" - "fmt" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/wasm" @@ -125,10 +124,7 @@ func NewRuntime() Runtime { // NewRuntimeWithConfig returns a runtime with the given configuration. func NewRuntimeWithConfig(rConfig RuntimeConfig) Runtime { - config, ok := rConfig.(*runtimeConfig) - if !ok { - panic(fmt.Errorf("unsupported wazero.RuntimeConfig implementation: %#v", rConfig)) - } + config := rConfig.(*runtimeConfig) store, ns := wasm.NewStore(config.enabledFeatures, config.newEngine(config.enabledFeatures)) return &runtime{ store: store, @@ -164,11 +160,7 @@ func (r *runtime) CompileModule(ctx context.Context, binary []byte, cConfig Comp return nil, errors.New("binary == nil") } - config, ok := cConfig.(*compileConfig) - if !ok { - panic(fmt.Errorf("unsupported wazero.CompileConfig implementation: %#v", cConfig)) - } - + config := cConfig.(*compileConfig) if len(binary) < 4 || !bytes.Equal(binary[0:4], binaryformat.Magic) { return nil, errors.New("invalid binary") } diff --git a/runtime_test.go b/runtime_test.go index dfd7dbe9..08b41e78 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -23,16 +23,6 @@ var ( zero = wasm.Index(0) ) -func TestNewRuntimeWithConfig_PanicsOnWrongImpl(t *testing.T) { - // It causes maintenance to define an impl of RuntimeConfig in tests just to verify the error when it is wrong. - // Instead, we pass nil which is implicitly the wrong type, as that's less work! - err := require.CapturePanic(func() { - NewRuntimeWithConfig(nil) - }) - - require.EqualError(t, err, "unsupported wazero.RuntimeConfig implementation: ") -} - func TestRuntime_CompileModule(t *testing.T) { tests := []struct { name string @@ -516,35 +506,6 @@ func TestRuntime_InstantiateModuleFromBinary_ErrorOnStart(t *testing.T) { } } -func TestRuntime_InstantiateModule_PanicsOnWrongCompiledCodeImpl(t *testing.T) { - // It causes maintenance to define an impl of CompiledModule in tests just to verify the error when it is wrong. - // Instead, we pass nil which is implicitly the wrong type, as that's less work! - r := NewRuntime() - defer r.Close(testCtx) - - err := require.CapturePanic(func() { - _, _ = r.InstantiateModule(testCtx, nil, NewModuleConfig()) - }) - - require.EqualError(t, err, "unsupported wazero.CompiledModule implementation: ") -} - -func TestRuntime_InstantiateModule_PanicsOnWrongModuleConfigImpl(t *testing.T) { - r := NewRuntime() - defer r.Close(testCtx) - - code, err := r.CompileModule(testCtx, binaryformat.EncodeModule(&wasm.Module{}), NewCompileConfig()) - require.NoError(t, err) - - // It causes maintenance to define an impl of ModuleConfig in tests just to verify the error when it is wrong. - // Instead, we pass nil which is implicitly the wrong type, as that's less work! - err = require.CapturePanic(func() { - _, _ = r.InstantiateModule(testCtx, code, nil) - }) - - require.EqualError(t, err, "unsupported wazero.ModuleConfig implementation: ") -} - // TestRuntime_InstantiateModule_WithName tests that we can pre-validate (cache) a module and instantiate it under // different names. This pattern is used in wapc-go. func TestRuntime_InstantiateModule_WithName(t *testing.T) { diff --git a/wasi_snapshot_preview1/clock.go b/wasi_snapshot_preview1/clock.go index 09f44ec9..a2071e0d 100644 --- a/wasi_snapshot_preview1/clock.go +++ b/wasi_snapshot_preview1/clock.go @@ -5,6 +5,7 @@ import ( "time" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/internal/wasm" ) const ( @@ -56,7 +57,7 @@ const ( // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_res_getid-clockid---errno-timestamp // See https://linux.die.net/man/3/clock_getres func (a *wasi) ClockResGet(ctx context.Context, mod api.Module, id uint32, resultResolution uint32) Errno { - sysCtx := getSysCtx(mod) + sysCtx := mod.(*wasm.CallContext).Sys var resolution uint64 // ns switch id { @@ -99,7 +100,7 @@ func (a *wasi) ClockResGet(ctx context.Context, mod api.Module, id uint32, resul // See https://linux.die.net/man/3/clock_gettime func (a *wasi) ClockTimeGet(ctx context.Context, mod api.Module, id uint32, precision uint64, resultTimestamp uint32) Errno { // TODO: precision is currently ignored. - sysCtx := getSysCtx(mod) + sysCtx := mod.(*wasm.CallContext).Sys var val uint64 switch id { diff --git a/wasi_snapshot_preview1/poll.go b/wasi_snapshot_preview1/poll.go index 812f93bc..68622f8f 100644 --- a/wasi_snapshot_preview1/poll.go +++ b/wasi_snapshot_preview1/poll.go @@ -5,6 +5,8 @@ import ( "encoding/binary" "github.com/tetratelabs/wazero/api" + internalsys "github.com/tetratelabs/wazero/internal/sys" + "github.com/tetratelabs/wazero/internal/wasm" ) // https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-eventtype-enumu8 @@ -114,7 +116,8 @@ func processClockEvent(ctx context.Context, mod api.Module, inBuf []byte) Errno // unaffected. Since this function only supports relative timeout, we can // skip clock ID validation and use a single sleep function. - getSysCtx(mod).Nanosleep(ctx, int64(timeout)) + sysCtx := mod.(*wasm.CallContext).Sys + sysCtx.Nanosleep(ctx, int64(timeout)) return ErrnoSuccess } @@ -122,12 +125,13 @@ func processClockEvent(ctx context.Context, mod api.Module, inBuf []byte) Errno // subscriptions are not yet supported. func processFDEvent(ctx context.Context, mod api.Module, eventType byte, inBuf []byte) Errno { fd := binary.LittleEndian.Uint32(inBuf) + sysCtx := mod.(*wasm.CallContext).Sys // Choose the best error, which falls back to unsupported, until we support files. errno := ErrnoNotsup - if eventType == eventTypeFdRead && fdReader(ctx, mod, fd) == nil { + if eventType == eventTypeFdRead && internalsys.FdReader(ctx, sysCtx, fd) == nil { errno = ErrnoBadf - } else if eventType == eventTypeFdWrite && fdWriter(ctx, mod, fd) == nil { + } else if eventType == eventTypeFdWrite && internalsys.FdWriter(ctx, sysCtx, fd) == nil { errno = ErrnoBadf } diff --git a/wasi_snapshot_preview1/poll_test.go b/wasi_snapshot_preview1/poll_test.go index 69e3091e..12833342 100644 --- a/wasi_snapshot_preview1/poll_test.go +++ b/wasi_snapshot_preview1/poll_test.go @@ -3,6 +3,7 @@ package wasi_snapshot_preview1 import ( "testing" + internalsys "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -121,7 +122,7 @@ func Test_PollOneoff_Errors(t *testing.T) { mem: []byte{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata eventTypeFdRead, 0x0, 0x0, 0x0, - fdStdin, 0x0, 0x0, 0x0, // valid readable FD + internalsys.FdStdin, 0x0, 0x0, 0x0, // valid readable FD '?', // stopped after encoding }, expectedErrno: ErrnoSuccess, diff --git a/wasi_snapshot_preview1/wasi.go b/wasi_snapshot_preview1/wasi.go index c33469eb..de00eefc 100644 --- a/wasi_snapshot_preview1/wasi.go +++ b/wasi_snapshot_preview1/wasi.go @@ -17,7 +17,6 @@ package wasi_snapshot_preview1 import ( "context" "errors" - "fmt" "io" "io/fs" @@ -424,12 +423,6 @@ const ( (func $wasi.sock_shutdown (param $fd i32) (param $how i32) (result (;errno;) i32)))` ) -const ( - fdStdin = iota - fdStdout - fdStderr -) - // wasi includes all host functions to export for WASI version "wasi_snapshot_preview1". // // ## Translation notes @@ -542,7 +535,7 @@ func wasiFunctions() map[string]interface{} { // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#args_get // See https://en.wikipedia.org/wiki/Null-terminated_string func (a *wasi) ArgsGet(ctx context.Context, mod api.Module, argv, argvBuf uint32) Errno { - sysCtx := getSysCtx(mod) + sysCtx := mod.(*wasm.CallContext).Sys return writeOffsetsAndNullTerminatedValues(ctx, mod.Memory(), sysCtx.Args(), argv, argvBuf) } @@ -573,7 +566,7 @@ func (a *wasi) ArgsGet(ctx context.Context, mod api.Module, argv, argvBuf uint32 // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#args_sizes_get // See https://en.wikipedia.org/wiki/Null-terminated_string func (a *wasi) ArgsSizesGet(ctx context.Context, mod api.Module, resultArgc, resultArgvBufSize uint32) Errno { - sysCtx := getSysCtx(mod) + sysCtx := mod.(*wasm.CallContext).Sys mem := mod.Memory() if !mem.WriteUint32Le(ctx, resultArgc, uint32(len(sysCtx.Args()))) { @@ -612,8 +605,8 @@ func (a *wasi) ArgsSizesGet(ctx context.Context, mod api.Module, resultArgc, res // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#environ_get // See https://en.wikipedia.org/wiki/Null-terminated_string func (a *wasi) EnvironGet(ctx context.Context, mod api.Module, environ uint32, environBuf uint32) Errno { - env := getSysCtx(mod).Environ() - return writeOffsetsAndNullTerminatedValues(ctx, mod.Memory(), env, environ, environBuf) + sysCtx := mod.(*wasm.CallContext).Sys + return writeOffsetsAndNullTerminatedValues(ctx, mod.Memory(), sysCtx.Environ(), environ, environBuf) } // EnvironSizesGet is the WASI function named functionEnvironSizesGet that reads environment variable @@ -644,7 +637,7 @@ func (a *wasi) EnvironGet(ctx context.Context, mod api.Module, environ uint32, e // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#environ_sizes_get // See https://en.wikipedia.org/wiki/Null-terminated_string func (a *wasi) EnvironSizesGet(ctx context.Context, mod api.Module, resultEnvironc uint32, resultEnvironBufSize uint32) Errno { - sysCtx := getSysCtx(mod) + sysCtx := mod.(*wasm.CallContext).Sys mem := mod.Memory() if !mem.WriteUint32Le(ctx, resultEnvironc, uint32(len(sysCtx.Environ()))) { @@ -676,9 +669,8 @@ func (a *wasi) FdAllocate(ctx context.Context, mod api.Module, fd uint32, offset // See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_close // See https://linux.die.net/man/3/close func (a *wasi) FdClose(ctx context.Context, mod api.Module, fd uint32) Errno { - if ok, err := getSysCtx(mod).FS(ctx).CloseFile(ctx, fd); err != nil { - return ErrnoIo - } else if !ok { + sysCtx := mod.(*wasm.CallContext).Sys + if ok := sysCtx.FS(ctx).CloseFile(ctx, fd); !ok { return ErrnoBadf } @@ -723,7 +715,8 @@ func (a *wasi) FdDatasync(ctx context.Context, mod api.Module, fd uint32) Errno // See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_fdstat_get // See https://linux.die.net/man/3/fsync func (a *wasi) FdFdstatGet(ctx context.Context, mod api.Module, fd uint32, resultStat uint32) Errno { - if _, ok := getSysCtx(mod).FS(ctx).OpenedFile(ctx, fd); !ok { + sysCtx := mod.(*wasm.CallContext).Sys + if _, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok { return ErrnoBadf } return ErrnoSuccess @@ -757,7 +750,8 @@ func (a *wasi) FdFdstatGet(ctx context.Context, mod api.Module, fd uint32, resul // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#prestat // See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_prestat_get func (a *wasi) FdPrestatGet(ctx context.Context, mod api.Module, fd uint32, resultPrestat uint32) Errno { - entry, ok := getSysCtx(mod).FS(ctx).OpenedFile(ctx, fd) + sysCtx := mod.(*wasm.CallContext).Sys + entry, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd) if !ok { return ErrnoBadf } @@ -830,7 +824,8 @@ func (a *wasi) FdPread(ctx context.Context, mod api.Module, fd, iovs, iovsCount // See FdPrestatGet // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_prestat_dir_name func (a *wasi) FdPrestatDirName(ctx context.Context, mod api.Module, fd uint32, pathPtr uint32, pathLen uint32) Errno { - f, ok := getSysCtx(mod).FS(ctx).OpenedFile(ctx, fd) + sysCtx := mod.(*wasm.CallContext).Sys + f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd) if !ok { return ErrnoBadf } @@ -896,7 +891,8 @@ func (a *wasi) FdPwrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#iovec // See https://linux.die.net/man/3/readv func (a *wasi) FdRead(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) Errno { - reader := fdReader(ctx, mod, fd) + sysCtx := mod.(*wasm.CallContext).Sys + reader := sys.FdReader(ctx, sysCtx, fd) if reader == nil { return ErrnoBadf } @@ -972,9 +968,10 @@ func (a *wasi) FdRenumber(ctx context.Context, mod api.Module, fd, to uint32) Er // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_seek // See https://linux.die.net/man/3/lseek func (a *wasi) FdSeek(ctx context.Context, mod api.Module, fd uint32, offset uint64, whence uint32, resultNewoffset uint32) Errno { + sysCtx := mod.(*wasm.CallContext).Sys var seeker io.Seeker // Check to see if the file descriptor is available - if f, ok := getSysCtx(mod).FS(ctx).OpenedFile(ctx, fd); !ok || f.File == nil { + if f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok || f.File == nil { return ErrnoBadf // fs.FS doesn't declare io.Seeker, but implementations such as os.File implement it. } else if seeker, ok = f.File.(io.Seeker); !ok { @@ -1056,7 +1053,8 @@ func (a *wasi) FdTell(ctx context.Context, mod api.Module, fd, resultOffset uint // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_write // See https://linux.die.net/man/3/writev func (a *wasi) FdWrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) Errno { - writer := fdWriter(ctx, mod, fd) + sysCtx := mod.(*wasm.CallContext).Sys + writer := sys.FdWriter(ctx, sysCtx, fd) if writer == nil { return ErrnoBadf } @@ -1088,40 +1086,6 @@ func (a *wasi) FdWrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount, return ErrnoSuccess } -// fdReader returns a valid reader for the given file descriptor or nil if ErrnoBadf. -func fdReader(ctx context.Context, mod api.Module, fd uint32) io.Reader { - sysCtx := getSysCtx(mod) - if fd == fdStdin { - return sysCtx.Stdin() - } else if f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok { - return nil - } else { - return f.File - } -} - -// fdWriter returns a valid writer for the given file descriptor or nil if ErrnoBadf. -func fdWriter(ctx context.Context, mod api.Module, fd uint32) io.Writer { - sysCtx := getSysCtx(mod) - switch fd { - case fdStdout: - return sysCtx.Stdout() - case fdStderr: - return sysCtx.Stderr() - default: - // Check to see if the file descriptor is available - if f, ok := sysCtx.FS(ctx).OpenedFile(ctx, fd); !ok || f.File == nil { - return nil - // fs.FS doesn't declare io.Writer, but implementations such as - // os.File implement it. - } else if writer, ok := f.File.(io.Writer); !ok { - return nil - } else { - return writer - } - } -} - // PathCreateDirectory is the WASI function named functionPathCreateDirectory func (a *wasi) PathCreateDirectory(ctx context.Context, mod api.Module, fd, path, pathLen uint32) Errno { return ErrnoNosys // stubbed for GrainLang per #271 @@ -1189,7 +1153,7 @@ func (a *wasi) PathLink(ctx context.Context, mod api.Module, oldFd, oldFlags, ol // See https://linux.die.net/man/3/openat func (a *wasi) PathOpen(ctx context.Context, mod api.Module, fd, dirflags, pathPtr, pathLen, oflags uint32, fsRightsBase, fsRightsInheriting uint64, fdflags, resultOpenedFd uint32) (errno Errno) { - sysCtx := getSysCtx(mod) + sysCtx := mod.(*wasm.CallContext).Sys fsc := sysCtx.FS(ctx) if _, ok := fsc.OpenedFile(ctx, fd); !ok { return ErrnoBadf @@ -1210,7 +1174,7 @@ func (a *wasi) PathOpen(ctx context.Context, mod api.Module, fd, dirflags, pathP return ErrnoIo } } else if !mod.Memory().WriteUint32Le(ctx, resultOpenedFd, newFD) { - _, _ = fsc.CloseFile(ctx, newFD) + _ = fsc.CloseFile(ctx, newFD) return ErrnoFault } return ErrnoSuccess @@ -1280,7 +1244,8 @@ func (a *wasi) SchedYield(mod api.Module) Errno { // Note: importRandomGet shows this signature in the WebAssembly 1.0 Text Format. // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-bufLen-size---errno func (a *wasi) RandomGet(ctx context.Context, mod api.Module, buf uint32, bufLen uint32) (errno Errno) { - randSource := getSysCtx(mod).RandSource() + sysCtx := mod.(*wasm.CallContext).Sys + randSource := sysCtx.RandSource() randomBytes, ok := mod.Memory().Read(ctx, buf, bufLen) if !ok { // out-of-range @@ -1310,14 +1275,6 @@ func (a *wasi) SockShutdown(ctx context.Context, mod api.Module, fd, how uint32) return ErrnoNosys // stubbed for GrainLang per #271 } -func getSysCtx(mod api.Module) *sys.Context { - if internal, ok := mod.(*wasm.CallContext); !ok { - panic(fmt.Errorf("unsupported wasm.Module implementation: %v", mod)) - } else { - return internal.Sys - } -} - func writeOffsetsAndNullTerminatedValues(ctx context.Context, mem api.Memory, values []string, offsets, bytes uint32) Errno { for _, value := range values { // Write current offset and advance it. diff --git a/wasi_snapshot_preview1/wasi_test.go b/wasi_snapshot_preview1/wasi_test.go index 069243b7..d618112d 100644 --- a/wasi_snapshot_preview1/wasi_test.go +++ b/wasi_snapshot_preview1/wasi_test.go @@ -468,7 +468,7 @@ func Test_FdClose(t *testing.T) { verify := func(mod api.Module) { // Verify fdToClose is closed and removed from the opened FDs. - fsc := getSysCtx(mod).FS(testCtx) + fsc := mod.(*wasm.CallContext).Sys.FS(testCtx) _, ok := fsc.OpenedFile(testCtx, fdToClose) require.False(t, ok) @@ -1509,7 +1509,7 @@ func Test_PathOpen(t *testing.T) { require.Equal(t, expectedMemory, actual) // verify the file was actually opened - fsc := getSysCtx(mod).FS(ctx) + fsc := mod.(*wasm.CallContext).Sys.FS(ctx) f, ok := fsc.OpenedFile(testCtx, expectedFD) require.True(t, ok) require.Equal(t, pathName, f.Path)