wasi: Implements wasi_snapshot_preview1.poll_oneoff for relative clock events (#629)

This implements wasi_snapshot_preview1.poll_oneoff for relative clock events,
and in doing so stubs `Nanosleep` which defaults to noop, but can be configured
to `time.Sleep`.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2022-06-17 16:48:35 +08:00
committed by GitHub
parent cd00799b57
commit 30be6a8e2a
21 changed files with 1171 additions and 718 deletions

View File

@@ -417,7 +417,7 @@ is to satisfy WASI:
See https://github.com/WebAssembly/wasi-clocks
## Why default to fake time?
### Why default to fake time?
WebAssembly has an implicit design pattern of capabilities based security. By
defaulting to a fake time, we reduce the chance of timing attacks, at the cost
@@ -425,7 +425,7 @@ of requiring configuration to opt-into real clocks.
See https://gruss.cc/files/fantastictimers.pdf for an example attacks.
## Why does fake time increase on reading?
### Why does fake time increase on reading?
Both the fake nanotime and walltime increase by 1ms on reading. Particularly in
the case of nanotime, this prevents spinning. For example, when Go compiles
@@ -433,7 +433,7 @@ the case of nanotime, this prevents spinning. For example, when Go compiles
never increases, the gouroutine is mistaken for being busy. This would be worse
if a compiler implement sleep using nanotime, yet doesn't check for spinning!
## Why not `time.Clock`?
### Why not `time.Clock`?
wazero can't use `time.Clock` as a plugin for clock implementation as it is
only substitutable with build flags (`faketime`) and conflates wall and
@@ -473,6 +473,30 @@ to a POSIX method.
Writing assembly would allow making syscalls without CGO, but comes with the cost that it will require implementations
across many combinations of OS and architecture.
## sys.Nanosleep
All major programming languages have a `sleep` mechanism to block for a
duration. Sleep is typically implemented by a WASI `poll_oneoff` relative clock
subscription.
For example, the below ends up calling `wasi_snapshot_preview1.poll_oneoff`:
```zig
const std = @import("std");
pub fn main() !void {
std.time.sleep(std.time.ns_per_s * 5);
}
```
Besides Zig, this is also the case with TinyGo (`-target=wasi`) and Rust
(`--target wasm32-wasi`). This isn't the case with Go (`GOOS=js GOARCH=wasm`),
though. In the latter case, wasm loops on `sys.Nanotime`.
We decided to expose `sys.Nanosleep` to allow overriding the implementation
used in the common case, even if it isn't used by Go, because this gives an
easy and efficient closure over a common program function. We also documented
`sys.Nanotime` to warn users that some compilers don't optimize sleep.
## Signed encoding of integer global constant initializers
wazero treats integer global constant initializers signed as their interpretation is not known at declaration time. For
example, there is no signed integer [value type](https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#value-types%E2%91%A0).

View File

@@ -487,8 +487,11 @@ type ModuleConfig interface {
// return clock.nanotime()
// }, sys.ClockResolution(time.Microsecond.Nanoseconds()))
//
// Note: This does not default to time.Since as that violates sandboxing.
// Use WithSysNanotime for a usable implementation.
// 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.
@@ -496,6 +499,31 @@ type ModuleConfig interface {
// 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
@@ -530,6 +558,7 @@ type moduleConfig struct {
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
@@ -650,6 +679,18 @@ 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()
@@ -698,6 +739,7 @@ func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) {
c.randSource,
c.walltime, c.walltimeResolution,
c.nanotime, c.nanotimeResolution,
c.nanosleep,
preopens,
)
}

View File

@@ -325,6 +325,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -341,7 +342,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -358,7 +359,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -375,7 +376,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -392,7 +393,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -409,6 +410,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -425,7 +427,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -442,7 +444,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -459,7 +461,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
),
},
@@ -476,7 +478,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
map[uint32]*internalsys.FileEntry{ // openedFiles
3: {Path: "/", FS: testFS},
4: {Path: ".", FS: testFS},
@@ -496,6 +498,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
map[uint32]*internalsys.FileEntry{ // openedFiles
3: {Path: "/", FS: testFS2},
4: {Path: ".", FS: testFS2},
@@ -515,6 +518,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
map[uint32]*internalsys.FileEntry{ // openedFiles
3: {Path: ".", FS: testFS},
},
@@ -533,6 +537,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
map[uint32]*internalsys.FileEntry{ // openedFiles
3: {Path: "/", FS: testFS},
4: {Path: ".", FS: testFS2},
@@ -552,6 +557,7 @@ func TestModuleConfig_toSysContext(t *testing.T) {
nil, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
map[uint32]*internalsys.FileEntry{ // openedFiles
3: {Path: ".", FS: testFS},
4: {Path: "/", FS: testFS2},
@@ -715,6 +721,18 @@ func TestModuleConfig_toSysContext_WithNanotime(t *testing.T) {
})
}
// TestModuleConfig_toSysContext_WithNanosleep has to test differently because
// we can't compare function pointers when functions are passed by value.
func TestModuleConfig_toSysContext_WithNanosleep(t *testing.T) {
sysCtx, err := NewModuleConfig().
WithNanosleep(func(ctx context.Context, ns int64) {
require.Equal(t, testCtx, ctx)
}).(*moduleConfig).toSysContext()
require.NoError(t, err)
// If below pass, the context was correct!
sysCtx.Nanosleep(testCtx, 2)
}
func TestModuleConfig_toSysContext_Errors(t *testing.T) {
tests := []struct {
name string
@@ -820,6 +838,7 @@ func requireSysContext(
randSource io.Reader,
walltime *sys.Walltime, walltimeResolution sys.ClockResolution,
nanotime *sys.Nanotime, nanotimeResolution sys.ClockResolution,
nanosleep *sys.Nanosleep,
openedFiles map[uint32]*internalsys.FileEntry,
) *internalsys.Context {
sysCtx, err := internalsys.NewContext(
@@ -832,6 +851,7 @@ func requireSysContext(
randSource,
walltime, walltimeResolution,
nanotime, nanotimeResolution,
nanosleep,
openedFiles,
)
require.NoError(t, err)

View File

@@ -37,6 +37,10 @@ func NewFakeNanotime() *sys.Nanotime {
return &nt
}
// FakeNanosleep implements sys.Nanosleep by returning without sleeping.
func FakeNanosleep(context.Context, int64) {
}
// Walltime implements sys.Walltime with time.Now.
//
// Note: This is only notably less efficient than it could be is reading
@@ -66,3 +70,8 @@ func nanotimePortable() int64 {
func Nanotime(context.Context) int64 {
return nanotime()
}
// Nanosleep implements sys.Nanosleep with time.Sleep.
func Nanosleep(_ context.Context, ns int64) {
time.Sleep(time.Duration(ns))
}

View File

@@ -67,3 +67,13 @@ func Test_Nanotime(t *testing.T) {
})
}
}
func Test_Nanosleep(t *testing.T) {
ns := int64(50 * time.Millisecond)
start := Nanotime(context.Background())
Nanosleep(context.Background(), ns)
duration := Nanotime(context.Background()) - start
max := ns * 2 // max scheduling delay
require.True(t, duration > 0 && duration < max, "Nanosleep(%d) slept for %d", ns, duration)
}

View File

@@ -26,6 +26,7 @@ type Context struct {
walltimeResolution sys.ClockResolution
nanotime *sys.Nanotime
nanotimeResolution sys.ClockResolution
nanosleep *sys.Nanosleep
randSource io.Reader
fs *FSContext
@@ -103,8 +104,21 @@ func (c *Context) NanotimeResolution() sys.ClockResolution {
return c.nanotimeResolution
}
// Nanosleep implements sys.Nanosleep.
func (c *Context) Nanosleep(ctx context.Context, ns int64) {
(*(c.nanosleep))(ctx, ns)
}
// FS returns the file system context.
func (c *Context) FS() *FSContext {
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 c.fs
}
@@ -128,7 +142,7 @@ func (eofReader) Read([]byte) (int, error) {
// Note: This isn't a constant because Context.openedFiles is currently mutable even when empty.
// TODO: Make it an error to open or close files when no FS was assigned.
func DefaultContext() *Context {
if sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil); err != nil {
if sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil, nil); err != nil {
panic(fmt.Errorf("BUG: DefaultContext should never error: %w", err))
} else {
return sysCtx
@@ -136,6 +150,7 @@ func DefaultContext() *Context {
}
var _ = DefaultContext() // Force panic on bug.
var ns sys.Nanosleep = platform.FakeNanosleep
// NewContext is a factory function which helps avoid needing to know defaults or exporting all fields.
// Note: max is exposed for testing. max is only used for env/args validation.
@@ -147,6 +162,7 @@ func NewContext(
randSource io.Reader,
walltime *sys.Walltime, walltimeResolution sys.ClockResolution,
nanotime *sys.Nanotime, nanotimeResolution sys.ClockResolution,
nanosleep *sys.Nanosleep,
openedFiles map[uint32]*FileEntry,
) (sysCtx *Context, err error) {
sysCtx = &Context{args: args, environ: environ}
@@ -205,6 +221,12 @@ func NewContext(
sysCtx.nanotimeResolution = sys.ClockResolution(time.Nanosecond)
}
if nanosleep != nil {
sysCtx.nanosleep = nanosleep
} else {
sysCtx.nanosleep = &ns
}
sysCtx.fs = NewFSContext(openedFiles)
return

View File

@@ -2,6 +2,7 @@ package sys
import (
"bytes"
"context"
"crypto/rand"
"io"
"testing"
@@ -23,6 +24,7 @@ func TestDefaultSysContext(t *testing.T) {
nil, // randSource
nil, 0, // walltime, walltimeResolution
nil, 0, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
)
require.NoError(t, err)
@@ -41,8 +43,9 @@ func TestDefaultSysContext(t *testing.T) {
require.Equal(t, sys.ClockResolution(1_000), sysCtx.WalltimeResolution())
require.Zero(t, sysCtx.Nanotime(testCtx)) // See above on functions.
require.Equal(t, sys.ClockResolution(1), sysCtx.NanotimeResolution())
require.Equal(t, &ns, sysCtx.nanosleep)
require.Equal(t, rand.Reader, sysCtx.RandSource())
require.Equal(t, NewFSContext(map[uint32]*FileEntry{}), sysCtx.FS())
require.Equal(t, NewFSContext(map[uint32]*FileEntry{}), sysCtx.FS(testCtx))
}
func TestNewContext_Args(t *testing.T) {
@@ -93,6 +96,7 @@ func TestNewContext_Args(t *testing.T) {
nil, // randSource
nil, 0, // walltime, walltimeResolution
nil, 0, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
)
if tc.expectedErr == "" {
@@ -154,6 +158,7 @@ func TestNewContext_Environ(t *testing.T) {
nil, // randSource
nil, 0, // walltime, walltimeResolution
nil, 0, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
)
if tc.expectedErr == "" {
@@ -201,6 +206,7 @@ func TestNewContext_Walltime(t *testing.T) {
nil, // randSource
tc.time, tc.resolution, // walltime, walltimeResolution
nil, 0, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
)
if tc.expectedErr == "" {
@@ -248,6 +254,7 @@ func TestNewContext_Nanotime(t *testing.T) {
nil, // randSource
nil, 0, // nanotime, nanotimeResolution
tc.time, tc.resolution, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // openedFiles
)
if tc.expectedErr == "" {
@@ -291,3 +298,23 @@ func Test_clockResolutionInvalid(t *testing.T) {
})
}
}
func TestNewContext_Nanosleep(t *testing.T) {
var aNs sys.Nanosleep = func(context.Context, int64) {
}
sysCtx, err := NewContext(
0, // max
nil, // args
nil,
nil, // stdin
nil, // stdout
nil, // stderr
nil, // randSource
nil, 0, // Nanosleep, NanosleepResolution
nil, 0, // Nanosleep, NanosleepResolution
&aNs, // nanosleep
nil, // openedFiles
)
require.Nil(t, err)
require.Equal(t, &aNs, sysCtx.nanosleep)
}

View File

@@ -116,7 +116,7 @@ func (m *CallContext) close(ctx context.Context, exitCode uint32) (c bool, err e
return false, nil
}
if sysCtx := m.Sys; sysCtx != nil { // ex nil if from ModuleBuilder
return true, sysCtx.FS().Close(ctx)
return true, sysCtx.FS(ctx).Close(ctx)
}
return true, nil
}

View File

@@ -144,14 +144,15 @@ func TestCallContext_Close(t *testing.T) {
t.Run("calls Context.Close()", func(t *testing.T) {
sysCtx := sys.DefaultContext()
sysCtx.FS().OpenFile(&sys.FileEntry{Path: "."})
fsCtx := sysCtx.FS(testCtx)
fsCtx.OpenFile(&sys.FileEntry{Path: "."})
m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx, nil)
require.NoError(t, err)
// We use side effects to determine if Close in fact called Context.Close (without repeating sys_test.go).
// One side effect of Context.Close is that it clears the openedFiles map. Verify our base case.
fsCtx := sysCtx.FS()
_, ok := fsCtx.OpenedFile(3)
require.True(t, ok, "sysCtx.openedFiles was empty")
@@ -169,7 +170,9 @@ func TestCallContext_Close(t *testing.T) {
t.Run("error closing", func(t *testing.T) {
// Right now, the only way to err closing the sys context is if a File.Close erred.
sysCtx := sys.DefaultContext()
sysCtx.FS().OpenFile(&sys.FileEntry{Path: ".", File: &testFile{errors.New("error closing")}})
fsCtx := sysCtx.FS(testCtx)
fsCtx.OpenFile(&sys.FileEntry{Path: ".", File: &testFile{errors.New("error closing")}})
m, err := s.Instantiate(context.Background(), ns, &Module{}, t.Name(), sysCtx, nil)
require.NoError(t, err)
@@ -177,7 +180,7 @@ func TestCallContext_Close(t *testing.T) {
require.EqualError(t, m.Close(testCtx), "error closing")
// Verify our intended side-effect
_, ok := sysCtx.FS().OpenedFile(3)
_, ok := fsCtx.OpenedFile(3)
require.False(t, ok, "expected no opened files")
})
}

View File

@@ -157,6 +157,7 @@ func (m *MemoryInstance) WriteUint16Le(_ context.Context, offset uint32, v uint1
// WriteUint32Le implements the same method as documented on api.Memory.
func (m *MemoryInstance) WriteUint32Le(_ context.Context, offset, v uint32) bool {
// Note: If you use the context.Context param, don't forget to coerce nil to context.Background()!
return m.writeUint32Le(offset, v)
}

View File

@@ -163,7 +163,9 @@ func TestNamespace_CloseWithExitCode(t *testing.T) {
t.Run("error closing", func(t *testing.T) {
sysCtx := sys.DefaultContext()
sysCtx.FS().OpenFile(&sys.FileEntry{Path: ".", File: &testFile{errors.New("error closing")}})
fsCtx := sysCtx.FS(testCtx)
fsCtx.OpenFile(&sys.FileEntry{Path: ".", File: &testFile{errors.New("error closing")}})
ns, m1, m2 := newTestNamespace()
m1.CallCtx.Sys = sysCtx // This should err, but both should close

View File

@@ -76,53 +76,53 @@ your use case (ex which language you are using to compile, a.k.a. target Wasm).
<details><summary>Click to see the full list of supported WASI functions</summary>
<p>
| Function | Status | Known Usage |
|:------------------------|:------:|---------------:|
| args_get | ✅ | TinyGo |
| args_sizes_get | ✅ | TinyGo |
| environ_get | ✅ | TinyGo |
| environ_sizes_get | ✅ | TinyGo |
| clock_res_get | ✅ | |
| clock_time_get | ✅ | TinyGo |
| fd_advise | ❌ | |
| fd_allocate | ❌ | |
| fd_close | ✅ | TinyGo |
| fd_datasync | ❌ | |
| fd_fdstat_get | ✅ | TinyGo |
| fd_fdstat_set_flags | ❌ | |
| fd_fdstat_set_rights | ❌ | |
| fd_filestat_get | ❌ | |
| fd_filestat_set_size | ❌ | |
| fd_filestat_set_times | ❌ | |
| fd_pread | ❌ | |
| fd_prestat_get | ✅ | TinyGo |
| fd_prestat_dir_name | ✅ | TinyGo |
| fd_pwrite | ❌ | |
| fd_read | ✅ | TinyGo |
| fd_readdir | ❌ | |
| fd_renumber | ❌ | |
| fd_seek | ✅ | TinyGo |
| fd_sync | ❌ | |
| fd_tell | ❌ | |
| fd_write | ✅ | |
| path_create_directory | ❌ | |
| path_filestat_get | ❌ | |
| path_filestat_set_times | ❌ | |
| path_link | ❌ | |
| path_open | ✅ | TinyGo |
| path_readlink | ❌ | |
| path_remove_directory | ❌ | |
| path_rename | ❌ | |
| path_symlink | ❌ | |
| path_unlink_file | ❌ | |
| poll_oneoff | ✅ | TinyGo |
| proc_exit | ✅ | AssemblyScript |
| proc_raise | ❌ | |
| sched_yield | ❌ | |
| random_get | ✅ | |
| sock_recv | ❌ | |
| sock_send | ❌ | |
| sock_shutdown | ❌ | |
| Function | Status | Known Usage |
|:------------------------|:------:|----------------:|
| args_get | ✅ | TinyGo |
| args_sizes_get | ✅ | TinyGo |
| environ_get | ✅ | TinyGo |
| environ_sizes_get | ✅ | TinyGo |
| clock_res_get | ✅ | |
| clock_time_get | ✅ | TinyGo |
| fd_advise | ❌ | |
| fd_allocate | ❌ | |
| fd_close | ✅ | TinyGo |
| fd_datasync | ❌ | |
| fd_fdstat_get | ✅ | TinyGo |
| fd_fdstat_set_flags | ❌ | |
| fd_fdstat_set_rights | ❌ | |
| fd_filestat_get | ❌ | |
| fd_filestat_set_size | ❌ | |
| fd_filestat_set_times | ❌ | |
| fd_pread | ❌ | |
| fd_prestat_get | ✅ | TinyGo |
| fd_prestat_dir_name | ✅ | TinyGo |
| fd_pwrite | ❌ | |
| fd_read | ✅ | TinyGo |
| fd_readdir | ❌ | |
| fd_renumber | ❌ | |
| fd_seek | ✅ | TinyGo |
| fd_sync | ❌ | |
| fd_tell | ❌ | |
| fd_write | ✅ | |
| path_create_directory | ❌ | |
| path_filestat_get | ❌ | |
| path_filestat_set_times | ❌ | |
| path_link | ❌ | |
| path_open | ✅ | TinyGo |
| path_readlink | ❌ | |
| path_remove_directory | ❌ | |
| path_rename | ❌ | |
| path_symlink | ❌ | |
| path_unlink_file | ❌ | |
| poll_oneoff | ✅ | Rust,TinyGo,Zig |
| proc_exit | ✅ | AssemblyScript |
| proc_raise | ❌ | |
| sched_yield | ❌ | |
| random_get | ✅ | |
| sock_recv | ❌ | |
| sock_send | ❌ | |
| sock_shutdown | ❌ | |
</p>
</details>

View File

@@ -19,3 +19,6 @@ type Walltime func(context.Context) (sec int64, nsec int32)
// Note: There are no constraints on the value return except that it
// increments. For example, -1 is a valid if the next value is >= 0.
type Nanotime func(context.Context) int64
// Nanosleep puts the current goroutine to sleep for at least ns nanoseconds.
type Nanosleep func(ctx context.Context, ns int64)

View File

@@ -0,0 +1,123 @@
package wasi_snapshot_preview1
import (
"context"
"time"
"github.com/tetratelabs/wazero/api"
)
const (
// functionClockResGet returns the resolution of a clock.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_res_getid-clockid---errno-timestamp
functionClockResGet = "clock_res_get"
// importClockResGet is the WebAssembly 1.0 Text format import of functionClockResGet.
importClockResGet = `(import "wasi_snapshot_preview1" "clock_res_get"
(func $wasi.clock_res_get (param $id i32) (param $result.resolution i32) (result (;errno;) i32)))`
// functionClockTimeGet returns the time value of a clock.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp
functionClockTimeGet = "clock_time_get"
// importClockTimeGet is the WebAssembly 1.0 Text format import of functionClockTimeGet.
importClockTimeGet = `(import "wasi_snapshot_preview1" "clock_time_get"
(func $wasi.clock_time_get (param $id i32) (param $precision i64) (param $result.timestamp i32) (result (;errno;) i32)))`
)
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clockid-enumu32
const (
// clockIDRealtime is the clock ID named "realtime" associated with sys.Walltime
clockIDRealtime = iota
// clockIDMonotonic is the clock ID named "monotonic" with sys.Nanotime
clockIDMonotonic
// clockIDProcessCputime is the unsupported clock ID named "process_cputime_id"
clockIDProcessCputime
// clockIDThreadCputime is the unsupported clock ID named "thread_cputime_id"
clockIDThreadCputime
)
// ClockResGet is the WASI function named functionClockResGet that returns the resolution of time values returned by ClockTimeGet.
//
// * id - The clock id for which to return the time.
// * resultResolution - the offset to write the resolution to mod.Memory
// * the resolution is an uint64 little-endian encoding.
//
// For example, if the resolution is 100ns, this function writes the below to `mod.Memory`:
//
// uint64le
// +-------------------------------------+
// | |
// []byte{?, 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ?}
// resultResolution --^
//
// Note: importClockResGet shows this signature in the WebAssembly 1.0 Text Format.
// Note: This is similar to `clock_getres` in POSIX.
// 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)
var resolution uint64 // ns
switch id {
case clockIDRealtime:
resolution = uint64(sysCtx.WalltimeResolution())
case clockIDMonotonic:
resolution = uint64(sysCtx.NanotimeResolution())
case clockIDProcessCputime, clockIDThreadCputime:
// Similar to many other runtimes, we only support realtime and monotonic clocks. Other types
// are slated to be removed from the next version of WASI.
return ErrnoNotsup
default:
return ErrnoInval
}
if !mod.Memory().WriteUint64Le(ctx, resultResolution, resolution) {
return ErrnoFault
}
return ErrnoSuccess
}
// ClockTimeGet is the WASI function named functionClockTimeGet that returns the time value of a clock (time.Now).
//
// * id - The clock id for which to return the time.
// * precision - The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
// * resultTimestamp - the offset to write the timestamp to mod.Memory
// * the timestamp is epoch nanoseconds encoded as a uint64 little-endian encoding.
//
// For example, if time.Now returned exactly midnight UTC 2022-01-01 (1640995200000000000), and
// parameters resultTimestamp=1, this function writes the below to `mod.Memory`:
//
// uint64le
// +------------------------------------------+
// | |
// []byte{?, 0x0, 0x0, 0x1f, 0xa6, 0x70, 0xfc, 0xc5, 0x16, ?}
// resultTimestamp --^
//
// Note: importClockTimeGet shows this signature in the WebAssembly 1.0 Text Format.
// Note: This is similar to `clock_gettime` in POSIX.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp
// 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)
var val uint64
switch id {
case clockIDRealtime:
sec, nsec := sysCtx.Walltime(ctx)
val = (uint64(sec) * uint64(time.Second.Nanoseconds())) + uint64(nsec)
case clockIDMonotonic:
val = uint64(sysCtx.Nanotime(ctx))
case clockIDProcessCputime, clockIDThreadCputime:
// Similar to many other runtimes, we only support realtime and monotonic clocks. Other types
// are slated to be removed from the next version of WASI.
return ErrnoNotsup
default:
return ErrnoInval
}
if !mod.Memory().WriteUint64Le(ctx, resultTimestamp, val) {
return ErrnoFault
}
return ErrnoSuccess
}

View File

@@ -0,0 +1,278 @@
package wasi_snapshot_preview1
import (
_ "embed"
"fmt"
"testing"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/wasm"
)
// Test_ClockResGet only tests it is stubbed for GrainLang per #271
func Test_ClockResGet(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionClockResGet, importClockResGet, nil)
defer mod.Close(testCtx)
resultResolution := uint32(1) // arbitrary offset
expectedMemoryMicro := []byte{
'?', // resultResolution is after this
0xe8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // little endian-encoded resolution (fixed to 1000).
'?', // stopped after encoding
}
expectedMemoryNano := []byte{
'?', // resultResolution is after this
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // little endian-encoded resolution (fixed to 1000).
'?', // stopped after encoding
}
tests := []struct {
name string
clockID uint64
expectedMemory []byte
invocation func(clockID uint64) Errno
}{
{
name: "wasi.ClockResGet",
clockID: 0,
expectedMemory: expectedMemoryMicro,
invocation: func(clockID uint64) Errno {
return a.ClockResGet(testCtx, mod, uint32(clockID), resultResolution)
},
},
{
name: "wasi.ClockResGet",
clockID: 1,
expectedMemory: expectedMemoryNano,
invocation: func(clockID uint64) Errno {
return a.ClockResGet(testCtx, mod, uint32(clockID), resultResolution)
},
},
{
name: functionClockResGet,
clockID: 0,
expectedMemory: expectedMemoryMicro,
invocation: func(clockID uint64) Errno {
results, err := fn.Call(testCtx, clockID, uint64(resultResolution))
require.NoError(t, err)
return Errno(results[0]) // results[0] is the errno
},
},
{
name: functionClockResGet,
clockID: 1,
expectedMemory: expectedMemoryNano,
invocation: func(clockID uint64) Errno {
results, err := fn.Call(testCtx, clockID, uint64(resultResolution))
require.NoError(t, err)
return Errno(results[0]) // results[0] is the errno
},
},
}
for _, tt := range tests {
tc := tt
t.Run(fmt.Sprintf("%v/clockID=%v", tc.name, tc.clockID), func(t *testing.T) {
maskMemory(t, testCtx, mod, len(tc.expectedMemory))
errno := tc.invocation(tc.clockID)
require.Equal(t, ErrnoSuccess, errno, ErrnoName(errno))
actual, ok := mod.Memory().Read(testCtx, 0, uint32(len(tc.expectedMemory)))
require.True(t, ok)
require.Equal(t, tc.expectedMemory, actual)
})
}
}
func Test_ClockResGet_Unsupported(t *testing.T) {
resultResolution := uint32(1) // arbitrary offset
mod, fn := instantiateModule(testCtx, t, functionClockResGet, importClockResGet, nil)
defer mod.Close(testCtx)
tests := []struct {
name string
clockID uint64
expectedErrno Errno
}{
{
name: "process cputime",
clockID: 2,
expectedErrno: ErrnoNotsup,
},
{
name: "thread cputime",
clockID: 3,
expectedErrno: ErrnoNotsup,
},
{
name: "undefined",
clockID: 100,
expectedErrno: ErrnoInval,
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
results, err := fn.Call(testCtx, tc.clockID, uint64(resultResolution))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
require.Equal(t, tc.expectedErrno, errno, ErrnoName(errno))
})
}
}
func Test_ClockTimeGet(t *testing.T) {
resultTimestamp := uint32(1) // arbitrary offset
mod, fn := instantiateModule(testCtx, t, functionClockTimeGet, importClockTimeGet, nil)
defer mod.Close(testCtx)
clocks := []struct {
clock string
id uint32
expectedMemory []byte
}{
{
clock: "Realtime",
id: clockIDRealtime,
expectedMemory: []byte{
'?', // resultTimestamp is after this
0x0, 0x0, 0x1f, 0xa6, 0x70, 0xfc, 0xc5, 0x16, // little endian-encoded epochNanos
'?', // stopped after encoding
},
},
{
clock: "Monotonic",
id: clockIDMonotonic,
expectedMemory: []byte{
'?', // resultTimestamp is after this
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // fake nanotime starts at zero
'?', // stopped after encoding
},
},
}
for _, c := range clocks {
cc := c
t.Run(cc.clock, func(t *testing.T) {
tests := []struct {
name string
invocation func() Errno
}{
{
name: "wasi.ClockTimeGet",
invocation: func() Errno {
return a.ClockTimeGet(testCtx, mod, cc.id, 0 /* TODO: precision */, resultTimestamp)
},
},
{
name: functionClockTimeGet,
invocation: func() Errno {
results, err := fn.Call(testCtx, uint64(cc.id), 0 /* TODO: precision */, uint64(resultTimestamp))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
return errno
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
// Reset the fake clock
sysCtx, err := newSysContext(nil, nil, nil)
require.NoError(t, err)
mod.(*wasm.CallContext).Sys = sysCtx
maskMemory(t, testCtx, mod, len(cc.expectedMemory))
errno := tc.invocation()
require.Zero(t, errno, ErrnoName(errno))
actual, ok := mod.Memory().Read(testCtx, 0, uint32(len(cc.expectedMemory)))
require.True(t, ok)
require.Equal(t, cc.expectedMemory, actual)
})
}
})
}
}
func Test_ClockTimeGet_Unsupported(t *testing.T) {
resultTimestamp := uint32(1) // arbitrary offset
mod, fn := instantiateModule(testCtx, t, functionClockTimeGet, importClockTimeGet, nil)
defer mod.Close(testCtx)
tests := []struct {
name string
clockID uint64
expectedErrno Errno
}{
{
name: "process cputime",
clockID: 2,
expectedErrno: ErrnoNotsup,
},
{
name: "thread cputime",
clockID: 3,
expectedErrno: ErrnoNotsup,
},
{
name: "undefined",
clockID: 100,
expectedErrno: ErrnoInval,
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
results, err := fn.Call(testCtx, tc.clockID, 0 /* TODO: precision */, uint64(resultTimestamp))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
require.Equal(t, tc.expectedErrno, errno, ErrnoName(errno))
})
}
}
func Test_ClockTimeGet_Errors(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionClockTimeGet, importClockTimeGet, nil)
defer mod.Close(testCtx)
memorySize := mod.Memory().Size(testCtx)
tests := []struct {
name string
resultTimestamp uint32
argvBufSize uint32
}{
{
name: "resultTimestamp out-of-memory",
resultTimestamp: memorySize,
},
{
name: "resultTimestamp exceeds the maximum valid address by 1",
resultTimestamp: memorySize - 4 + 1, // 4 is the size of uint32, the type of the count of args
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
results, err := fn.Call(testCtx, 0 /* TODO: id */, 0 /* TODO: precision */, uint64(tc.resultTimestamp))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
require.Equal(t, ErrnoFault, errno, ErrnoName(errno))
})
}
}

View File

@@ -13,7 +13,7 @@ import (
//
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-errno-enumu16 and
// https://linux.die.net/man/3/errno
type Errno = uint32 // alias for parity with wasm.ValueType
type Errno = uint32 // neither uint16 nor an alias for parity with wasm.ValueType
// ErrnoName returns the POSIX error code name, except ErrnoSuccess, which is not an error. Ex. Errno2big -> "E2BIG"
func ErrnoName(errno Errno) string {

View File

@@ -0,0 +1,135 @@
package wasi_snapshot_preview1
import (
"context"
"encoding/binary"
"github.com/tetratelabs/wazero/api"
)
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-eventtype-enumu8
const (
// eventTypeClock is the timeout event named "clock".
eventTypeClock = iota
// eventTypeFdRead is the data available event named "fd_read".
eventTypeFdRead
// eventTypeFdWrite is the capacity available event named "fd_write".
eventTypeFdWrite
)
// PollOneoff is the WASI function named functionPollOneoff that concurrently
// polls for the occurrence of a set of events.
//
// Parameters
//
// * in - pointer to the subscriptions (48 bytes each)
// * out - pointer to the resulting events (32 bytes each)
// * nsubscriptions - count of subscriptions, zero returns ErrnoInval.
// * resultNevents - count of events.
//
// Result (Errno)
//
// The return value is ErrnoSuccess except the following error conditions:
// * ErrnoInval - If the parameters are invalid
// * ErrnoNotsup - If a parameters is valid, but not yet supported.
// * ErrnoFault - if there is not enough memory to read the subscriptions or write results.
//
// Notes
//
// * Since the `out` pointer nests Errno, the result is always ErrnoSuccess.
// * importPollOneoff shows this signature in the WebAssembly 1.0 Text Format.
// * This is similar to `poll` in POSIX.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#poll_oneoff
// See https://linux.die.net/man/3/poll
func (a *wasi) PollOneoff(ctx context.Context, mod api.Module, in, out, nsubscriptions, resultNevents uint32) Errno {
if nsubscriptions == 0 {
return ErrnoInval
}
mem := mod.Memory()
// Ensure capacity prior to the read loop to reduce error handling.
inBuf, ok := mem.Read(ctx, in, nsubscriptions*48)
if !ok {
return ErrnoFault
}
outBuf, ok := mem.Read(ctx, out, nsubscriptions*32)
if !ok {
return ErrnoFault
}
// Eagerly write the number of events which will equal subscriptions unless
// there's a fault in parsing (not processing).
if !mod.Memory().WriteUint32Le(ctx, resultNevents, nsubscriptions) {
return ErrnoFault
}
// Loop through all subscriptions and write their output.
for sub := uint32(0); sub < nsubscriptions; sub++ {
inOffset := sub * 48
outOffset := sub * 32
var errno Errno
eventType := inBuf[inOffset+8] // +8 past userdata
switch eventType {
case eventTypeClock: // handle later
// +8 past userdata +8 clock alignment
errno = processClockEvent(ctx, mod, inBuf[inOffset+8+8:])
case eventTypeFdRead, eventTypeFdWrite:
// +8 past userdata +4 FD alignment
errno = processFDEvent(ctx, mod, eventType, inBuf[inOffset+8+4:])
default:
return ErrnoInval
}
// Write the event corresponding to the processed subscription.
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-event-struct
copy(outBuf, inBuf[inOffset:inOffset+8]) // userdata
outBuf[outOffset+8] = byte(errno) // uint16, but safe as < 255
outBuf[outOffset+9] = 0
binary.LittleEndian.PutUint32(outBuf[outOffset+10:], uint32(eventType))
// TODO: When FD events are supported, write outOffset+16
}
return ErrnoSuccess
}
// processClockEvent supports only relative clock events, as that's what's used
// to implement sleep in various compilers including Rust, Zig and TinyGo.
func processClockEvent(ctx context.Context, mod api.Module, inBuf []byte) Errno {
_ /* ID */ = binary.LittleEndian.Uint32(inBuf[0:8]) // See below
timeout := binary.LittleEndian.Uint64(inBuf[8:16]) // nanos if relative
_ /* precision */ = binary.LittleEndian.Uint64(inBuf[16:24]) // Unused
flags := binary.LittleEndian.Uint16(inBuf[24:32])
// subclockflags has only one flag defined: subscription_clock_abstime
switch flags {
case 0: // relative time
case 1: // subscription_clock_abstime
return ErrnoNotsup
default: // subclockflags has only one flag defined.
return ErrnoInval
}
// https://linux.die.net/man/3/clock_settime says relative timers are
// 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))
return ErrnoSuccess
}
// processFDEvent returns a validation error or ErrnoNotsup as file or socket
// subscriptions are not yet supported.
func processFDEvent(ctx context.Context, mod api.Module, eventType byte, inBuf []byte) Errno {
fd := binary.LittleEndian.Uint32(inBuf)
// Choose the best error, which falls back to unsupported, until we support files.
errno := ErrnoNotsup
if eventType == eventTypeFdRead && fdReader(ctx, mod, fd) == nil {
errno = ErrnoBadf
} else if eventType == eventTypeFdWrite && fdWriter(ctx, mod, fd) == nil {
errno = ErrnoBadf
}
return errno
}

View File

@@ -0,0 +1,163 @@
package wasi_snapshot_preview1
import (
"testing"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/wasm"
)
func Test_PollOneoff(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPollOneoff, importPollOneoff, nil)
defer mod.Close(testCtx)
tests := []struct {
name string
mem []byte
expectedMem []byte // at offset out
}{
{
name: "monotonic relative",
mem: []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata
eventTypeClock, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // event type and padding
clockIDMonotonic, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // clockID
0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // timeout (ns)
0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // precision (ns)
0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // flags (relative)
'?', // stopped after encoding
},
expectedMem: []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata
byte(ErrnoSuccess), 0x0, // errno is 16 bit
eventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum
'?', // stopped after encoding
},
},
}
in := uint32(0) // past in
out := uint32(128) // past in
nsubscriptions := uint32(1)
resultNevents := uint32(512) // past out
requireExpectedMem := func(expectedMem []byte) {
outMem, ok := mod.Memory().Read(testCtx, out, uint32(len(expectedMem)))
require.True(t, ok)
require.Equal(t, expectedMem, outMem)
nevents, ok := mod.Memory().ReadUint32Le(testCtx, resultNevents)
require.True(t, ok)
require.Equal(t, nsubscriptions, nevents)
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
t.Run("wasi.PollOneoff", func(t *testing.T) {
maskMemory(t, testCtx, mod, 1024)
mod.Memory().Write(testCtx, in, tc.mem)
errno := a.PollOneoff(testCtx, mod, in, out, nsubscriptions, resultNevents)
require.Equal(t, ErrnoSuccess, errno, ErrnoName(errno))
requireExpectedMem(tc.expectedMem)
})
t.Run(functionPollOneoff, func(t *testing.T) {
maskMemory(t, testCtx, mod, 1024)
mod.Memory().Write(testCtx, in, tc.mem)
results, err := fn.Call(testCtx, uint64(in), uint64(out), uint64(nsubscriptions), uint64(resultNevents))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
require.Equal(t, ErrnoSuccess, errno, ErrnoName(errno))
requireExpectedMem(tc.expectedMem)
})
})
}
}
func Test_PollOneoff_Errors(t *testing.T) {
mod, _ := instantiateModule(testCtx, t, functionPollOneoff, importPollOneoff, nil)
defer mod.Close(testCtx)
tests := []struct {
name string
in, out, nsubscriptions, resultNevents uint32
mem []byte // at offset in
expectedErrno Errno
expectedMem []byte // at offset out
}{
{
name: "in out of range",
in: wasm.MemoryPageSize,
nsubscriptions: 1,
out: 128, // past in
resultNevents: 512, //past out
expectedErrno: ErrnoFault,
},
{
name: "out out of range",
out: wasm.MemoryPageSize,
resultNevents: 512, //past out
nsubscriptions: 1,
expectedErrno: ErrnoFault,
},
{
name: "resultNevents out of range",
resultNevents: wasm.MemoryPageSize,
nsubscriptions: 1,
expectedErrno: ErrnoFault,
},
{
name: "nsubscriptions zero",
out: 128, // past in
resultNevents: 512, //past out
expectedErrno: ErrnoInval,
},
{
name: "unsupported eventTypeFdRead",
nsubscriptions: 1,
mem: []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata
eventTypeFdRead, 0x0, 0x0, 0x0,
fdStdin, 0x0, 0x0, 0x0, // valid readable FD
'?', // stopped after encoding
},
expectedErrno: ErrnoSuccess,
out: 128, // past in
resultNevents: 512, //past out
expectedMem: []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata
byte(ErrnoNotsup), 0x0, // errno is 16 bit
eventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum
'?', // stopped after encoding
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
maskMemory(t, testCtx, mod, 1024)
if tc.mem != nil {
mod.Memory().Write(testCtx, tc.in, tc.mem)
}
errno := a.PollOneoff(testCtx, mod, tc.in, tc.out, tc.nsubscriptions, tc.resultNevents)
require.Equal(t, tc.expectedErrno, errno, ErrnoName(errno))
out, ok := mod.Memory().Read(testCtx, tc.out, uint32(len(tc.expectedMem)))
require.True(t, ok)
require.Equal(t, tc.expectedMem, out)
// Events should be written on success regardless of nested failure.
if tc.expectedErrno == ErrnoSuccess {
nevents, ok := mod.Memory().ReadUint32Le(testCtx, tc.resultNevents)
require.True(t, ok)
require.Equal(t, uint32(1), nevents)
}
})
}
}

View File

@@ -8,7 +8,7 @@
// r := wazero.NewRuntime()
// defer r.Close(ctx) // This closes everything this Runtime created.
//
// _, _ = wasi_snapshot_preview1.Instantiate(ctx, r)
// _, _ = Instantiate(ctx, r)
// mod, _ := r.InstantiateModuleFromBinary(ctx, wasm)
//
// See https://github.com/WebAssembly/WASI
@@ -21,7 +21,6 @@ import (
"io"
"io/fs"
"path"
"time"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
@@ -85,7 +84,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-args_getargv-pointerpointeru8-argv_buf-pointeru8---errno
functionArgsGet = "args_get"
// importArgsGet is the WebAssembly 1.0 (20191205) Text format import of functionArgsGet.
// importArgsGet is the WebAssembly 1.0 Text format import of functionArgsGet.
importArgsGet = `(import "wasi_snapshot_preview1" "args_get"
(func $wasi.args_get (param $argv i32) (param $argv_buf i32) (result (;errno;) i32)))`
@@ -93,7 +92,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-args_sizes_get---errno-size-size
functionArgsSizesGet = "args_sizes_get"
// importArgsSizesGet is the WebAssembly 1.0 (20191205) Text format import of functionArgsSizesGet.
// importArgsSizesGet is the WebAssembly 1.0 Text format import of functionArgsSizesGet.
importArgsSizesGet = `(import "wasi_snapshot_preview1" "args_sizes_get"
(func $wasi.args_sizes_get (param $result.argc i32) (param $result.argv_buf_size i32) (result (;errno;) i32)))`
@@ -101,7 +100,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-environ_getenviron-pointerpointeru8-environ_buf-pointeru8---errno
functionEnvironGet = "environ_get"
// importEnvironGet is the WebAssembly 1.0 (20191205) Text format import of functionEnvironGet.
// importEnvironGet is the WebAssembly 1.0 Text format import of functionEnvironGet.
importEnvironGet = `(import "wasi_snapshot_preview1" "environ_get"
(func $wasi.environ_get (param $environ i32) (param $environ_buf i32) (result (;errno;) i32)))`
@@ -109,31 +108,15 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-environ_sizes_get---errno-size-size
functionEnvironSizesGet = "environ_sizes_get"
// importEnvironSizesGet is the WebAssembly 1.0 (20191205) Text format import of functionEnvironSizesGet.
// importEnvironSizesGet is the WebAssembly 1.0 Text format import of functionEnvironSizesGet.
importEnvironSizesGet = `(import "wasi_snapshot_preview1" "environ_sizes_get"
(func $wasi.environ_sizes_get (param $result.environc i32) (param $result.environBufSize i32) (result (;errno;) i32)))`
// functionClockResGet returns the resolution of a clock.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_res_getid-clockid---errno-timestamp
functionClockResGet = "clock_res_get"
// importClockResGet is the WebAssembly 1.0 (20191205) Text format import of functionClockResGet.
importClockResGet = `(import "wasi_snapshot_preview1" "clock_res_get"
(func $wasi.clock_res_get (param $id i32) (param $result.resolution i32) (result (;errno;) i32)))`
// functionClockTimeGet returns the time value of a clock.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp
functionClockTimeGet = "clock_time_get"
// importClockTimeGet is the WebAssembly 1.0 (20191205) Text format import of functionClockTimeGet.
importClockTimeGet = `(import "wasi_snapshot_preview1" "clock_time_get"
(func $wasi.clock_time_get (param $id i32) (param $precision i64) (param $result.timestamp i32) (result (;errno;) i32)))`
// functionFdAdvise provides file advisory information on a file descriptor.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_advisefd-fd-offset-filesize-len-filesize-advice-advice---errno
functionFdAdvise = "fd_advise"
// importFdAdvise is the WebAssembly 1.0 (20191205) Text format import of functionFdAdvise.
// importFdAdvise is the WebAssembly 1.0 Text format import of functionFdAdvise.
importFdAdvise = `(import "wasi_snapshot_preview1" "fd_advise"
(func $wasi.fd_advise (param $fd i32) (param $offset i64) (param $len i64) (param $result.advice i32) (result (;errno;) i32)))`
@@ -141,7 +124,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_allocatefd-fd-offset-filesize-len-filesize---errno
functionFdAllocate = "fd_allocate"
// importFdAllocate is the WebAssembly 1.0 (20191205) Text format import of functionFdAllocate.
// importFdAllocate is the WebAssembly 1.0 Text format import of functionFdAllocate.
importFdAllocate = `(import "wasi_snapshot_preview1" "fd_allocate"
(func $wasi.fd_allocate (param $fd i32) (param $offset i64) (param $len i64) (result (;errno;) i32)))`
@@ -149,7 +132,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_close
functionFdClose = "fd_close"
// importFdClose is the WebAssembly 1.0 (20191205) Text format import of functionFdClose.
// importFdClose is the WebAssembly 1.0 Text format import of functionFdClose.
importFdClose = `(import "wasi_snapshot_preview1" "fd_close"
(func $wasi.fd_close (param $fd i32) (result (;errno;) i32)))`
@@ -157,7 +140,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_close
functionFdDatasync = "fd_datasync"
// importFdDatasync is the WebAssembly 1.0 (20191205) Text format import of functionFdDatasync.
// importFdDatasync is the WebAssembly 1.0 Text format import of functionFdDatasync.
importFdDatasync = `(import "wasi_snapshot_preview1" "fd_datasync"
(func $wasi.fd_datasync (param $fd i32) (result (;errno;) i32)))`
@@ -165,15 +148,15 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_fdstat_getfd-fd---errno-fdstat
functionFdFdstatGet = "fd_fdstat_get"
// importFdFdstatGet is the WebAssembly 1.0 (20191205) Text format import of functionFdFdstatGet.
importFdFdstatGet = `(import "wasi_snapshot_preview1" "fd_fdstat_get"
// importFdFdstatGet is the WebAssembly 1.0 Text format import of functionFdFdstatGet.
_ = /* importFdFdstatGet */ `(import "wasi_snapshot_preview1" "fd_fdstat_get"
(func $wasi.fd_fdstat_get (param $fd i32) (param $result.stat i32) (result (;errno;) i32)))` //nolint
// functionFdFdstatSetFlags adjusts the flags associated with a file descriptor.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_fdstat_set_flagsfd-fd-flags-fdflags---errno
functionFdFdstatSetFlags = "fd_fdstat_set_flags"
// importFdFdstatSetFlags is the WebAssembly 1.0 (20191205) Text format import of functionFdFdstatSetFlags.
// importFdFdstatSetFlags is the WebAssembly 1.0 Text format import of functionFdFdstatSetFlags.
importFdFdstatSetFlags = `(import "wasi_snapshot_preview1" "fd_fdstat_set_flags"
(func $wasi.fd_fdstat_set_flags (param $fd i32) (param $flags i32) (result (;errno;) i32)))`
@@ -181,7 +164,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_fdstat_set_rightsfd-fd-fs_rights_base-rights-fs_rights_inheriting-rights---errno
functionFdFdstatSetRights = "fd_fdstat_set_rights"
// importFdFdstatSetRights is the WebAssembly 1.0 (20191205) Text format import of functionFdFdstatSetRights.
// importFdFdstatSetRights is the WebAssembly 1.0 Text format import of functionFdFdstatSetRights.
importFdFdstatSetRights = `(import "wasi_snapshot_preview1" "fd_fdstat_set_rights"
(func $wasi.fd_fdstat_set_rights (param $fd i32) (param $fs_rights_base i64) (param $fs_rights_inheriting i64) (result (;errno;) i32)))`
@@ -189,7 +172,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_getfd-fd---errno-filestat
functionFdFilestatGet = "fd_filestat_get"
// importFdFilestatGet is the WebAssembly 1.0 (20191205) Text format import of functionFdFilestatGet.
// importFdFilestatGet is the WebAssembly 1.0 Text format import of functionFdFilestatGet.
importFdFilestatGet = `(import "wasi_snapshot_preview1" "fd_filestat_get"
(func $wasi.fd_filestat_get (param $fd i32) (param $result.buf i32) (result (;errno;) i32)))`
@@ -197,7 +180,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_set_sizefd-fd-size-filesize---errno
functionFdFilestatSetSize = "fd_filestat_set_size"
// importFdFilestatSetSize is the WebAssembly 1.0 (20191205) Text format import of functionFdFilestatSetSize.
// importFdFilestatSetSize is the WebAssembly 1.0 Text format import of functionFdFilestatSetSize.
importFdFilestatSetSize = `(import "wasi_snapshot_preview1" "fd_filestat_set_size"
(func $wasi.fd_filestat_set_size (param $fd i32) (param $size i64) (result (;errno;) i32)))`
@@ -205,7 +188,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_filestat_set_timesfd-fd-atim-timestamp-mtim-timestamp-fst_flags-fstflags---errno
functionFdFilestatSetTimes = "fd_filestat_set_times"
// importFdFilestatSetTimes is the WebAssembly 1.0 (20191205) Text format import of functionFdFilestatSetTimes.
// importFdFilestatSetTimes is the WebAssembly 1.0 Text format import of functionFdFilestatSetTimes.
importFdFilestatSetTimes = `(import "wasi_snapshot_preview1" "fd_filestat_set_times"
(func $wasi.fd_filestat_set_times (param $fd i32) (param $atim i64) (param $mtim i64) (param $fst_flags i32) (result (;errno;) i32)))`
@@ -213,7 +196,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_preadfd-fd-iovs-iovec_array-offset-filesize---errno-size
functionFdPread = "fd_pread"
// importFdPread is the WebAssembly 1.0 (20191205) Text format import of functionFdPread.
// importFdPread is the WebAssembly 1.0 Text format import of functionFdPread.
importFdPread = `(import "wasi_snapshot_preview1" "fd_pread"
(func $wasi.fd_pread (param $fd i32) (param $iovs i32) (param $iovs_len i32) (param $offset i64) (param $result.nread i32) (result (;errno;) i32)))`
@@ -221,7 +204,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_prestat_get
functionFdPrestatGet = "fd_prestat_get"
// importFdPrestatGet is the WebAssembly 1.0 (20191205) Text format import of functionFdPrestatGet.
// importFdPrestatGet is the WebAssembly 1.0 Text format import of functionFdPrestatGet.
importFdPrestatGet = `(import "wasi_snapshot_preview1" "fd_prestat_get"
(func $wasi.fd_prestat_get (param $fd i32) (param $result.prestat i32) (result (;errno;) i32)))`
@@ -229,7 +212,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_prestat_dir_name
functionFdPrestatDirName = "fd_prestat_dir_name"
// importFdPrestatDirName is the WebAssembly 1.0 (20191205) Text format import of functionFdPrestatDirName.
// importFdPrestatDirName is the WebAssembly 1.0 Text format import of functionFdPrestatDirName.
importFdPrestatDirName = `(import "wasi_snapshot_preview1" "fd_prestat_dir_name"
(func $wasi.fd_prestat_dir_name (param $fd i32) (param $path i32) (param $path_len i32) (result (;errno;) i32)))`
@@ -237,7 +220,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_pwritefd-fd-iovs-ciovec_array-offset-filesize---errno-size
functionFdPwrite = "fd_pwrite"
// importFdPwrite is the WebAssembly 1.0 (20191205) Text format import of functionFdPwrite.
// importFdPwrite is the WebAssembly 1.0 Text format import of functionFdPwrite.
importFdPwrite = `(import "wasi_snapshot_preview1" "fd_pwrite"
(func $wasi.fd_pwrite (param $fd i32) (param $iovs i32) (param $iovs_len i32) (param $offset i64) (param $result.nwritten i32) (result (;errno;) i32)))`
@@ -245,7 +228,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_read
functionFdRead = "fd_read"
// importFdRead is the WebAssembly 1.0 (20191205) Text format import of functionFdRead.
// importFdRead is the WebAssembly 1.0 Text format import of functionFdRead.
importFdRead = `(import "wasi_snapshot_preview1" "fd_read"
(func $wasi.fd_read (param $fd i32) (param $iovs i32) (param $iovs_len i32) (param $result.size i32) (result (;errno;) i32)))`
@@ -253,7 +236,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_readdirfd-fd-buf-pointeru8-buf_len-size-cookie-dircookie---errno-size
functionFdReaddir = "fd_readdir"
// importFdReaddir is the WebAssembly 1.0 (20191205) Text format import of functionFdReaddir.
// importFdReaddir is the WebAssembly 1.0 Text format import of functionFdReaddir.
importFdReaddir = `(import "wasi_snapshot_preview1" "fd_readdir"
(func $wasi.fd_readdir (param $fd i32) (param $buf i32) (param $buf_len i32) (param $cookie i64) (param $result.bufused i32) (result (;errno;) i32)))`
@@ -261,7 +244,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_renumberfd-fd-to-fd---errno
functionFdRenumber = "fd_renumber"
// importFdRenumber is the WebAssembly 1.0 (20191205) Text format import of functionFdRenumber.
// importFdRenumber is the WebAssembly 1.0 Text format import of functionFdRenumber.
importFdRenumber = `(import "wasi_snapshot_preview1" "fd_renumber"
(func $wasi.fd_renumber (param $fd i32) (param $to i32) (result (;errno;) i32)))`
@@ -269,7 +252,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_seekfd-fd-offset-filedelta-whence-whence---errno-filesize
functionFdSeek = "fd_seek"
// importFdSeek is the WebAssembly 1.0 (20191205) Text format import of functionFdSeek.
// importFdSeek is the WebAssembly 1.0 Text format import of functionFdSeek.
importFdSeek = `(import "wasi_snapshot_preview1" "fd_seek"
(func $wasi.fd_seek (param $fd i32) (param $offset i64) (param $whence i32) (param $result.newoffset i32) (result (;errno;) i32)))`
@@ -277,7 +260,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_syncfd-fd---errno
functionFdSync = "fd_sync"
// importFdSync is the WebAssembly 1.0 (20191205) Text format import of functionFdSync.
// importFdSync is the WebAssembly 1.0 Text format import of functionFdSync.
importFdSync = `(import "wasi_snapshot_preview1" "fd_sync"
(func $wasi.fd_sync (param $fd i32) (result (;errno;) i32)))`
@@ -285,7 +268,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-fd_tellfd-fd---errno-filesize
functionFdTell = "fd_tell"
// importFdTell is the WebAssembly 1.0 (20191205) Text format import of functionFdTell.
// importFdTell is the WebAssembly 1.0 Text format import of functionFdTell.
importFdTell = `(import "wasi_snapshot_preview1" "fd_tell"
(func $wasi.fd_tell (param $fd i32) (param $result.offset i32) (result (;errno;) i32)))`
@@ -293,7 +276,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_write
functionFdWrite = "fd_write"
// importFdWrite is the WebAssembly 1.0 (20191205) Text format import of functionFdWrite.
// importFdWrite is the WebAssembly 1.0 Text format import of functionFdWrite.
importFdWrite = `(import "wasi_snapshot_preview1" "fd_write"
(func $wasi.fd_write (param $fd i32) (param $iovs i32) (param $iovs_len i32) (param $result.size i32) (result (;errno;) i32)))`
@@ -301,7 +284,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_create_directoryfd-fd-path-string---errno
functionPathCreateDirectory = "path_create_directory"
// importPathCreateDirectory is the WebAssembly 1.0 (20191205) Text format import of functionPathCreateDirectory.
// importPathCreateDirectory is the WebAssembly 1.0 Text format import of functionPathCreateDirectory.
importPathCreateDirectory = `(import "wasi_snapshot_preview1" "path_create_directory"
(func $wasi.path_create_directory (param $fd i32) (param $path i32) (param $path_len i32) (result (;errno;) i32)))`
@@ -309,7 +292,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_filestat_getfd-fd-flags-lookupflags-path-string---errno-filestat
functionPathFilestatGet = "path_filestat_get"
// importPathFilestatGet is the WebAssembly 1.0 (20191205) Text format import of functionPathFilestatGet.
// importPathFilestatGet is the WebAssembly 1.0 Text format import of functionPathFilestatGet.
importPathFilestatGet = `(import "wasi_snapshot_preview1" "path_filestat_get"
(func $wasi.path_filestat_get (param $fd i32) (param $flags i32) (param $path i32) (param $path_len i32) (param $result.buf i32) (result (;errno;) i32)))`
@@ -317,7 +300,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_filestat_set_timesfd-fd-flags-lookupflags-path-string-atim-timestamp-mtim-timestamp-fst_flags-fstflags---errno
functionPathFilestatSetTimes = "path_filestat_set_times"
// importPathFilestatSetTimes is the WebAssembly 1.0 (20191205) Text format import of functionPathFilestatSetTimes.
// importPathFilestatSetTimes is the WebAssembly 1.0 Text format import of functionPathFilestatSetTimes.
importPathFilestatSetTimes = `(import "wasi_snapshot_preview1" "path_filestat_set_times"
(func $wasi.path_filestat_set_times (param $fd i32) (param $flags i32) (param $path i32) (param $path_len i32) (param $atim i64) (param $mtim i64) (param $fst_flags i32) (result (;errno;) i32)))`
@@ -325,7 +308,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#path_link
functionPathLink = "path_link"
// importPathLink is the WebAssembly 1.0 (20191205) Text format import of functionPathLink.
// importPathLink is the WebAssembly 1.0 Text format import of functionPathLink.
importPathLink = `(import "wasi_snapshot_preview1" "path_link"
(func $wasi.path_link (param $old_fd i32) (param $old_flags i32) (param $old_path i32) (param $old_path_len i32) (param $new_fd i32) (param $new_path i32) (param $new_path_len i32) (result (;errno;) i32)))`
@@ -333,7 +316,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_openfd-fd-dirflags-lookupflags-path-string-oflags-oflags-fs_rights_base-rights-fs_rights_inheriting-rights-fdflags-fdflags---errno-fd
functionPathOpen = "path_open"
// importPathOpen is the WebAssembly 1.0 (20191205) Text format import of functionPathOpen.
// importPathOpen is the WebAssembly 1.0 Text format import of functionPathOpen.
importPathOpen = `(import "wasi_snapshot_preview1" "path_open"
(func $wasi.path_open (param $fd i32) (param $dirflags i32) (param $path i32) (param $path_len i32) (param $oflags i32) (param $fs_rights_base i64) (param $fs_rights_inheriting i64) (param $fdflags i32) (param $result.opened_fd i32) (result (;errno;) i32)))`
@@ -341,7 +324,7 @@ const (
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_readlinkfd-fd-path-string-buf-pointeru8-buf_len-size---errno-size
functionPathReadlink = "path_readlink"
// importPathReadlink is the WebAssembly 1.0 (20191205) Text format import of functionPathReadlink.
// importPathReadlink is the WebAssembly 1.0 Text format import of functionPathReadlink.
importPathReadlink = `(import "wasi_snapshot_preview1" "path_readlink"
(func $wasi.path_readlink (param $fd i32) (param $path i32) (param $path_len i32) (param $buf i32) (param $buf_len i32) (param $result.bufused i32) (result (;errno;) i32)))`
@@ -349,7 +332,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_remove_directoryfd-fd-path-string---errno
functionPathRemoveDirectory = "path_remove_directory"
// importPathRemoveDirectory is the WebAssembly 1.0 (20191205) Text format import of functionPathRemoveDirectory.
// importPathRemoveDirectory is the WebAssembly 1.0 Text format import of functionPathRemoveDirectory.
importPathRemoveDirectory = `(import "wasi_snapshot_preview1" "path_remove_directory"
(func $wasi.path_remove_directory (param $fd i32) (param $path i32) (param $path_len i32) (result (;errno;) i32)))`
@@ -357,7 +340,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_renamefd-fd-old_path-string-new_fd-fd-new_path-string---errno
functionPathRename = "path_rename"
// importPathRename is the WebAssembly 1.0 (20191205) Text format import of functionPathRename.
// importPathRename is the WebAssembly 1.0 Text format import of functionPathRename.
importPathRename = `(import "wasi_snapshot_preview1" "path_rename"
(func $wasi.path_rename (param $fd i32) (param $old_path i32) (param $old_path_len i32) (param $new_fd i32) (param $new_path i32) (param $new_path_len i32) (result (;errno;) i32)))`
@@ -365,7 +348,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#path_symlink
functionPathSymlink = "path_symlink"
// importPathSymlink is the WebAssembly 1.0 (20191205) Text format import of functionPathSymlink.
// importPathSymlink is the WebAssembly 1.0 Text format import of functionPathSymlink.
importPathSymlink = `(import "wasi_snapshot_preview1" "path_symlink"
(func $wasi.path_symlink (param $old_path i32) (param $old_path_len i32) (param $fd i32) (param $new_path i32) (param $new_path_len i32) (result (;errno;) i32)))`
@@ -373,15 +356,15 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-path_unlink_filefd-fd-path-string---errno
functionPathUnlinkFile = "path_unlink_file"
// importPathUnlinkFile is the WebAssembly 1.0 (20191205) Text format import of functionPathUnlinkFile.
// importPathUnlinkFile is the WebAssembly 1.0 Text format import of functionPathUnlinkFile.
importPathUnlinkFile = `(import "wasi_snapshot_preview1" "path_unlink_file"
(func $wasi.path_unlink_file (param $fd i32) (param $path i32) (param $path_len i32) (result (;errno;) i32)))`
// functionPollOneoff unlinks a file.
// functionPollOneoff concurrently polls for the occurrence of a set of events.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-poll_oneoffin-constpointersubscription-out-pointerevent-nsubscriptions-size---errno-size
functionPollOneoff = "poll_oneoff"
// importPollOneoff is the WebAssembly 1.0 (20191205) Text format import of functionPollOneoff.
// importPollOneoff is the WebAssembly 1.0 Text format import of functionPollOneoff.
importPollOneoff = `(import "wasi_snapshot_preview1" "poll_oneoff"
(func $wasi.poll_oneoff (param $in i32) (param $out i32) (param $nsubscriptions i32) (param $result.nevents i32) (result (;errno;) i32)))`
@@ -389,12 +372,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit
functionProcExit = "proc_exit"
// importProcExit is the WebAssembly 1.0 (20191205) Text format import of functionProcExit.
//
// See importProcExit
// See wasi.ProcExit
// See functionProcExit
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#proc_exit
// importProcExit is the WebAssembly 1.0 Text format import of functionProcExit.
importProcExit = `(import "wasi_snapshot_preview1" "proc_exit"
(func $wasi.proc_exit (param $rval i32)))`
@@ -402,7 +380,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-proc_raisesig-signal---errno
functionProcRaise = "proc_raise"
// importProcRaise is the WebAssembly 1.0 (20191205) Text format import of functionProcRaise.
// importProcRaise is the WebAssembly 1.0 Text format import of functionProcRaise.
importProcRaise = `(import "wasi_snapshot_preview1" "proc_raise"
(func $wasi.proc_raise (param $sig i32) (result (;errno;) i32)))`
@@ -410,7 +388,7 @@ const (
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sched_yield---errno
functionSchedYield = "sched_yield"
// importSchedYield is the WebAssembly 1.0 (20191205) Text format import of functionSchedYield.
// importSchedYield is the WebAssembly 1.0 Text format import of functionSchedYield.
importSchedYield = `(import "wasi_snapshot_preview1" "sched_yield"
(func $wasi.sched_yield (result (;errno;) i32)))`
@@ -418,7 +396,7 @@ const (
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
functionRandomGet = "random_get"
// importRandomGet is the WebAssembly 1.0 (20191205) Text format import of functionRandomGet.
// importRandomGet is the WebAssembly 1.0 Text format import of functionRandomGet.
importRandomGet = `(import "wasi_snapshot_preview1" "random_get"
(func $wasi.random_get (param $buf i32) (param $buf_len i32) (result (;errno;) i32)))`
@@ -426,7 +404,7 @@ const (
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_recvfd-fd-ri_data-iovec_array-ri_flags-riflags---errno-size-roflags
functionSockRecv = "sock_recv"
// importSockRecv is the WebAssembly 1.0 (20191205) Text format import of functionSockRecv.
// importSockRecv is the WebAssembly 1.0 Text format import of functionSockRecv.
importSockRecv = `(import "wasi_snapshot_preview1" "sock_recv"
(func $wasi.sock_recv (param $fd i32) (param $ri_data i32) (param $ri_data_count i32) (param $ri_flags i32) (param $result.ro_datalen i32) (param $result.ro_flags i32) (result (;errno;) i32)))`
@@ -434,7 +412,7 @@ const (
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_sendfd-fd-si_data-ciovec_array-si_flags-siflags---errno-size
functionSockSend = "sock_send"
// importSockSend is the WebAssembly 1.0 (20191205) Text format import of functionSockSend.
// importSockSend is the WebAssembly 1.0 Text format import of functionSockSend.
importSockSend = `(import "wasi_snapshot_preview1" "sock_send"
(func $wasi.sock_send (param $fd i32) (param $si_data i32) (param $si_data_count i32) (param $si_flags i32) (param $result.so_datalen i32) (result (;errno;) i32)))`
@@ -442,29 +420,35 @@ const (
// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_shutdownfd-fd-how-sdflags---errno
functionSockShutdown = "sock_shutdown"
// importSockShutdown is the WebAssembly 1.0 (20191205) Text format import of functionSockShutdown.
// importSockShutdown is the WebAssembly 1.0 Text format import of functionSockShutdown.
importSockShutdown = `(import "wasi_snapshot_preview1" "sock_shutdown"
(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
// ### String
// WebAssembly 1.0 (20191205) has no string type, so any string input parameter expands to two uint32 parameters: offset
// WebAssembly 1.0 has no string type, so any string input parameter expands to two uint32 parameters: offset
// and length.
//
// ### iovec_array
// `iovec_array` is encoded as two uin32le values (i32): offset and count.
//
// ### Result
// Each result besides wasi_snapshot_preview1.Errno is always an uint32 parameter. WebAssembly 1.0 (20191205) can have up to one result,
// which is already used by wasi_snapshot_preview1.Errno. This forces other results to be parameters. A result parameter is a memory
// Each result besides Errno is always an uint32 parameter. WebAssembly 1.0 can have up to one result,
// which is already used by Errno. This forces other results to be parameters. A result parameter is a memory
// offset to write the result to. As memory offsets are uint32, each parameter representing a result is uint32.
//
// ### Errno
// The WASI specification is sometimes ambiguous resulting in some runtimes interpreting the same function ways.
// wasi_snapshot_preview1.Errno mappings are not defined in WASI, yet, so these mappings are best efforts by maintainers. When in doubt
// Errno mappings are not defined in WASI, yet, so these mappings are best efforts by maintainers. When in doubt
// about portability, first look at /RATIONALE.md and if needed an issue on
// https://github.com/WebAssembly/WASI/issues
//
@@ -554,7 +538,7 @@ func wasiFunctions() map[string]interface{} {
// offset that begins "a" --+ |
// offset that begins "bc" --+
//
// Note: importArgsGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importArgsGet shows this signature in the WebAssembly 1.0 Text Format.
// See ArgsSizesGet
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#args_get
// See https://en.wikipedia.org/wiki/Null-terminated_string
@@ -585,7 +569,7 @@ func (a *wasi) ArgsGet(ctx context.Context, mod api.Module, argv, argvBuf uint32
// resultArgvBufSize --|
// len([]byte{'a',0,'b',c',0}) --+
//
// Note: importArgsSizesGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importArgsSizesGet shows this signature in the WebAssembly 1.0 Text Format.
// See ArgsGet
// 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
@@ -624,7 +608,7 @@ func (a *wasi) ArgsSizesGet(ctx context.Context, mod api.Module, resultArgc, res
// environ offset for "a=b" --+ |
// environ offset for "b=cd" --+
//
// Note: importEnvironGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importEnvironGet shows this signature in the WebAssembly 1.0 Text Format.
// See EnvironSizesGet
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#environ_get
// See https://en.wikipedia.org/wiki/Null-terminated_string
@@ -656,7 +640,7 @@ func (a *wasi) EnvironGet(ctx context.Context, mod api.Module, environ uint32, e
// len([]byte{'a','=','b',0, |
// 'b','=','c','d',0}) --+
//
// Note: importEnvironGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importEnvironGet shows this signature in the WebAssembly 1.0 Text Format.
// See EnvironGet
// 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
@@ -674,87 +658,6 @@ func (a *wasi) EnvironSizesGet(ctx context.Context, mod api.Module, resultEnviro
return ErrnoSuccess
}
// ClockResGet is the WASI function named functionClockResGet that returns the resolution of time values returned by ClockTimeGet.
//
// * id - The clock id for which to return the time.
// * resultResolution - the offset to write the resolution to mod.Memory
// * the resolution is an uint64 little-endian encoding.
//
// For example, if the resolution is 100ns, this function writes the below to `mod.Memory`:
//
// uint64le
// +-------------------------------------+
// | |
// []byte{?, 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ?}
// resultResolution --^
//
// Note: importClockResGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: This is similar to `clock_getres` in POSIX.
// 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)
var resolution uint64 // ns
switch id {
case clockIDRealtime:
resolution = uint64(sysCtx.WalltimeResolution())
case clockIDMonotonic:
resolution = uint64(sysCtx.NanotimeResolution())
default:
// Similar to many other runtimes, we only support realtime and monotonic clocks. Other types
// are slated to be removed from the next version of WASI.
return ErrnoNosys
}
if !mod.Memory().WriteUint64Le(ctx, resultResolution, resolution) {
return ErrnoFault
}
return ErrnoSuccess
}
// ClockTimeGet is the WASI function named functionClockTimeGet that returns the time value of a clock (time.Now).
//
// * id - The clock id for which to return the time.
// * precision - The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
// * resultTimestamp - the offset to write the timestamp to mod.Memory
// * the timestamp is epoch nanoseconds encoded as a uint64 little-endian encoding.
//
// For example, if time.Now returned exactly midnight UTC 2022-01-01 (1640995200000000000), and
// parameters resultTimestamp=1, this function writes the below to `mod.Memory`:
//
// uint64le
// +------------------------------------------+
// | |
// []byte{?, 0x0, 0x0, 0x1f, 0xa6, 0x70, 0xfc, 0xc5, 0x16, ?}
// resultTimestamp --^
//
// Note: importClockTimeGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: This is similar to `clock_gettime` in POSIX.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp
// 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)
var val uint64
switch id {
case clockIDRealtime:
sec, nsec := sysCtx.Walltime(ctx)
val = (uint64(sec) * uint64(time.Second.Nanoseconds())) + uint64(nsec)
case clockIDMonotonic:
val = uint64(sysCtx.Nanotime(ctx))
default:
// Similar to many other runtimes, we only support realtime and monotonic clocks. Other types
// are slated to be removed from the next version of WASI.
return ErrnoNosys
}
if !mod.Memory().WriteUint64Le(ctx, resultTimestamp, val) {
return ErrnoFault
}
return ErrnoSuccess
}
// FdAdvise is the WASI function named functionFdAdvise and is stubbed for GrainLang per #271
func (a *wasi) FdAdvise(ctx context.Context, mod api.Module, fd uint32, offset, len uint64, resultAdvice uint32) Errno {
return ErrnoNosys // stubbed for GrainLang per #271
@@ -769,14 +672,12 @@ func (a *wasi) FdAllocate(ctx context.Context, mod api.Module, fd uint32, offset
//
// * fd - the file descriptor to close
//
// Note: importFdClose shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importFdClose shows this signature in the WebAssembly 1.0 Text Format.
// Note: This is similar to `close` in POSIX.
// 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 {
_, fsc := sysFSCtx(ctx, mod)
if ok, err := fsc.CloseFile(fd); err != nil {
if ok, err := getSysCtx(mod).FS(ctx).CloseFile(fd); err != nil {
return ErrnoIo
} else if !ok {
return ErrnoBadf
@@ -795,9 +696,9 @@ func (a *wasi) FdDatasync(ctx context.Context, mod api.Module, fd uint32) Errno
// * fd - the file descriptor to get the fdstat attributes data
// * resultFdstat - the offset to write the result fdstat data
//
// The wasi_snapshot_preview1.Errno returned is wasi_snapshot_preview1.ErrnoSuccess except the following error conditions:
// * wasi_snapshot_preview1.ErrnoBadf - if `fd` is invalid
// * wasi_snapshot_preview1.ErrnoFault - if `resultFdstat` contains an invalid offset due to the memory constraint
// The Errno returned is ErrnoSuccess except the following error conditions:
// * ErrnoBadf - if `fd` is invalid
// * ErrnoFault - if `resultFdstat` contains an invalid offset due to the memory constraint
//
// fdstat byte layout is 24-byte size, which as the following elements in order
// * fs_filetype 1 byte, to indicate the file type
@@ -817,15 +718,13 @@ func (a *wasi) FdDatasync(ctx context.Context, mod api.Module, fd uint32) Errno
// |
// +-- fs_filetype
//
// Note: importFdFdstatGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importFdFdstatGet shows this signature in the WebAssembly 1.0 Text Format.
// Note: FdFdstatGet returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields.
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fdstat
// 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 {
_, fsc := sysFSCtx(ctx, mod)
if _, ok := fsc.OpenedFile(fd); !ok {
if _, ok := getSysCtx(mod).FS(ctx).OpenedFile(fd); !ok {
return ErrnoBadf
}
return ErrnoSuccess
@@ -836,9 +735,9 @@ func (a *wasi) FdFdstatGet(ctx context.Context, mod api.Module, fd uint32, resul
// * fd - the file descriptor to get the prestat
// * resultPrestat - the offset to write the result prestat data
//
// The wasi_snapshot_preview1.Errno returned is wasi_snapshot_preview1.ErrnoSuccess except the following error conditions:
// * wasi_snapshot_preview1.ErrnoBadf - if `fd` is invalid or the `fd` is not a pre-opened directory.
// * wasi_snapshot_preview1.ErrnoFault - if `resultPrestat` is an invalid offset due to the memory constraint
// The Errno returned is ErrnoSuccess except the following error conditions:
// * ErrnoBadf - if `fd` is invalid or the `fd` is not a pre-opened directory.
// * ErrnoFault - if `resultPrestat` is an invalid offset due to the memory constraint
//
// prestat byte layout is 8 bytes, beginning with an 8-bit tag and 3 pad bytes. The only valid tag is `prestat_dir`,
// which is tag zero. This simplifies the byte layout to 4 empty bytes followed by the uint32le encoded path length.
@@ -854,14 +753,12 @@ func (a *wasi) FdFdstatGet(ctx context.Context, mod api.Module, fd uint32, resul
// tag --+ |
// +-- size in bytes of the string "/tmp"
//
// Note: importFdPrestatGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importFdPrestatGet shows this signature in the WebAssembly 1.0 Text Format.
// See FdPrestatDirName
// 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 {
_, fsc := sysFSCtx(ctx, mod)
entry, ok := fsc.OpenedFile(fd)
entry, ok := getSysCtx(mod).FS(ctx).OpenedFile(fd)
if !ok {
return ErrnoBadf
}
@@ -916,10 +813,10 @@ func (a *wasi) FdPread(ctx context.Context, mod api.Module, fd, iovs, iovsCount
// * pathLen - the count of bytes to write to `path`
// * This should match the uint32le FdPrestatGet writes to offset `resultPrestat`+4
//
// The wasi_snapshot_preview1.Errno returned is wasi_snapshot_preview1.ErrnoSuccess except the following error conditions:
// * wasi_snapshot_preview1.ErrnoBadf - if `fd` is invalid
// * wasi_snapshot_preview1.ErrnoFault - if `path` is an invalid offset due to the memory constraint
// * wasi_snapshot_preview1.ErrnoNametoolong - if `pathLen` is longer than the actual length of the result path
// The Errno returned is ErrnoSuccess except the following error conditions:
// * ErrnoBadf - if `fd` is invalid
// * ErrnoFault - if `path` is an invalid offset due to the memory constraint
// * ErrnoNametoolong - if `pathLen` is longer than the actual length of the result path
//
// For example, the directory name corresponding with `fd` was "/tmp" and
// parameters path=1 pathLen=4 (correct), this function will write the below to `mod.Memory`:
@@ -930,13 +827,11 @@ func (a *wasi) FdPread(ctx context.Context, mod api.Module, fd, iovs, iovsCount
// []byte{?, '/', 't', 'm', 'p', ?}
// path --^
//
// Note: importFdPrestatDirName shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importFdPrestatDirName shows this signature in the WebAssembly 1.0 Text Format.
// 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 {
_, fsc := sysFSCtx(ctx, mod)
f, ok := fsc.OpenedFile(fd)
f, ok := getSysCtx(mod).FS(ctx).OpenedFile(fd)
if !ok {
return ErrnoBadf
}
@@ -966,10 +861,10 @@ func (a *wasi) FdPwrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount
// * iovsCount - the count of memory offset, size pairs to read sequentially starting at iovs.
// * resultSize - the offset in `mod.Memory` to write the number of bytes read
//
// The wasi_snapshot_preview1.Errno returned is wasi_snapshot_preview1.ErrnoSuccess except the following error conditions:
// * wasi_snapshot_preview1.ErrnoBadf - if `fd` is invalid
// * wasi_snapshot_preview1.ErrnoFault - if `iovs` or `resultSize` contain an invalid offset due to the memory constraint
// * wasi_snapshot_preview1.ErrnoIo - if an IO related error happens during the operation
// The Errno returned is ErrnoSuccess except the following error conditions:
// * ErrnoBadf - if `fd` is invalid
// * ErrnoFault - if `iovs` or `resultSize` contain an invalid offset due to the memory constraint
// * ErrnoIo - if an IO related error happens during the operation
//
// For example, this function needs to first read `iovs` to determine where to write contents. If
// parameters iovs=1 iovsCount=2, this function reads two offset/length pairs from `mod.Memory`:
@@ -995,23 +890,16 @@ func (a *wasi) FdPwrite(ctx context.Context, mod api.Module, fd, iovs, iovsCount
// iovs[1].offset --+ |
// resultSize --+
//
// Note: importFdRead shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importFdRead shows this signature in the WebAssembly 1.0 Text Format.
// Note: This is similar to `readv` in POSIX.
// See FdWrite
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#fd_read
// 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 {
sysCtx, fsCtx := sysFSCtx(ctx, mod)
var reader io.Reader
if fd == fdStdin {
reader = sysCtx.Stdin()
} else if f, ok := fsCtx.OpenedFile(fd); !ok || f.File == nil {
reader := fdReader(ctx, mod, fd)
if reader == nil {
return ErrnoBadf
} else {
reader = f.File
}
var nread uint32
@@ -1063,11 +951,11 @@ func (a *wasi) FdRenumber(ctx context.Context, mod api.Module, fd, to uint32) Er
// * If io.SeekEnd, new offset == file size of `fd` + `offset`.
// * resultNewoffset: the offset in `mod.Memory` to write the new offset to, relative to start of the file
//
// The wasi_snapshot_preview1.Errno returned is wasi_snapshot_preview1.ErrnoSuccess except the following error conditions:
// * wasi_snapshot_preview1.ErrnoBadf - if `fd` is invalid
// * wasi_snapshot_preview1.ErrnoFault - if `resultNewoffset` is an invalid offset in `mod.Memory` due to the memory constraint
// * wasi_snapshot_preview1.ErrnoInval - if `whence` is an invalid value
// * wasi_snapshot_preview1.ErrnoIo - if other error happens during the operation of the underying file system
// The Errno returned is ErrnoSuccess except the following error conditions:
// * ErrnoBadf - if `fd` is invalid
// * ErrnoFault - if `resultNewoffset` is an invalid offset in `mod.Memory` due to the memory constraint
// * ErrnoInval - if `whence` is an invalid value
// * ErrnoIo - if other error happens during the operation of the underying file system
//
// For example, if fd 3 is a file with offset 0, and
// parameters fd=3, offset=4, whence=0 (=io.SeekStart), resultNewOffset=1,
@@ -1080,16 +968,14 @@ func (a *wasi) FdRenumber(ctx context.Context, mod api.Module, fd, to uint32) Er
// resultNewoffset --^
//
// See io.Seeker
// Note: importFdSeek shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importFdSeek shows this signature in the WebAssembly 1.0 Text Format.
// Note: This is similar to `lseek` in POSIX.
// 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 {
_, fsc := sysFSCtx(ctx, mod)
var seeker io.Seeker
// Check to see if the file descriptor is available
if f, ok := fsc.OpenedFile(fd); !ok || f.File == nil {
if f, ok := getSysCtx(mod).FS(ctx).OpenedFile(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 {
@@ -1129,10 +1015,10 @@ func (a *wasi) FdTell(ctx context.Context, mod api.Module, fd, resultOffset uint
// * iovsCount - the count of memory offset, size pairs to read sequentially starting at iovs.
// * resultSize - the offset in `mod.Memory` to write the number of bytes written
//
// The wasi_snapshot_preview1.Errno returned is wasi_snapshot_preview1.ErrnoSuccess except the following error conditions:
// * wasi_snapshot_preview1.ErrnoBadf - if `fd` is invalid
// * wasi_snapshot_preview1.ErrnoFault - if `iovs` or `resultSize` contain an invalid offset due to the memory constraint
// * wasi_snapshot_preview1.ErrnoIo - if an IO related error happens during the operation
// The Errno returned is ErrnoSuccess except the following error conditions:
// * ErrnoBadf - if `fd` is invalid
// * ErrnoFault - if `iovs` or `resultSize` contain an invalid offset due to the memory constraint
// * ErrnoIo - if an IO related error happens during the operation
//
// For example, this function needs to first read `iovs` to determine what to write to `fd`. If
// parameters iovs=1 iovsCount=2, this function reads two offset/length pairs from `mod.Memory`:
@@ -1164,30 +1050,16 @@ func (a *wasi) FdTell(ctx context.Context, mod api.Module, fd, resultOffset uint
// []byte{ 0..24, ?, 6, 0, 0, 0', ? }
// resultSize --^
//
// Note: importFdWrite shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importFdWrite shows this signature in the WebAssembly 1.0 Text Format.
// Note: This is similar to `writev` in POSIX.
// See FdRead
// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#ciovec
// 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 {
sysCtx, fsCtx := sysFSCtx(ctx, mod)
var writer io.Writer
switch fd {
case fdStdout:
writer = sysCtx.Stdout()
case fdStderr:
writer = sysCtx.Stderr()
default:
// Check to see if the file descriptor is available
if f, ok := fsCtx.OpenedFile(fd); !ok || f.File == nil {
return ErrnoBadf
// 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 ErrnoBadf
}
writer := fdWriter(ctx, mod, fd)
if writer == nil {
return ErrnoBadf
}
var nwritten uint32
@@ -1217,6 +1089,39 @@ 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(fd); !ok || f.File == nil {
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(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
@@ -1250,13 +1155,13 @@ func (a *wasi) PathLink(ctx context.Context, mod api.Module, oldFd, oldFlags, ol
// * resultOpenedFd - the offset in `mod.Memory` to write the newly created file descriptor to.
// * The result FD value is guaranteed to be less than 2**31
//
// The wasi_snapshot_preview1.Errno returned is wasi_snapshot_preview1.ErrnoSuccess except the following error conditions:
// * wasi_snapshot_preview1.ErrnoBadf - if `fd` is invalid
// * wasi_snapshot_preview1.ErrnoFault - if `resultOpenedFd` contains an invalid offset due to the memory constraint
// * wasi_snapshot_preview1.ErrnoNoent - if `path` does not exist.
// * wasi_snapshot_preview1.ErrnoExist - if `path` exists, while `oFlags` requires that it must not.
// * wasi_snapshot_preview1.ErrnoNotdir - if `path` is not a directory, while `oFlags` requires that it must be.
// * wasi_snapshot_preview1.ErrnoIo - if other error happens during the operation of the underying file system.
// The Errno returned is ErrnoSuccess except the following error conditions:
// * ErrnoBadf - if `fd` is invalid
// * ErrnoFault - if `resultOpenedFd` contains an invalid offset due to the memory constraint
// * ErrnoNoent - if `path` does not exist.
// * ErrnoExist - if `path` exists, while `oFlags` requires that it must not.
// * ErrnoNotdir - if `path` is not a directory, while `oFlags` requires that it must be.
// * ErrnoIo - if other error happens during the operation of the underying file system.
//
// For example, this function needs to first read `path` to determine the file to open.
// If parameters `path` = 1, `pathLen` = 6, and the path is "wazero", PathOpen reads the path from `mod.Memory`:
@@ -1276,7 +1181,7 @@ func (a *wasi) PathLink(ctx context.Context, mod api.Module, oldFd, oldFlags, ol
// []byte{ 0..6, ?, 5, 0, 0, 0, ?}
// resultOpenedFd --^
//
// Note: importPathOpen shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importPathOpen shows this signature in the WebAssembly 1.0 Text Format.
// Note: This is similar to `openat` in POSIX.
// Note: The returned file descriptor is not guaranteed to be the lowest-numbered file
// Note: Rights will never be implemented per https://github.com/WebAssembly/WASI/issues/469#issuecomment-1045251844
@@ -1284,8 +1189,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) {
_, fsc := sysFSCtx(ctx, mod)
fsc := getSysCtx(mod).FS(ctx)
dir, ok := fsc.OpenedFile(fd)
if !ok || dir.FS == nil {
return ErrnoBadf
@@ -1339,11 +1243,6 @@ func (a *wasi) PathUnlinkFile(ctx context.Context, mod api.Module, fd, path, pat
return ErrnoNosys // stubbed for GrainLang per #271
}
// PollOneoff is the WASI function named functionPollOneoff
func (a *wasi) PollOneoff(ctx context.Context, mod api.Module, in, out, nsubscriptions, resultNevents uint32) Errno {
return ErrnoNosys // stubbed for GrainLang per #271
}
// ProcExit is the WASI function that terminates the execution of the module with an exit code.
// An exit code of 0 indicates successful termination. The meanings of other values are not defined by WASI.
//
@@ -1351,7 +1250,7 @@ func (a *wasi) PollOneoff(ctx context.Context, mod api.Module, in, out, nsubscri
//
// In wazero, this calls api.Module CloseWithExitCode.
//
// Note: importProcExit shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// Note: importProcExit shows this signature in the WebAssembly 1.0 Text Format.
// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit
func (a *wasi) ProcExit(ctx context.Context, mod api.Module, exitCode uint32) {
_ = mod.CloseWithExitCode(ctx, exitCode)
@@ -1380,7 +1279,7 @@ func (a *wasi) SchedYield(mod api.Module) Errno {
// []byte{?, 0x53, 0x8c, 0x7f, 0x96, 0xb1, ?}
// buf --^
//
// Note: importRandomGet shows this signature in the WebAssembly 1.0 (20191205) Text Format.
// 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()
@@ -1413,18 +1312,6 @@ func (a *wasi) SockShutdown(ctx context.Context, mod api.Module, fd, how uint32)
return ErrnoNosys // stubbed for GrainLang per #271
}
const (
fdStdin = 0
fdStdout = 1
fdStderr = 2
)
// https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-clockid-enumu32
const (
clockIDRealtime = 0
clockIDMonotonic = 1
)
func getSysCtx(mod api.Module) *sys.Context {
if internal, ok := mod.(*wasm.CallContext); !ok {
panic(fmt.Errorf("unsupported wasm.Module implementation: %v", mod))
@@ -1433,22 +1320,6 @@ func getSysCtx(mod api.Module) *sys.Context {
}
}
func sysFSCtx(ctx context.Context, mod api.Module) (*sys.Context, *sys.FSContext) {
if internal, ok := mod.(*wasm.CallContext); !ok {
panic(fmt.Errorf("unsupported wasm.Module implementation: %v", mod))
} else {
// Override Context when it is passed via context
if fsValue := ctx.Value(sys.FSKey{}); fsValue != nil {
fsCtx, ok := fsValue.(*sys.FSContext)
if !ok {
panic(fmt.Errorf("unsupported fs key: %v", fsValue))
}
return internal.Sys, fsCtx
}
return internal.Sys, internal.Sys.FS()
}
}
func openFileEntry(rootFS fs.FS, pathName string) (*sys.FileEntry, Errno) {
f, err := rootFS.Open(pathName)
if err != nil {

View File

@@ -21,11 +21,11 @@ var testMem = &wasm.MemoryInstance{
},
}
func Test_EnvironGet(t *testing.T) {
sys, err := newSysContext(nil, []string{"a=b", "b=cd"}, nil)
func Test_Benchmark_EnvironGet(t *testing.T) {
sysCtx, err := newSysContext(nil, []string{"a=b", "b=cd"}, nil)
require.NoError(t, err)
mod := newModule(make([]byte, 20), sys)
mod := newModule(make([]byte, 20), sysCtx)
environGet := (&wasi{}).EnvironGet
require.Equal(t, ErrnoSuccess, environGet(testCtx, mod, 11, 1))
@@ -33,7 +33,7 @@ func Test_EnvironGet(t *testing.T) {
}
func Benchmark_EnvironGet(b *testing.B) {
sys, err := newSysContext(nil, []string{"a=b", "b=cd"}, nil)
sysCtx, err := newSysContext(nil, []string{"a=b", "b=cd"}, nil)
if err != nil {
b.Fatal(err)
}
@@ -46,7 +46,7 @@ func Benchmark_EnvironGet(b *testing.B) {
1, 0, 0, 0, // little endian-encoded offset of "a=b"
5, 0, 0, 0, // little endian-encoded offset of "b=cd"
0,
}, sys)
}, sysCtx)
environGet := (&wasi{}).EnvironGet
b.Run("EnvironGet", func(b *testing.B) {

View File

@@ -37,7 +37,7 @@ var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
var a = &wasi{}
func TestSnapshotPreview1_ArgsGet(t *testing.T) {
func Test_ArgsGet(t *testing.T) {
sysCtx, err := newSysContext([]string{"a", "bc"}, nil, nil)
require.NoError(t, err)
@@ -81,7 +81,7 @@ func TestSnapshotPreview1_ArgsGet(t *testing.T) {
})
}
func TestSnapshotPreview1_ArgsGet_Errors(t *testing.T) {
func Test_ArgsGet_Errors(t *testing.T) {
sysCtx, err := newSysContext([]string{"a", "bc"}, nil, nil)
require.NoError(t, err)
@@ -131,7 +131,7 @@ func TestSnapshotPreview1_ArgsGet_Errors(t *testing.T) {
}
}
func TestSnapshotPreview1_ArgsSizesGet(t *testing.T) {
func Test_ArgsSizesGet(t *testing.T) {
sysCtx, err := newSysContext([]string{"a", "bc"}, nil, nil)
require.NoError(t, err)
@@ -174,7 +174,7 @@ func TestSnapshotPreview1_ArgsSizesGet(t *testing.T) {
})
}
func TestSnapshotPreview1_ArgsSizesGet_Errors(t *testing.T) {
func Test_ArgsSizesGet_Errors(t *testing.T) {
sysCtx, err := newSysContext([]string{"a", "bc"}, nil, nil)
require.NoError(t, err)
@@ -221,7 +221,7 @@ func TestSnapshotPreview1_ArgsSizesGet_Errors(t *testing.T) {
}
}
func TestSnapshotPreview1_EnvironGet(t *testing.T) {
func Test_EnvironGet(t *testing.T) {
sysCtx, err := newSysContext(nil, []string{"a=b", "b=cd"}, nil)
require.NoError(t, err)
@@ -266,7 +266,7 @@ func TestSnapshotPreview1_EnvironGet(t *testing.T) {
})
}
func TestSnapshotPreview1_EnvironGet_Errors(t *testing.T) {
func Test_EnvironGet_Errors(t *testing.T) {
sysCtx, err := newSysContext(nil, []string{"a=bc", "b=cd"}, nil)
require.NoError(t, err)
@@ -315,7 +315,7 @@ func TestSnapshotPreview1_EnvironGet_Errors(t *testing.T) {
}
}
func TestSnapshotPreview1_EnvironSizesGet(t *testing.T) {
func Test_EnvironSizesGet(t *testing.T) {
sysCtx, err := newSysContext(nil, []string{"a=b", "b=cd"}, nil)
require.NoError(t, err)
@@ -358,7 +358,7 @@ func TestSnapshotPreview1_EnvironSizesGet(t *testing.T) {
})
}
func TestSnapshotPreview1_EnvironSizesGet_Errors(t *testing.T) {
func Test_EnvironSizesGet_Errors(t *testing.T) {
sysCtx, err := newSysContext(nil, []string{"a=b", "b=cd"}, nil)
require.NoError(t, err)
@@ -405,268 +405,8 @@ func TestSnapshotPreview1_EnvironSizesGet_Errors(t *testing.T) {
}
}
// TestSnapshotPreview1_ClockResGet only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_ClockResGet(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionClockResGet, importClockResGet, nil)
defer mod.Close(testCtx)
resultResolution := uint32(1) // arbitrary offset
expectedMemoryMicro := []byte{
'?', // resultResolution is after this
0xe8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // little endian-encoded resolution (fixed to 1000).
'?', // stopped after encoding
}
expectedMemoryNano := []byte{
'?', // resultResolution is after this
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // little endian-encoded resolution (fixed to 1000).
'?', // stopped after encoding
}
tests := []struct {
name string
clockID uint64
expectedMemory []byte
invocation func(clockID uint64) Errno
}{
{
name: "wasi.ClockResGet",
clockID: 0,
expectedMemory: expectedMemoryMicro,
invocation: func(clockID uint64) Errno {
return a.ClockResGet(testCtx, mod, uint32(clockID), resultResolution)
},
},
{
name: "wasi.ClockResGet",
clockID: 1,
expectedMemory: expectedMemoryNano,
invocation: func(clockID uint64) Errno {
return a.ClockResGet(testCtx, mod, uint32(clockID), resultResolution)
},
},
{
name: functionClockResGet,
clockID: 0,
expectedMemory: expectedMemoryMicro,
invocation: func(clockID uint64) Errno {
results, err := fn.Call(testCtx, clockID, uint64(resultResolution))
require.NoError(t, err)
return Errno(results[0]) // results[0] is the errno
},
},
{
name: functionClockResGet,
clockID: 1,
expectedMemory: expectedMemoryNano,
invocation: func(clockID uint64) Errno {
results, err := fn.Call(testCtx, clockID, uint64(resultResolution))
require.NoError(t, err)
return Errno(results[0]) // results[0] is the errno
},
},
}
for _, tt := range tests {
tc := tt
t.Run(fmt.Sprintf("%v/clockID=%v", tc.name, tc.clockID), func(t *testing.T) {
maskMemory(t, testCtx, mod, len(tc.expectedMemory))
errno := tc.invocation(tc.clockID)
require.Equal(t, ErrnoSuccess, errno, ErrnoName(errno))
actual, ok := mod.Memory().Read(testCtx, 0, uint32(len(tc.expectedMemory)))
require.True(t, ok)
require.Equal(t, tc.expectedMemory, actual)
})
}
}
func TestSnapshotPreview1_ClockResGet_Unsupported(t *testing.T) {
resultResolution := uint32(1) // arbitrary offset
mod, fn := instantiateModule(testCtx, t, functionClockResGet, importClockResGet, nil)
defer mod.Close(testCtx)
tests := []struct {
name string
clockID uint64
}{
{
name: "process cputime",
clockID: 2,
},
{
name: "thread cputime",
clockID: 3,
},
{
name: "undefined",
clockID: 100,
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
results, err := fn.Call(testCtx, tc.clockID, uint64(resultResolution))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
require.Equal(t, ErrnoNosys, errno, ErrnoName(errno))
})
}
}
func TestSnapshotPreview1_ClockTimeGet(t *testing.T) {
resultTimestamp := uint32(1) // arbitrary offset
mod, fn := instantiateModule(testCtx, t, functionClockTimeGet, importClockTimeGet, nil)
defer mod.Close(testCtx)
clocks := []struct {
clock string
id uint32
expectedMemory []byte
}{
{
clock: "Realtime",
id: clockIDRealtime,
expectedMemory: []byte{
'?', // resultTimestamp is after this
0x0, 0x0, 0x1f, 0xa6, 0x70, 0xfc, 0xc5, 0x16, // little endian-encoded epochNanos
'?', // stopped after encoding
},
},
{
clock: "Monotonic",
id: clockIDMonotonic,
expectedMemory: []byte{
'?', // resultTimestamp is after this
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // fake nanotime starts at zero
'?', // stopped after encoding
},
},
}
for _, c := range clocks {
cc := c
t.Run(cc.clock, func(t *testing.T) {
tests := []struct {
name string
invocation func() Errno
}{
{
name: "wasi.ClockTimeGet",
invocation: func() Errno {
return a.ClockTimeGet(testCtx, mod, cc.id, 0 /* TODO: precision */, resultTimestamp)
},
},
{
name: functionClockTimeGet,
invocation: func() Errno {
results, err := fn.Call(testCtx, uint64(cc.id), 0 /* TODO: precision */, uint64(resultTimestamp))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
return errno
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
// Reset the fake clock
sysCtx, err := newSysContext(nil, nil, nil)
require.NoError(t, err)
mod.(*wasm.CallContext).Sys = sysCtx
maskMemory(t, testCtx, mod, len(cc.expectedMemory))
errno := tc.invocation()
require.Zero(t, errno, ErrnoName(errno))
actual, ok := mod.Memory().Read(testCtx, 0, uint32(len(cc.expectedMemory)))
require.True(t, ok)
require.Equal(t, cc.expectedMemory, actual)
})
}
})
}
}
func TestSnapshotPreview1_ClockTimeGet_Unsupported(t *testing.T) {
resultTimestamp := uint32(1) // arbitrary offset
mod, fn := instantiateModule(testCtx, t, functionClockTimeGet, importClockTimeGet, nil)
defer mod.Close(testCtx)
tests := []struct {
name string
clockID uint64
}{
{
name: "process cputime",
clockID: 2,
},
{
name: "thread cputime",
clockID: 3,
},
{
name: "undefined",
clockID: 100,
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
results, err := fn.Call(testCtx, tc.clockID, 0 /* TODO: precision */, uint64(resultTimestamp))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
require.Equal(t, ErrnoNosys, errno, ErrnoName(errno))
})
}
}
func TestSnapshotPreview1_ClockTimeGet_Errors(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionClockTimeGet, importClockTimeGet, nil)
defer mod.Close(testCtx)
memorySize := mod.Memory().Size(testCtx)
tests := []struct {
name string
resultTimestamp uint32
argvBufSize uint32
}{
{
name: "resultTimestamp out-of-memory",
resultTimestamp: memorySize,
},
{
name: "resultTimestamp exceeds the maximum valid address by 1",
resultTimestamp: memorySize - 4 + 1, // 4 is the size of uint32, the type of the count of args
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
results, err := fn.Call(testCtx, 0 /* TODO: id */, 0 /* TODO: precision */, uint64(tc.resultTimestamp))
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
require.Equal(t, ErrnoFault, errno, ErrnoName(errno))
})
}
}
// TestSnapshotPreview1_FdAdvise only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdAdvise(t *testing.T) {
// Test_FdAdvise only tests it is stubbed for GrainLang per #271
func Test_FdAdvise(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdAdvise, importFdAdvise, nil)
defer mod.Close(testCtx)
@@ -683,8 +423,8 @@ func TestSnapshotPreview1_FdAdvise(t *testing.T) {
})
}
// TestSnapshotPreview1_FdAllocate only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdAllocate(t *testing.T) {
// Test_FdAllocate only tests it is stubbed for GrainLang per #271
func Test_FdAllocate(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdAllocate, importFdAllocate, nil)
defer mod.Close(testCtx)
@@ -701,7 +441,7 @@ func TestSnapshotPreview1_FdAllocate(t *testing.T) {
})
}
func TestSnapshotPreview1_FdClose(t *testing.T) {
func Test_FdClose(t *testing.T) {
fdToClose := uint32(3) // arbitrary fd
fdToKeep := uint32(4) // another arbitrary fd
@@ -726,7 +466,7 @@ func TestSnapshotPreview1_FdClose(t *testing.T) {
verify := func(mod api.Module) {
// Verify fdToClose is closed and removed from the opened FDs.
_, fsc := sysFSCtx(testCtx, mod)
fsc := getSysCtx(mod).FS(testCtx)
_, ok := fsc.OpenedFile(fdToClose)
require.False(t, ok)
@@ -764,8 +504,8 @@ func TestSnapshotPreview1_FdClose(t *testing.T) {
})
}
// TestSnapshotPreview1_FdDatasync only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdDatasync(t *testing.T) {
// Test_FdDatasync only tests it is stubbed for GrainLang per #271
func Test_FdDatasync(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdDatasync, importFdDatasync, nil)
defer mod.Close(testCtx)
@@ -782,14 +522,10 @@ func TestSnapshotPreview1_FdDatasync(t *testing.T) {
})
}
// TODO: TestSnapshotPreview1_FdFdstatGet TestSnapshotPreview1_FdFdstatGet_Errors
func TestSnapshotPreview1_FdFdstatGet(t *testing.T) {
t.Skip("TODO")
_ = importFdFdstatGet // stop linter complaint until we implement this
}
// TODO: Test_FdFdstatGet Test_FdFdstatGet_Errors
// TestSnapshotPreview1_FdFdstatSetFlags only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdFdstatSetFlags(t *testing.T) {
// Test_FdFdstatSetFlags only tests it is stubbed for GrainLang per #271
func Test_FdFdstatSetFlags(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdFdstatSetFlags, importFdFdstatSetFlags, nil)
defer mod.Close(testCtx)
@@ -806,8 +542,8 @@ func TestSnapshotPreview1_FdFdstatSetFlags(t *testing.T) {
})
}
// TestSnapshotPreview1_FdFdstatSetRights only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdFdstatSetRights(t *testing.T) {
// Test_FdFdstatSetRights only tests it is stubbed for GrainLang per #271
func Test_FdFdstatSetRights(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdFdstatSetRights, importFdFdstatSetRights, nil)
defer mod.Close(testCtx)
@@ -824,8 +560,8 @@ func TestSnapshotPreview1_FdFdstatSetRights(t *testing.T) {
})
}
// TestSnapshotPreview1_FdFilestatGet only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdFilestatGet(t *testing.T) {
// Test_FdFilestatGet only tests it is stubbed for GrainLang per #271
func Test_FdFilestatGet(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdFilestatGet, importFdFilestatGet, nil)
defer mod.Close(testCtx)
@@ -842,8 +578,8 @@ func TestSnapshotPreview1_FdFilestatGet(t *testing.T) {
})
}
// TestSnapshotPreview1_FdFilestatSetSize only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdFilestatSetSize(t *testing.T) {
// Test_FdFilestatSetSize only tests it is stubbed for GrainLang per #271
func Test_FdFilestatSetSize(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdFilestatSetSize, importFdFilestatSetSize, nil)
defer mod.Close(testCtx)
@@ -860,8 +596,8 @@ func TestSnapshotPreview1_FdFilestatSetSize(t *testing.T) {
})
}
// TestSnapshotPreview1_FdFilestatSetTimes only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdFilestatSetTimes(t *testing.T) {
// Test_FdFilestatSetTimes only tests it is stubbed for GrainLang per #271
func Test_FdFilestatSetTimes(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdFilestatSetTimes, importFdFilestatSetTimes, nil)
defer mod.Close(testCtx)
@@ -878,8 +614,8 @@ func TestSnapshotPreview1_FdFilestatSetTimes(t *testing.T) {
})
}
// TestSnapshotPreview1_FdPread only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdPread(t *testing.T) {
// Test_FdPread only tests it is stubbed for GrainLang per #271
func Test_FdPread(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdPread, importFdPread, nil)
defer mod.Close(testCtx)
@@ -896,7 +632,7 @@ func TestSnapshotPreview1_FdPread(t *testing.T) {
})
}
func TestSnapshotPreview1_FdPrestatGet(t *testing.T) {
func Test_FdPrestatGet(t *testing.T) {
fd := uint32(3) // arbitrary fd after 0, 1, and 2, that are stdin/out/err
pathName := "/tmp"
@@ -941,7 +677,7 @@ func TestSnapshotPreview1_FdPrestatGet(t *testing.T) {
})
}
func TestSnapshotPreview1_FdPrestatGet_Errors(t *testing.T) {
func Test_FdPrestatGet_Errors(t *testing.T) {
fd := uint32(3) // fd 3 will be opened for the "/tmp" directory after 0, 1, and 2, that are stdin/out/err
validAddress := uint32(0) // Arbitrary valid address as arguments to fd_prestat_get. We chose 0 here.
@@ -984,7 +720,7 @@ func TestSnapshotPreview1_FdPrestatGet_Errors(t *testing.T) {
}
}
func TestSnapshotPreview1_FdPrestatDirName(t *testing.T) {
func Test_FdPrestatDirName(t *testing.T) {
fd := uint32(3) // arbitrary fd after 0, 1, and 2, that are stdin/out/err
sysCtx, err := newSysContext(nil, nil, map[uint32]*internalsys.FileEntry{fd: {Path: "/tmp"}})
@@ -1026,7 +762,7 @@ func TestSnapshotPreview1_FdPrestatDirName(t *testing.T) {
})
}
func TestSnapshotPreview1_FdPrestatDirName_Errors(t *testing.T) {
func Test_FdPrestatDirName_Errors(t *testing.T) {
fd := uint32(3) // arbitrary fd after 0, 1, and 2, that are stdin/out/err
sysCtx, err := newSysContext(nil, nil, map[uint32]*internalsys.FileEntry{fd: {Path: "/tmp"}})
require.NoError(t, err)
@@ -1086,8 +822,8 @@ func TestSnapshotPreview1_FdPrestatDirName_Errors(t *testing.T) {
}
}
// TestSnapshotPreview1_FdPwrite only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdPwrite(t *testing.T) {
// Test_FdPwrite only tests it is stubbed for GrainLang per #271
func Test_FdPwrite(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdPwrite, importFdPwrite, nil)
defer mod.Close(testCtx)
@@ -1104,7 +840,7 @@ func TestSnapshotPreview1_FdPwrite(t *testing.T) {
})
}
func TestSnapshotPreview1_FdRead(t *testing.T) {
func Test_FdRead(t *testing.T) {
fd := uint32(3) // arbitrary fd after 0, 1, and 2, that are stdin/out/err
iovs := uint32(1) // arbitrary offset
initialMemory := []byte{
@@ -1127,7 +863,7 @@ func TestSnapshotPreview1_FdRead(t *testing.T) {
'?',
)
// TestSnapshotPreview1_FdRead uses a matrix because setting up test files is complicated and has to be clean each time.
// Test_FdRead uses a matrix because setting up test files is complicated and has to be clean each time.
type fdReadFn func(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) Errno
tests := []struct {
name string
@@ -1173,7 +909,7 @@ func TestSnapshotPreview1_FdRead(t *testing.T) {
}
}
func TestSnapshotPreview1_FdRead_Errors(t *testing.T) {
func Test_FdRead_Errors(t *testing.T) {
validFD := uint32(3) // arbitrary valid fd after 0, 1, and 2, that are stdin/out/err
file, testFS := createFile(t, "test_path", []byte{}) // file with empty contents
@@ -1265,8 +1001,8 @@ func TestSnapshotPreview1_FdRead_Errors(t *testing.T) {
}
}
// TestSnapshotPreview1_FdReaddir only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdReaddir(t *testing.T) {
// Test_FdReaddir only tests it is stubbed for GrainLang per #271
func Test_FdReaddir(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdReaddir, importFdReaddir, nil)
defer mod.Close(testCtx)
@@ -1283,8 +1019,8 @@ func TestSnapshotPreview1_FdReaddir(t *testing.T) {
})
}
// TestSnapshotPreview1_FdRenumber only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdRenumber(t *testing.T) {
// Test_FdRenumber only tests it is stubbed for GrainLang per #271
func Test_FdRenumber(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdRenumber, importFdRenumber, nil)
defer mod.Close(testCtx)
@@ -1301,7 +1037,7 @@ func TestSnapshotPreview1_FdRenumber(t *testing.T) {
})
}
func TestSnapshotPreview1_FdSeek(t *testing.T) {
func Test_FdSeek(t *testing.T) {
fd := uint32(3) // arbitrary fd after 0, 1, and 2, that are stdin/out/err
resultNewoffset := uint32(1) // arbitrary offset in `ctx.Memory` for the new offset value
file, testFS := createFile(t, "test_path", []byte("wazero")) // arbitrary non-empty contents
@@ -1311,12 +1047,12 @@ func TestSnapshotPreview1_FdSeek(t *testing.T) {
})
require.NoError(t, err)
fsCtx := sysCtx.FS()
fsCtx := sysCtx.FS(testCtx)
mod, fn := instantiateModule(testCtx, t, functionFdSeek, importFdSeek, sysCtx)
defer mod.Close(testCtx)
// TestSnapshotPreview1_FdSeek uses a matrix because setting up test files is complicated and has to be clean each time.
// Test_FdSeek uses a matrix because setting up test files is complicated and has to be clean each time.
type fdSeekFn func(ctx context.Context, mod api.Module, fd uint32, offset uint64, whence, resultNewOffset uint32) Errno
seekFns := []struct {
name string
@@ -1410,7 +1146,7 @@ func TestSnapshotPreview1_FdSeek(t *testing.T) {
}
}
func TestSnapshotPreview1_FdSeek_Errors(t *testing.T) {
func Test_FdSeek_Errors(t *testing.T) {
validFD := uint32(3) // arbitrary valid fd after 0, 1, and 2, that are stdin/out/err
file, testFS := createFile(t, "test_path", []byte("wazero")) // arbitrary valid file with non-empty contents
@@ -1460,8 +1196,8 @@ func TestSnapshotPreview1_FdSeek_Errors(t *testing.T) {
}
// TestSnapshotPreview1_FdSync only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdSync(t *testing.T) {
// Test_FdSync only tests it is stubbed for GrainLang per #271
func Test_FdSync(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdSync, importFdSync, nil)
defer mod.Close(testCtx)
@@ -1478,8 +1214,8 @@ func TestSnapshotPreview1_FdSync(t *testing.T) {
})
}
// TestSnapshotPreview1_FdTell only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_FdTell(t *testing.T) {
// Test_FdTell only tests it is stubbed for GrainLang per #271
func Test_FdTell(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionFdTell, importFdTell, nil)
defer mod.Close(testCtx)
@@ -1496,7 +1232,7 @@ func TestSnapshotPreview1_FdTell(t *testing.T) {
})
}
func TestSnapshotPreview1_FdWrite(t *testing.T) {
func Test_FdWrite(t *testing.T) {
fd := uint32(3) // arbitrary fd after 0, 1, and 2, that are stdin/out/err
iovs := uint32(1) // arbitrary offset
initialMemory := []byte{
@@ -1519,7 +1255,7 @@ func TestSnapshotPreview1_FdWrite(t *testing.T) {
'?',
)
// TestSnapshotPreview1_FdWrite uses a matrix because setting up test files is complicated and has to be clean each time.
// Test_FdWrite uses a matrix because setting up test files is complicated and has to be clean each time.
type fdWriteFn func(ctx context.Context, mod api.Module, fd, iovs, iovsCount, resultSize uint32) Errno
tests := []struct {
name string
@@ -1573,7 +1309,7 @@ func TestSnapshotPreview1_FdWrite(t *testing.T) {
}
}
func TestSnapshotPreview1_FdWrite_Errors(t *testing.T) {
func Test_FdWrite_Errors(t *testing.T) {
validFD := uint32(3) // arbitrary valid fd after 0, 1, and 2, that are stdin/out/err
tmpDir := t.TempDir() // open before loop to ensure no locking problems.
@@ -1651,8 +1387,8 @@ func TestSnapshotPreview1_FdWrite_Errors(t *testing.T) {
}
}
// TestSnapshotPreview1_PathCreateDirectory only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathCreateDirectory(t *testing.T) {
// Test_PathCreateDirectory only tests it is stubbed for GrainLang per #271
func Test_PathCreateDirectory(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathCreateDirectory, importPathCreateDirectory, nil)
defer mod.Close(testCtx)
@@ -1669,8 +1405,8 @@ func TestSnapshotPreview1_PathCreateDirectory(t *testing.T) {
})
}
// TestSnapshotPreview1_PathFilestatGet only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathFilestatGet(t *testing.T) {
// Test_PathFilestatGet only tests it is stubbed for GrainLang per #271
func Test_PathFilestatGet(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathFilestatGet, importPathFilestatGet, nil)
defer mod.Close(testCtx)
@@ -1687,8 +1423,8 @@ func TestSnapshotPreview1_PathFilestatGet(t *testing.T) {
})
}
// TestSnapshotPreview1_PathFilestatSetTimes only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathFilestatSetTimes(t *testing.T) {
// Test_PathFilestatSetTimes only tests it is stubbed for GrainLang per #271
func Test_PathFilestatSetTimes(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathFilestatSetTimes, importPathFilestatSetTimes, nil)
defer mod.Close(testCtx)
@@ -1705,8 +1441,8 @@ func TestSnapshotPreview1_PathFilestatSetTimes(t *testing.T) {
})
}
// TestSnapshotPreview1_PathLink only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathLink(t *testing.T) {
// Test_PathLink only tests it is stubbed for GrainLang per #271
func Test_PathLink(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathLink, importPathLink, nil)
defer mod.Close(testCtx)
@@ -1723,7 +1459,7 @@ func TestSnapshotPreview1_PathLink(t *testing.T) {
})
}
func TestSnapshotPreview1_PathOpen(t *testing.T) {
func Test_PathOpen(t *testing.T) {
type pathOpenArgs struct {
fd uint32
dirflags uint32
@@ -1780,7 +1516,7 @@ func TestSnapshotPreview1_PathOpen(t *testing.T) {
require.Equal(t, expectedMemory, actual)
// verify the file was actually opened
_, fsc := sysFSCtx(ctx, mod)
fsc := getSysCtx(mod).FS(ctx)
f, ok := fsc.OpenedFile(expectedFD)
require.True(t, ok)
require.Equal(t, pathName, f.Path)
@@ -1832,7 +1568,7 @@ func TestSnapshotPreview1_PathOpen(t *testing.T) {
})
}
func TestSnapshotPreview1_PathOpen_Errors(t *testing.T) {
func Test_PathOpen_Errors(t *testing.T) {
validFD := uint32(3) // arbitrary valid fd after 0, 1, and 2, that are stdin/out/err
pathName := "wazero"
testFS := fstest.MapFS{pathName: &fstest.MapFile{Mode: os.ModeDir}}
@@ -1899,8 +1635,8 @@ func TestSnapshotPreview1_PathOpen_Errors(t *testing.T) {
}
}
// TestSnapshotPreview1_PathReadlink only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathReadlink(t *testing.T) {
// Test_PathReadlink only tests it is stubbed for GrainLang per #271
func Test_PathReadlink(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathReadlink, importPathReadlink, nil)
defer mod.Close(testCtx)
@@ -1917,8 +1653,8 @@ func TestSnapshotPreview1_PathReadlink(t *testing.T) {
})
}
// TestSnapshotPreview1_PathRemoveDirectory only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathRemoveDirectory(t *testing.T) {
// Test_PathRemoveDirectory only tests it is stubbed for GrainLang per #271
func Test_PathRemoveDirectory(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathRemoveDirectory, importPathRemoveDirectory, nil)
defer mod.Close(testCtx)
@@ -1935,8 +1671,8 @@ func TestSnapshotPreview1_PathRemoveDirectory(t *testing.T) {
})
}
// TestSnapshotPreview1_PathRename only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathRename(t *testing.T) {
// Test_PathRename only tests it is stubbed for GrainLang per #271
func Test_PathRename(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathRename, importPathRename, nil)
defer mod.Close(testCtx)
@@ -1953,8 +1689,8 @@ func TestSnapshotPreview1_PathRename(t *testing.T) {
})
}
// TestSnapshotPreview1_PathSymlink only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathSymlink(t *testing.T) {
// Test_PathSymlink only tests it is stubbed for GrainLang per #271
func Test_PathSymlink(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathSymlink, importPathSymlink, nil)
defer mod.Close(testCtx)
@@ -1971,8 +1707,8 @@ func TestSnapshotPreview1_PathSymlink(t *testing.T) {
})
}
// TestSnapshotPreview1_PathUnlinkFile only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PathUnlinkFile(t *testing.T) {
// Test_PathUnlinkFile only tests it is stubbed for GrainLang per #271
func Test_PathUnlinkFile(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPathUnlinkFile, importPathUnlinkFile, nil)
defer mod.Close(testCtx)
@@ -1989,25 +1725,7 @@ func TestSnapshotPreview1_PathUnlinkFile(t *testing.T) {
})
}
// TestSnapshotPreview1_PollOneoff only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_PollOneoff(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionPollOneoff, importPollOneoff, nil)
defer mod.Close(testCtx)
t.Run("wasi.PollOneoff", func(t *testing.T) {
errno := a.PollOneoff(testCtx, mod, 0, 0, 0, 0)
require.Equal(t, ErrnoNosys, errno, ErrnoName(errno))
})
t.Run(functionPollOneoff, func(t *testing.T) {
results, err := fn.Call(testCtx, 0, 0, 0, 0)
require.NoError(t, err)
errno := Errno(results[0]) // results[0] is the errno
require.Equal(t, ErrnoNosys, errno, ErrnoName(errno))
})
}
func TestSnapshotPreview1_ProcExit(t *testing.T) {
func Test_ProcExit(t *testing.T) {
tests := []struct {
name string
exitCode uint32
@@ -2038,8 +1756,8 @@ func TestSnapshotPreview1_ProcExit(t *testing.T) {
}
}
// TestSnapshotPreview1_ProcRaise only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_ProcRaise(t *testing.T) {
// Test_ProcRaise only tests it is stubbed for GrainLang per #271
func Test_ProcRaise(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionProcRaise, importProcRaise, nil)
defer mod.Close(testCtx)
@@ -2056,8 +1774,8 @@ func TestSnapshotPreview1_ProcRaise(t *testing.T) {
})
}
// TestSnapshotPreview1_SchedYield only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_SchedYield(t *testing.T) {
// Test_SchedYield only tests it is stubbed for GrainLang per #271
func Test_SchedYield(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionSchedYield, importSchedYield, nil)
defer mod.Close(testCtx)
@@ -2074,7 +1792,7 @@ func TestSnapshotPreview1_SchedYield(t *testing.T) {
})
}
func TestSnapshotPreview1_RandomGet(t *testing.T) {
func Test_RandomGet(t *testing.T) {
expectedMemory := []byte{
'?', // `offset` is after this
0x53, 0x8c, 0x7f, 0x96, 0xb1, // random data from seed value of 42
@@ -2116,7 +1834,7 @@ func TestSnapshotPreview1_RandomGet(t *testing.T) {
})
}
func TestSnapshotPreview1_RandomGet_Errors(t *testing.T) {
func Test_RandomGet_Errors(t *testing.T) {
validAddress := uint32(0) // arbitrary valid address
mod, _ := instantiateModule(testCtx, t, functionRandomGet, importRandomGet, nil)
@@ -2152,7 +1870,7 @@ func TestSnapshotPreview1_RandomGet_Errors(t *testing.T) {
}
}
func TestSnapshotPreview1_RandomGet_SourceError(t *testing.T) {
func Test_RandomGet_SourceError(t *testing.T) {
tests := []struct {
name string
randSource io.Reader
@@ -2180,6 +1898,7 @@ func TestSnapshotPreview1_RandomGet_SourceError(t *testing.T) {
tc.randSource,
nil, 0,
nil, 0,
nil, // nanosleep
nil,
)
require.NoError(t, err)
@@ -2193,8 +1912,8 @@ func TestSnapshotPreview1_RandomGet_SourceError(t *testing.T) {
}
}
// TestSnapshotPreview1_SockRecv only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_SockRecv(t *testing.T) {
// Test_SockRecv only tests it is stubbed for GrainLang per #271
func Test_SockRecv(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionSockRecv, importSockRecv, nil)
defer mod.Close(testCtx)
@@ -2211,8 +1930,8 @@ func TestSnapshotPreview1_SockRecv(t *testing.T) {
})
}
// TestSnapshotPreview1_SockSend only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_SockSend(t *testing.T) {
// Test_SockSend only tests it is stubbed for GrainLang per #271
func Test_SockSend(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionSockSend, importSockSend, nil)
defer mod.Close(testCtx)
@@ -2229,8 +1948,8 @@ func TestSnapshotPreview1_SockSend(t *testing.T) {
})
}
// TestSnapshotPreview1_SockShutdown only tests it is stubbed for GrainLang per #271
func TestSnapshotPreview1_SockShutdown(t *testing.T) {
// Test_SockShutdown only tests it is stubbed for GrainLang per #271
func Test_SockShutdown(t *testing.T) {
mod, fn := instantiateModule(testCtx, t, functionSockShutdown, importSockShutdown, nil)
defer mod.Close(testCtx)
@@ -2300,6 +2019,7 @@ func newSysContext(args, environ []string, openedFiles map[uint32]*internalsys.F
deterministicRandomSource(),
nil, 0,
nil, 0,
nil, // nanosleep
openedFiles,
)
}