Skip to content

Commit

Permalink
wasi: backfills constants and TODOs for remaining functions (#222)
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Cole <adrian@tetrate.io>
  • Loading branch information
codefromthecrypt authored Feb 10, 2022
1 parent 9f2ba95 commit e6d841c
Show file tree
Hide file tree
Showing 13 changed files with 963 additions and 664 deletions.
8 changes: 4 additions & 4 deletions tests/codec/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ func newExample() *wasm.Module {
},
ImportSection: []*wasm.Import{
{
Module: "wasi_snapshot_preview1", Name: "args_sizes_get",
Module: "wasi_snapshot_preview1", Name: wasi.FunctionArgsSizesGet,
Kind: wasm.ImportKindFunc,
DescFunc: 0,
}, {
Module: "wasi_snapshot_preview1", Name: "fd_write",
Module: "wasi_snapshot_preview1", Name: wasi.FunctionFDWrite,
Kind: wasm.ImportKindFunc,
DescFunc: 2,
},
Expand All @@ -67,8 +67,8 @@ func newExample() *wasm.Module {
NameSection: &wasm.NameSection{
ModuleName: "example",
FunctionNames: wasm.NameMap{
{Index: wasm.Index(0), Name: "runtime.args_sizes_get"},
{Index: wasm.Index(1), Name: "runtime.fd_write"},
{Index: wasm.Index(0), Name: "wasi.args_sizes_get"},
{Index: wasm.Index(1), Name: "wasi.fd_write"},
{Index: wasm.Index(2), Name: "call_hello"},
{Index: wasm.Index(3), Name: "hello"},
{Index: wasm.Index(4), Name: "addInt"},
Expand Down
5 changes: 3 additions & 2 deletions tests/codec/testdata/example.wat
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
(type $i32i32_i32 (func (param $i i32) (param $j i32) (result i32)))

;; type use by symbolic ID, but no param names
(import "wasi_snapshot_preview1" "args_sizes_get" (func $runtime.args_sizes_get (type $i32i32_i32)))
(import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi.args_sizes_get (type $i32i32_i32)))
;; type use on an import func which adds param names on anonymous type
(import "wasi_snapshot_preview1" "fd_write" (func $runtime.fd_write
;; TODO correct the param names
(import "wasi_snapshot_preview1" "fd_write" (func $wasi.fd_write
(param $fd i32) (param $iovs_ptr i32) (param $iovs_len i32) (param $nwritten_ptr i32) (result i32)))

;; func call referencing a func not defined, yet
Expand Down
175 changes: 0 additions & 175 deletions wasi/errno.go

This file was deleted.

41 changes: 41 additions & 0 deletions wasi/strings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package wasi

import (
"fmt"
"unicode/utf8"
)

// nullTerminatedStrings holds null-terminated strings. It ensures that
// its length and total buffer size don't exceed the max of uint32.
// nullTerminatedStrings are convenience struct for args_get and environ_get. (environ_get is not implemented yet)
//
// A Null-terminated string is a byte string with a NULL suffix ("\x00").
// See https://en.wikipedia.org/wiki/Null-terminated_string
type nullTerminatedStrings struct {
// nullTerminatedValues are null-terminated values with a NULL suffix.
nullTerminatedValues [][]byte
totalBufSize uint32
}

// newNullTerminatedStrings creates a nullTerminatedStrings from the given string slice. It returns an error
// if the length or the total buffer size of the result WASIStringArray exceeds the maxBufSize
func newNullTerminatedStrings(maxBufSize uint32, args ...string) (*nullTerminatedStrings, error) {
if len(args) == 0 {
return &nullTerminatedStrings{nullTerminatedValues: [][]byte{}}, nil
}
var strings [][]byte // don't pre-allocate as this function is size bound
totalBufSize := uint32(0)
for i, arg := range args {
if !utf8.ValidString(arg) {
return nil, fmt.Errorf("arg[%d] is not a valid UTF-8 string", i)
}
argLen := uint64(len(arg)) + 1 // + 1 for "\x00"; uint64 in case this one arg is huge
nextSize := uint64(totalBufSize) + argLen
if nextSize > uint64(maxBufSize) {
return nil, fmt.Errorf("arg[%d] will exceed max buffer size %d", i, maxBufSize)
}
totalBufSize = uint32(nextSize)
strings = append(strings, append([]byte(arg), 0))
}
return &nullTerminatedStrings{nullTerminatedValues: strings, totalBufSize: totalBufSize}, nil
}
88 changes: 88 additions & 0 deletions wasi/strings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package wasi

import (
_ "embed"
"testing"

"github.com/stretchr/testify/require"
)

func TestNewNullTerminatedStrings(t *testing.T) {
emptyWASIStringArray := &nullTerminatedStrings{nullTerminatedValues: [][]byte{}}
tests := []struct {
name string
input []string
expected *nullTerminatedStrings
}{
{
name: "nil",
expected: emptyWASIStringArray,
},
{
name: "none",
input: []string{},
expected: emptyWASIStringArray,
},
{
name: "two",
input: []string{"a", "bc"},
expected: &nullTerminatedStrings{
nullTerminatedValues: [][]byte{
{'a', 0},
{'b', 'c', 0},
},
totalBufSize: 5,
},
},
{
name: "two and empty string",
input: []string{"a", "", "bc"},
expected: &nullTerminatedStrings{
nullTerminatedValues: [][]byte{
{'a', 0},
{0},
{'b', 'c', 0},
},
totalBufSize: 6,
},
},
{
name: "utf-8",
// "😨", "🤣", and "️🏃‍♀️" have 4, 4, and 13 bytes respectively
input: []string{"😨🤣🏃\u200d♀️", "foo", "bar"},
expected: &nullTerminatedStrings{
nullTerminatedValues: [][]byte{
[]byte("😨🤣🏃\u200d♀️\x00"),
{'f', 'o', 'o', 0},
{'b', 'a', 'r', 0},
},
totalBufSize: 30,
},
},
}

for _, tt := range tests {
tc := tt

t.Run(tc.name, func(t *testing.T) {
s, err := newNullTerminatedStrings(100, tc.input...)
require.NoError(t, err)
require.Equal(t, tc.expected, s)
})
}
}

func TestNewNullTerminatedStrings_Errors(t *testing.T) {
t.Run("invalid utf-8", func(t *testing.T) {
_, err := newNullTerminatedStrings(100, "\xff\xfe\xfd", "foo", "bar")
require.EqualError(t, err, "arg[0] is not a valid UTF-8 string")
})
t.Run("arg[0] too large", func(t *testing.T) {
_, err := newNullTerminatedStrings(1, "a", "bc")
require.EqualError(t, err, "arg[0] will exceed max buffer size 1")
})
t.Run("empty arg too large due to null terminator", func(t *testing.T) {
_, err := newNullTerminatedStrings(2, "a", "", "bc")
require.EqualError(t, err, "arg[1] will exceed max buffer size 2")
})
}
10 changes: 6 additions & 4 deletions wasi/testdata/args.wat
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
;; this file is put here for now, because this is a temporary file until the parser supports
;; the enough syntax, and this file will be embedded in unit test codes after that.
(module
(import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi_args_sizes_get (param i32 i32) (result i32)))
(import "wasi_snapshot_preview1" "args_get" (func $wasi_args_get (param i32 i32) (result i32)))
(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)))
(import "wasi_snapshot_preview1" "args_get"
(func $wasi.args_get (param $argv i32) (param $argv_buf i32) (result (;errno;) i32)))
(memory 1) ;; just an arbitrary size big enough for tests
(export "memory" (memory 0))
;; Define wrapper functions instead of just exporting the imported WASI APIS for now
Expand All @@ -18,12 +20,12 @@
(func $args_sizes_get (param i32 i32) (result i32)
local.get 0
local.get 1
call $wasi_args_sizes_get
call $wasi.args_sizes_get
)
(func $args_get (param i32 i32) (result i32)
local.get 0
local.get 1
call $wasi_args_get
call $wasi.args_get
)
(export "args_sizes_get" (func $args_sizes_get))
(export "args_get" (func $args_get))
Expand Down
5 changes: 3 additions & 2 deletions wasi/testdata/clock.wat
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
;; this file is put here for now, because this is a temporary file until the parser supports
;; the enough syntax, and this file will be embedded in unit test codes after that.
(module
(import "wasi_snapshot_preview1" "clock_time_get" (func $wasi_clock_time_get (param i32 i64 i32) (result i32)))
(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)))
(memory 1) ;; just an arbitrary size big enough for tests
(export "memory" (memory 0))
;; Define wrapper functions instead of just exporting the imported WASI APIS for now
Expand All @@ -18,7 +19,7 @@
local.get 0
local.get 1
local.get 2
call $wasi_clock_time_get
call $wasi.clock_time_get
)
(export "clock_time_get" (func $clock_time_get))
)
Loading

0 comments on commit e6d841c

Please sign in to comment.