summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Clegg <sbc@chromium.org>2020-05-21 12:58:02 -0400
committerGitHub <noreply@github.com>2020-05-21 09:58:02 -0700
commit7639690bbc2a2a35f315e2ee061cfa8cfeb1c5b1 (patch)
treecc17293bc25af39a1bb9a6a2fcae9e8d5204facd
parent3625539c176839c9a45d58143767a63c04b95559 (diff)
downloadwabt-7639690bbc2a2a35f315e2ee061cfa8cfeb1c5b1.tar.gz
wabt-7639690bbc2a2a35f315e2ee061cfa8cfeb1c5b1.tar.bz2
wabt-7639690bbc2a2a35f315e2ee061cfa8cfeb1c5b1.zip
wasi: Implement more of the wasi API (#1430)
I've been used the tests from wasi-sdk to get started: https://github.com/WebAssembly/wasi-sdk/tree/master/tests All these tests now pass. Still this isn't saying much, there are still some big missing pieces. Started using the new serdes (serialize/deserialze) API in uvwasi. I think we are almost at the point were we can look at auto-generating some of this stuff from witx to avoid having the hand code all this marshelling stuff. Add support for ArgumentCount::ZeroOrMore to OptionParser and also the ability to force the end of option processing using `--`. This allows is to pass options through the underlying wasi program when running wasm-interp.
-rw-r--r--src/interp/interp-wasi.cc417
-rw-r--r--src/interp/wasi_api.def20
-rw-r--r--src/option-parser.cc170
-rw-r--r--src/option-parser.h2
-rw-r--r--src/test-option-parser.cc29
-rw-r--r--src/tools/wasm-interp.cc31
-rw-r--r--test/help/wasm-interp.txt3
-rw-r--r--test/wasi/clock.txt21
m---------third_party/uvwasi0
9 files changed, 590 insertions, 103 deletions
diff --git a/src/interp/interp-wasi.cc b/src/interp/interp-wasi.cc
index 28d94584..3e841769 100644
--- a/src/interp/interp-wasi.cc
+++ b/src/interp/interp-wasi.cc
@@ -43,8 +43,12 @@ using namespace wabt::interp;
namespace {
-// Types that align with WASI specis on size and alignment.
+// Types that align with WASI spec on size and alignment. These are
+// copied directly from wasi-lib's auto-generated api.h
// TODO(sbc): Auto-generate this from witx.
+
+// BEGIN: wasi.h types from wasi-libc
+
typedef uint32_t __wasi_size_t;
typedef uint32_t __wasi_ptr_t;
@@ -53,6 +57,49 @@ _Static_assert(_Alignof(__wasi_size_t) == 4, "witx calculated align");
_Static_assert(sizeof(__wasi_ptr_t) == 4, "witx calculated size");
_Static_assert(_Alignof(__wasi_ptr_t) == 4, "witx calculated align");
+typedef struct __wasi_prestat_dir_t {
+ __wasi_size_t pr_name_len;
+} __wasi_prestat_dir_t;
+
+typedef uint8_t __wasi_preopentype_t;
+typedef uint64_t __wasi_rights_t;
+typedef uint16_t __wasi_fdflags_t;
+typedef uint8_t __wasi_filetype_t;
+typedef uint16_t __wasi_oflags_t;
+typedef uint32_t __wasi_lookupflags_t;
+typedef uint32_t __wasi_fd_t;
+typedef uint64_t __wasi_timestamp_t;
+typedef uint8_t __wasi_whence_t;
+typedef int64_t __wasi_filedelta_t;
+typedef uint64_t __wasi_filesize_t;
+
+typedef union __wasi_prestat_u_t {
+ __wasi_prestat_dir_t dir;
+} __wasi_prestat_u_t;
+
+struct __wasi_prestat_t {
+ __wasi_preopentype_t tag;
+ __wasi_prestat_u_t u;
+};
+
+typedef struct __wasi_fdstat_t {
+ __wasi_filetype_t fs_filetype;
+ __wasi_fdflags_t fs_flags;
+ __wasi_rights_t fs_rights_base;
+ __wasi_rights_t fs_rights_inheriting;
+} __wasi_fdstat_t;
+
+_Static_assert(sizeof(__wasi_fdstat_t) == 24, "witx calculated size");
+_Static_assert(_Alignof(__wasi_fdstat_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_fdstat_t, fs_filetype) == 0,
+ "witx calculated offset");
+_Static_assert(offsetof(__wasi_fdstat_t, fs_flags) == 2,
+ "witx calculated offset");
+_Static_assert(offsetof(__wasi_fdstat_t, fs_rights_base) == 8,
+ "witx calculated offset");
+_Static_assert(offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16,
+ "witx calculated offset");
+
struct __wasi_iovec_t {
__wasi_ptr_t buf;
__wasi_size_t buf_len;
@@ -63,8 +110,54 @@ _Static_assert(_Alignof(__wasi_iovec_t) == 4, "witx calculated align");
_Static_assert(offsetof(__wasi_iovec_t, buf) == 0, "witx calculated offset");
_Static_assert(offsetof(__wasi_iovec_t, buf_len) == 4, "witx calculated offset");
+typedef uint64_t __wasi_device_t;
+
+_Static_assert(sizeof(__wasi_device_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_device_t) == 8, "witx calculated align");
+
+typedef uint64_t __wasi_inode_t;
+
+_Static_assert(sizeof(__wasi_inode_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_inode_t) == 8, "witx calculated align");
+
+typedef uint64_t __wasi_linkcount_t;
+
+_Static_assert(sizeof(__wasi_linkcount_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_linkcount_t) == 8, "witx calculated align");
+
+typedef struct __wasi_filestat_t {
+ __wasi_device_t dev;
+ __wasi_inode_t ino;
+ __wasi_filetype_t filetype;
+ __wasi_linkcount_t nlink;
+ __wasi_filesize_t size;
+ __wasi_timestamp_t atim;
+ __wasi_timestamp_t mtim;
+ __wasi_timestamp_t ctim;
+} __wasi_filestat_t;
+
+_Static_assert(sizeof(__wasi_filestat_t) == 64, "witx calculated size");
+_Static_assert(_Alignof(__wasi_filestat_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_filestat_t, dev) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, ino) == 8, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, filetype) == 16,
+ "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, nlink) == 24,
+ "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, size) == 32,
+ "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, atim) == 40,
+ "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, mtim) == 48,
+ "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, ctim) == 56,
+ "witx calculated offset");
+
+#define __WASI_ERRNO_SUCCESS (UINT16_C(0))
#define __WASI_ERRNO_NOENT (UINT16_C(44))
+// END wasi.h types from wasi-lib
+
class WasiInstance {
public:
WasiInstance(Instance::Ptr instance,
@@ -76,18 +169,259 @@ class WasiInstance {
uvwasi(uvwasi),
memory(memory) {}
+ Result random_get(const Values& params, Values& results, Trap::Ptr* trap) {
+ /* __wasi_errno_t __wasi_random_get(uint8_t * buf, __wasi_size_t buf_len) */
+ assert(false);
+ return Result::Ok;
+ }
+
Result proc_exit(const Values& params, Values& results, Trap::Ptr* trap) {
const Value arg0 = params[0];
uvwasi_proc_exit(uvwasi, arg0.i32_);
return Result::Ok;
}
+ Result poll_oneoff(const Values& params, Values& results, Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
+ Result clock_time_get(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ /* __wasi_errno_t __wasi_clock_time_get(__wasi_clockid_t id,
+ * __wasi_timestamp_t precision,
+ * __wasi_timestamp_t *time)
+ */
+ __wasi_timestamp_t t;
+ results[0].i32_ =
+ uvwasi_clock_time_get(uvwasi, params[0].i32_, params[1].i64_, &t);
+ uint32_t time_ptr = params[2].i32_;
+ CHECK_RESULT(writeValue<__wasi_timestamp_t>(t, time_ptr, trap));
+ return Result::Ok;
+ }
+
+ Result path_rename(const Values& params, Values& results, Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
+ Result path_open(const Values& params, Values& results, Trap::Ptr* trap) {
+ /* __wasi_errno_t __wasi_path_open(__wasi_fd_t fd,
+ __wasi_lookupflags_t dirflags,
+ const char *path,
+ size_t path_len,
+ __wasi_oflags_t oflags,
+ __wasi_rights_t fs_rights_base,
+ __wasi_rights_t fs_rights_inherting,
+ __wasi_fdflags_t fdflags,
+ __wasi_fd_t *opened_fd) */
+ uvwasi_fd_t dirfd = params[0].i32_;
+ __wasi_lookupflags_t dirflags = params[1].i32_;
+ uint32_t path_ptr = params[2].i32_;
+ __wasi_size_t path_len = params[3].i32_;
+ __wasi_oflags_t oflags = params[4].i32_;
+ __wasi_rights_t fs_rights_base = params[5].i32_;
+ __wasi_rights_t fs_rights_inherting = params[6].i32_;
+ __wasi_fdflags_t fs_flags = params[7].i32_;
+ uint32_t out_ptr = params[8].i32_;
+ char* path;
+ CHECK_RESULT(getMemPtr<char>(path_ptr, path_len, &path, trap));
+ if (trace_stream) {
+ trace_stream->Writef("path_open : %s\n", path);
+ }
+ uvwasi_fd_t outfd;
+ results[0].i32_ =
+ uvwasi_path_open(uvwasi, dirfd, dirflags, path, path_len, oflags,
+ fs_rights_base, fs_rights_inherting, fs_flags, &outfd);
+ if (trace_stream) {
+ trace_stream->Writef("path_open -> %d\n", results[0].i32_);
+ }
+ CHECK_RESULT(writeValue<__wasi_fd_t>(outfd, out_ptr, trap));
+ return Result::Ok;
+ }
+
+ Result path_filestat_get(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ /* __wasi_errno_t __wasi_path_filestat_get(__wasi_fd_t fd,
+ * __wasi_lookupflags_t flags,
+ * const char *path,
+ * size_t path_len,
+ * __wasi_filestat_t *buf
+ */
+ uvwasi_fd_t fd = params[0].i32_;
+ __wasi_lookupflags_t flags = params[1].i32_;
+ uint32_t path_ptr = params[2].i32_;
+ uvwasi_size_t path_len = params[3].i32_;
+ uint32_t filestat_ptr = params[4].i32_;
+ char* path;
+ CHECK_RESULT(getMemPtr<char>(path_ptr, path_len, &path, trap));
+ if (trace_stream) {
+ trace_stream->Writef("path_filestat_get : %d %s\n", fd, path);
+ }
+ uvwasi_filestat_t buf;
+ results[0].i32_ =
+ uvwasi_path_filestat_get(uvwasi, fd, flags, path, path_len, &buf);
+ __wasi_filestat_t* filestat;
+ CHECK_RESULT(getMemPtr<__wasi_filestat_t>(
+ filestat_ptr, sizeof(__wasi_filestat_t), &filestat, trap));
+ uvwasi_serdes_write_filestat_t(filestat, 0, &buf);
+ if (trace_stream) {
+ trace_stream->Writef("path_filestat_get -> size=%lu %d\n", buf.st_size,
+ results[0].i32_);
+ }
+ return Result::Ok;
+ }
+
+ Result path_symlink(const Values& params, Values& results, Trap::Ptr* trap) {
+ /* __wasi_errno_t __wasi_path_symlink(const char *old_path,
+ * size_t old_path_len,
+ * __wasi_fd_t fd,
+ * const char *new_path,
+ * size_t new_path_len);
+ */
+
+ uint32_t old_path_ptr = params[0].i32_;
+ __wasi_size_t old_path_len = params[1].i32_;
+ uvwasi_fd_t fd = params[2].i32_;
+ uint32_t new_path_ptr = params[3].i32_;
+ __wasi_size_t new_path_len = params[4].i32_;
+ char* old_path;
+ char* new_path;
+ CHECK_RESULT(getMemPtr<char>(old_path_ptr, old_path_len, &old_path, trap));
+ CHECK_RESULT(getMemPtr<char>(new_path_ptr, new_path_len, &new_path, trap));
+ if (trace_stream) {
+ trace_stream->Writef("path_symlink %d %s : %s\n", fd, old_path, new_path);
+ }
+ results[0].i32_ = uvwasi_path_symlink(uvwasi, old_path, old_path_len, fd,
+ new_path, new_path_len);
+ if (trace_stream) {
+ trace_stream->Writef("path_symlink -> %d\n", results[0].i32_);
+ }
+ return Result::Ok;
+ }
+
+ Result path_readlink(const Values& params, Values& results, Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
+ Result path_create_directory(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
+ Result path_remove_directory(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
+ Result path_unlink_file(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ /* __wasi_errno_t __wasi_path_unlink_file(__wasi_fd_t fd,
+ * const char *path,
+ * size_t path_len)
+ */
+ uvwasi_fd_t fd = params[0].i32_;
+ uint32_t path_ptr = params[1].i32_;
+ __wasi_size_t path_len = params[2].i32_;
+ char* path;
+ CHECK_RESULT(getMemPtr<char>(path_ptr, path_len, &path, trap));
+ if (trace_stream) {
+ trace_stream->Writef("path_unlink_file %d %s\n", fd, path);
+ }
+ results[0].i32_ = uvwasi_path_unlink_file(uvwasi, fd, path, path_len);
+ return Result::Ok;
+ }
+
+ Result fd_prestat_get(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ /* __wasi_errno_t __wasi_fd_prestat_get(__wasi_fd_t fd,
+ * __wasi_prestat_t *buf))
+ */
+ uvwasi_fd_t fd = params[0].i32_;
+ uint32_t prestat_ptr = params[1].i32_;
+ if (trace_stream) {
+ trace_stream->Writef("fd_prestat_get %d\n", fd);
+ }
+ uvwasi_prestat_t buf;
+ results[0].i32_ = uvwasi_fd_prestat_get(uvwasi, fd, &buf);
+ __wasi_prestat_t* prestat;
+ CHECK_RESULT(getMemPtr<__wasi_prestat_t>(prestat_ptr, 1, &prestat, trap));
+ uvwasi_serdes_write_prestat_t(prestat, 0, &buf);
+ if (trace_stream) {
+ trace_stream->Writef("fd_prestat_get -> %d\n", results[0].i32_);
+ }
+ return Result::Ok;
+ }
+
+ Result fd_prestat_dir_name(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ uvwasi_fd_t fd = params[0].i32_;
+ uint32_t path_ptr = params[1].i32_;
+ uvwasi_size_t path_len = params[2].i32_;
+ if (trace_stream) {
+ trace_stream->Writef("fd_prestat_dir_name %d %d %d\n", fd, path_ptr,
+ path_len);
+ }
+ char* path;
+ CHECK_RESULT(getMemPtr<char>(path_ptr, path_len, &path, trap));
+ results[0].i32_ = uvwasi_fd_prestat_dir_name(uvwasi, fd, path, path_len);
+ if (trace_stream) {
+ trace_stream->Writef("fd_prestat_dir_name %d -> %d %s %d\n", fd,
+ results[0].i32_, path, path_len);
+ }
+ return Result::Ok;
+ }
+
+ Result fd_filestat_get(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ /* __wasi_fd_filestat_get(__wasi_fd_t f, __wasi_filestat_t *buf) */
+ uvwasi_fd_t fd = params[0].i32_;
+ uint32_t filestat_ptr = params[1].i32_;
+ uvwasi_filestat_t buf;
+ results[0].i32_ = uvwasi_fd_filestat_get(uvwasi, fd, &buf);
+ __wasi_filestat_t* filestat;
+ CHECK_RESULT(getMemPtr<__wasi_filestat_t>(
+ filestat_ptr, sizeof(__wasi_filestat_t), &filestat, trap));
+ uvwasi_serdes_write_filestat_t(filestat, 0, &buf);
+ if (trace_stream) {
+ trace_stream->Writef("fd_filestat_get -> size=%lu %d\n", buf.st_size,
+ results[0].i32_);
+ }
+ return Result::Ok;
+ }
+
+ Result fd_fdstat_set_flags(const Values& params,
+ Values& results,
+ Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
Result fd_fdstat_get(const Values& params, Values& results, Trap::Ptr* trap) {
int32_t fd = params[0].i32_;
+ uint32_t stat_ptr = params[1].i32_;
if (trace_stream) {
trace_stream->Writef("fd_fdstat_get %d\n", fd);
}
- results[0].i32_ = __WASI_ERRNO_NOENT;
+ CHECK_RESULT(getMemPtr<__wasi_fdstat_t>(stat_ptr, 1, nullptr, trap));
+ uvwasi_fdstat_t host_statbuf;
+ results[0].i32_ = uvwasi_fd_fdstat_get(uvwasi, fd, &host_statbuf);
+
+ // Write the host statbuf into the target wasm memory
+ __wasi_fdstat_t* statbuf;
+ CHECK_RESULT(getMemPtr<__wasi_fdstat_t>(stat_ptr, 1, &statbuf, trap));
+ uvwasi_serdes_write_fdstat_t(statbuf, 0, &host_statbuf);
return Result::Ok;
}
@@ -95,6 +429,7 @@ class WasiInstance {
int32_t fd = params[0].i32_;
int32_t iovptr = params[1].i32_;
int32_t iovcnt = params[2].i32_;
+ int32_t out_ptr = params[2].i32_;
if (trace_stream) {
trace_stream->Writef("fd_read %d [%d]\n", fd, iovcnt);
}
@@ -109,7 +444,7 @@ class WasiInstance {
trap));
}
__wasi_ptr_t* out_addr;
- CHECK_RESULT(getMemPtr<__wasi_ptr_t>(params[3].i32_, 1, &out_addr, trap));
+ CHECK_RESULT(getMemPtr<__wasi_ptr_t>(out_ptr, 1, &out_addr, trap));
results[0].i32_ =
uvwasi_fd_read(uvwasi, fd, iovs.data(), iovs.size(), out_addr);
if (trace_stream) {
@@ -118,6 +453,16 @@ class WasiInstance {
return Result::Ok;
}
+ Result fd_pread(const Values& params, Values& results, Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
+ Result fd_readdir(const Values& params, Values& results, Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
Result fd_write(const Values& params, Values& results, Trap::Ptr* trap) {
int32_t fd = params[0].i32_;
int32_t iovptr = params[1].i32_;
@@ -138,6 +483,32 @@ class WasiInstance {
return Result::Ok;
}
+ Result fd_pwrite(const Values& params, Values& results, Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
+ Result fd_close(const Values& params, Values& results, Trap::Ptr* trap) {
+ assert(false);
+ return Result::Ok;
+ }
+
+ Result fd_seek(const Values& params, Values& results, Trap::Ptr* trap) {
+ /* __wasi_errno_t __wasi_fd_seek(__wasi_fd_t fd,
+ * __wasi_filedelta_t offset,
+ * __wasi_whence_t whence,
+ * __wasi_filesize_t *newoffset)
+ */
+ int32_t fd = params[0].i32_;
+ __wasi_filedelta_t offset = params[1].i32_;
+ __wasi_whence_t whence = params[2].i32_;
+ uint32_t newoffset_ptr = params[3].i32_;
+ uvwasi_filesize_t newoffset;
+ results[0].i32_ = uvwasi_fd_seek(uvwasi, fd, offset, whence, &newoffset);
+ CHECK_RESULT(writeValue<__wasi_filesize_t>(newoffset, newoffset_ptr, trap));
+ return Result::Ok;
+ }
+
Result environ_get(const Values& params, Values& results, Trap::Ptr* trap) {
uvwasi_size_t environc;
uvwasi_size_t environ_buf_size;
@@ -155,6 +526,7 @@ class WasiInstance {
CHECK_RESULT(writeValue<uint32_t>(wasm_buf + rel_address, dest, trap));
}
+ results[0].i32_ = __WASI_ERRNO_SUCCESS;
return Result::Ok;
}
@@ -170,6 +542,7 @@ class WasiInstance {
trace_stream->Writef("environ_sizes_get -> %d %d\n", environc,
environ_buf_size);
}
+ results[0].i32_ = __WASI_ERRNO_SUCCESS;
return Result::Ok;
}
@@ -180,8 +553,8 @@ class WasiInstance {
uint32_t wasm_buf = params[1].i32_;
char* buf;
CHECK_RESULT(getMemPtr<char>(wasm_buf, arg_buf_size, &buf, trap));
- char** host_args = new char*[argc];
- uvwasi_args_get(uvwasi, host_args, buf);
+ std::vector<char*> host_args(argc);
+ uvwasi_args_get(uvwasi, host_args.data(), buf);
// Copy host_args pointer array wasm_args)
for (uvwasi_size_t i = 0; i < argc; i++) {
@@ -189,7 +562,7 @@ class WasiInstance {
uint32_t dest = params[0].i32_ + (i * sizeof(uint32_t));
CHECK_RESULT(writeValue<uint32_t>(wasm_buf + rel_address, dest, trap));
}
-
+ results[0].i32_ = __WASI_ERRNO_SUCCESS;
return Result::Ok;
}
@@ -204,6 +577,7 @@ class WasiInstance {
if (trace_stream) {
trace_stream->Writef("args_sizes_get -> %d %d\n", argc, arg_buf_size);
}
+ results[0].i32_ = __WASI_ERRNO_SUCCESS;
return Result::Ok;
}
@@ -225,18 +599,21 @@ class WasiInstance {
// Result a wasm-memory-local address to an absolute memory location.
template <typename T>
Result getMemPtr(uint32_t address,
- uint32_t size,
+ uint32_t num_elems,
T** result,
Trap::Ptr* trap) {
- if (!memory->IsValidAccess(address, 0, size * sizeof(T))) {
- *trap = Trap::New(*instance.store(),
- StringPrintf("out of bounds memory access: "
- "[%u, %" PRIu64 ") >= max value %u",
- address, u64{address} + size * sizeof(T),
- memory->ByteSize()));
+ if (!memory->IsValidAccess(address, 0, num_elems * sizeof(T))) {
+ *trap =
+ Trap::New(*instance.store(),
+ StringPrintf("out of bounds memory access: "
+ "[%u, %" PRIu64 ") >= max value %u",
+ address, u64{address} + num_elems * sizeof(T),
+ memory->ByteSize()));
return Result::Error;
}
- *result = reinterpret_cast<T*>(memory->UnsafeData() + address);
+ if (result) {
+ *result = reinterpret_cast<T*>(memory->UnsafeData() + address);
+ }
return Result::Ok;
}
@@ -284,7 +661,8 @@ Result WasiBindImports(const Module::Ptr& module,
return Result::Error;
}
- if (import.type.module != "wasi_snapshot_preview1") {
+ if (import.type.module != "wasi_snapshot_preview1" &&
+ import.type.module != "wasi_unstable") {
stream->Writef("wasi error: unknown module import: `%s`\n",
import.type.module.c_str());
return Result::Error;
@@ -295,6 +673,7 @@ Result WasiBindImports(const Module::Ptr& module,
import.type.name.c_str());
HostFunc::Ptr host_func;
+ // TODO(sbc): Validate signatures of imports.
#define WASI_FUNC(NAME) \
if (import.type.name == #NAME) { \
host_func = HostFunc::New(*store, func_type, NAME); \
@@ -303,8 +682,7 @@ Result WasiBindImports(const Module::Ptr& module,
#include "wasi_api.def"
#undef WASI_FUNC
- stream->Writef("unknown `wasi_snapshot_preview1` import: `%s`\n",
- import.type.name.c_str());
+ stream->Writef("unknown wasi API import: `%s`\n", import.type.name.c_str());
return Result::Error;
found:
imports.push_back(host_func.ref());
@@ -359,8 +737,8 @@ Result WasiRunStart(const Instance::Ptr& instance,
}
// Register memory
- auto* wasi = new WasiInstance(instance, uvwasi, memory.get(), trace_stream);
- wasiInstances[instance.get()] = wasi;
+ WasiInstance wasi(instance, uvwasi, memory.get(), trace_stream);
+ wasiInstances[instance.get()] = &wasi;
// Call start ([] -> [])
Values params;
@@ -373,7 +751,6 @@ Result WasiRunStart(const Instance::Ptr& instance,
// Unregister memory
wasiInstances.erase(instance.get());
- delete wasi;
return res;
}
diff --git a/src/interp/wasi_api.def b/src/interp/wasi_api.def
index 6599fc06..008e9fd6 100644
--- a/src/interp/wasi_api.def
+++ b/src/interp/wasi_api.def
@@ -1,8 +1,28 @@
WASI_FUNC(proc_exit)
WASI_FUNC(fd_read)
+WASI_FUNC(fd_pread)
WASI_FUNC(fd_write)
+WASI_FUNC(fd_pwrite)
+WASI_FUNC(fd_close)
+WASI_FUNC(fd_seek)
+WASI_FUNC(fd_prestat_get)
+WASI_FUNC(fd_prestat_dir_name)
WASI_FUNC(fd_fdstat_get)
+WASI_FUNC(fd_fdstat_set_flags)
+WASI_FUNC(fd_filestat_get)
+WASI_FUNC(fd_readdir)
WASI_FUNC(environ_sizes_get)
WASI_FUNC(environ_get)
WASI_FUNC(args_sizes_get)
WASI_FUNC(args_get)
+WASI_FUNC(path_open)
+WASI_FUNC(path_filestat_get)
+WASI_FUNC(path_symlink)
+WASI_FUNC(path_unlink_file)
+WASI_FUNC(path_remove_directory)
+WASI_FUNC(path_create_directory)
+WASI_FUNC(path_readlink)
+WASI_FUNC(path_rename)
+WASI_FUNC(clock_time_get)
+WASI_FUNC(poll_oneoff)
+WASI_FUNC(random_get)
diff --git a/src/option-parser.cc b/src/option-parser.cc
index 99c3c74a..d6a38ae8 100644
--- a/src/option-parser.cc
+++ b/src/option-parser.cc
@@ -162,99 +162,107 @@ void OptionParser::HandleArgument(size_t* arg_index, const char* arg_value) {
void OptionParser::Parse(int argc, char* argv[]) {
size_t arg_index = 0;
+ bool processing_options = true;
for (int i = 1; i < argc; ++i) {
const char* arg = argv[i];
- if (arg[0] == '-') {
- if (arg[1] == '-') {
- // Long option.
- int best_index = -1;
- int best_length = 0;
- int best_count = 0;
- for (size_t j = 0; j < options_.size(); ++j) {
- const Option& option = options_[j];
- if (!option.long_name.empty()) {
- int match_length =
- Match(&arg[2], option.long_name, option.has_argument);
- if (match_length > best_length) {
- best_index = j;
- best_length = match_length;
- best_count = 1;
- } else if (match_length == best_length && best_length > 0) {
- best_count++;
- }
+ if (!processing_options || arg[0] != '-') {
+ // Non-option argument.
+ HandleArgument(&arg_index, arg);
+ continue;
+ }
+
+ if (arg[1] == '-') {
+ if (arg[2] == '\0') {
+ // -- on its own means stop processing args, everything should
+ // be treated as positional.
+ processing_options = false;
+ continue;
+ }
+ // Long option.
+ int best_index = -1;
+ int best_length = 0;
+ int best_count = 0;
+ for (size_t j = 0; j < options_.size(); ++j) {
+ const Option& option = options_[j];
+ if (!option.long_name.empty()) {
+ int match_length =
+ Match(&arg[2], option.long_name, option.has_argument);
+ if (match_length > best_length) {
+ best_index = j;
+ best_length = match_length;
+ best_count = 1;
+ } else if (match_length == best_length && best_length > 0) {
+ best_count++;
}
}
+ }
- if (best_count > 1) {
- Errorf("ambiguous option '%s'", arg);
- continue;
- } else if (best_count == 0) {
- Errorf("unknown option '%s'", arg);
- continue;
- }
+ if (best_count > 1) {
+ Errorf("ambiguous option '%s'", arg);
+ continue;
+ } else if (best_count == 0) {
+ Errorf("unknown option '%s'", arg);
+ continue;
+ }
- const Option& best_option = options_[best_index];
- const char* option_argument = nullptr;
- if (best_option.has_argument) {
- if (arg[best_length + 1] != 0 && // This byte is 0 on a full match.
- arg[best_length + 2] == '=') { // +2 to skip "--".
- option_argument = &arg[best_length + 3];
- } else {
- if (i + 1 == argc || argv[i + 1][0] == '-') {
- Errorf("option '--%s' requires argument",
- best_option.long_name.c_str());
- continue;
- }
- ++i;
- option_argument = argv[i];
+ const Option& best_option = options_[best_index];
+ const char* option_argument = nullptr;
+ if (best_option.has_argument) {
+ if (arg[best_length + 1] != 0 && // This byte is 0 on a full match.
+ arg[best_length + 2] == '=') { // +2 to skip "--".
+ option_argument = &arg[best_length + 3];
+ } else {
+ if (i + 1 == argc || argv[i + 1][0] == '-') {
+ Errorf("option '--%s' requires argument",
+ best_option.long_name.c_str());
+ continue;
}
+ ++i;
+ option_argument = argv[i];
}
- best_option.callback(option_argument);
- } else {
- // Short option.
- if (arg[1] == '\0') {
- // Just "-".
- HandleArgument(&arg_index, arg);
- continue;
- }
+ }
+ best_option.callback(option_argument);
+ } else {
+ // Short option.
+ if (arg[1] == '\0') {
+ // Just "-".
+ HandleArgument(&arg_index, arg);
+ continue;
+ }
- // Allow short names to be combined, e.g. "-d -v" => "-dv".
- for (int k = 1; arg[k]; ++k) {
- bool matched = false;
- for (const Option& option : options_) {
- if (option.short_name && arg[k] == option.short_name) {
- const char* option_argument = nullptr;
- if (option.has_argument) {
- // A short option with a required argument cannot be followed
- // by other short options_.
- if (arg[k + 1] != '\0') {
- Errorf("option '-%c' requires argument", option.short_name);
- break;
- }
-
- if (i + 1 == argc || argv[i + 1][0] == '-') {
- Errorf("option '-%c' requires argument", option.short_name);
- break;
- }
- ++i;
- option_argument = argv[i];
+ // Allow short names to be combined, e.g. "-d -v" => "-dv".
+ for (int k = 1; arg[k]; ++k) {
+ bool matched = false;
+ for (const Option& option : options_) {
+ if (option.short_name && arg[k] == option.short_name) {
+ const char* option_argument = nullptr;
+ if (option.has_argument) {
+ // A short option with a required argument cannot be followed
+ // by other short options_.
+ if (arg[k + 1] != '\0') {
+ Errorf("option '-%c' requires argument", option.short_name);
+ break;
}
- option.callback(option_argument);
- matched = true;
- break;
+
+ if (i + 1 == argc || argv[i + 1][0] == '-') {
+ Errorf("option '-%c' requires argument", option.short_name);
+ break;
+ }
+ ++i;
+ option_argument = argv[i];
}
+ option.callback(option_argument);
+ matched = true;
+ break;
}
+ }
- if (!matched) {
- Errorf("unknown option '-%c'", arg[k]);
- continue;
- }
+ if (!matched) {
+ Errorf("unknown option '-%c'", arg[k]);
+ continue;
}
}
- } else {
- // Non-option argument.
- HandleArgument(&arg_index, arg);
}
}
@@ -262,7 +270,9 @@ void OptionParser::Parse(int argc, char* argv[]) {
// handled at least once.
if (!arguments_.empty() && arguments_.back().handled_count == 0) {
for (size_t i = arg_index; i < arguments_.size(); ++i) {
- Errorf("expected %s argument.", arguments_[i].name.c_str());
+ if (arguments_[i].count != ArgumentCount::ZeroOrMore) {
+ Errorf("expected %s argument.", arguments_[i].name.c_str());
+ }
}
}
}
@@ -280,6 +290,10 @@ void OptionParser::PrintHelp() {
case ArgumentCount::OneOrMore:
printf(" %s+", argument.name.c_str());
break;
+
+ case ArgumentCount::ZeroOrMore:
+ printf(" [%s]...", argument.name.c_str());
+ break;
}
}
diff --git a/src/option-parser.h b/src/option-parser.h
index 96690781..051ce784 100644
--- a/src/option-parser.h
+++ b/src/option-parser.h
@@ -28,7 +28,7 @@ namespace wabt {
class OptionParser {
public:
enum class HasArgument { No, Yes };
- enum class ArgumentCount { One, OneOrMore };
+ enum class ArgumentCount { One, OneOrMore, ZeroOrMore };
struct Option;
typedef std::function<void(const char*)> Callback;
diff --git a/src/test-option-parser.cc b/src/test-option-parser.cc
index 3d190e71..9295c581 100644
--- a/src/test-option-parser.cc
+++ b/src/test-option-parser.cc
@@ -150,3 +150,32 @@ TEST(OptionParser, OneOrMoreArguments) {
parser.Parse(3, const_cast<char**>(args));
EXPECT_EQ("hellogoodbye", argument);
}
+
+TEST(OptionParser, ZeroOrMoreArguments) {
+ std::string argument;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [&](const char* arg) { argument += arg; });
+
+ const char* args_none[] = {"prog name"};
+ parser.Parse(1, const_cast<char**>(args_none));
+ EXPECT_EQ("", argument);
+
+ const char* args_many[] = {"prog name", "hello", "goodbye"};
+ parser.Parse(3, const_cast<char**>(args_many));
+ EXPECT_EQ("hellogoodbye", argument);
+}
+
+TEST(OptionParser, StopProccessing) {
+ std::string argument;
+ bool has_x = false;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [&](const char* arg) { argument += arg; });
+ parser.AddOption('x', "x", "help", [&]() { has_x = true; });
+
+ const char* args_many[] = {"prog name", "-x", "--", "foo", "-x", "-y", "bar"};
+ parser.Parse(7, const_cast<char**>(args_many));
+ EXPECT_TRUE(has_x);
+ EXPECT_EQ("foo-x-ybar", argument);
+}
diff --git a/src/tools/wasm-interp.cc b/src/tools/wasm-interp.cc
index a976d1b1..f7561d3f 100644
--- a/src/tools/wasm-interp.cc
+++ b/src/tools/wasm-interp.cc
@@ -49,6 +49,8 @@ static bool s_dummy_import_func;
static Features s_features;
static bool s_wasi;
static std::vector<std::string> s_wasi_env;
+static std::vector<std::string> s_wasi_argv;
+static std::vector<std::string> s_wasi_dirs;
static std::unique_ptr<FileStream> s_log_stream;
static std::unique_ptr<FileStream> s_stdout_stream;
@@ -106,6 +108,9 @@ static void ParseOptions(int argc, char** argv) {
"Pass the given environment string in the WASI runtime",
[](const std::string& argument) { s_wasi_env.push_back(argument); });
parser.AddOption(
+ 'd', "dir", "DIR", "Pass the given directory the the WASI runtime",
+ [](const std::string& argument) { s_wasi_dirs.push_back(argument); });
+ parser.AddOption(
"run-all-exports",
"Run all the exported functions, in order. Useful for testing",
[]() { s_run_all_exports = true; });
@@ -121,6 +126,9 @@ static void ParseOptions(int argc, char** argv) {
parser.AddArgument("filename", OptionParser::ArgumentCount::One,
[](const char* argument) { s_infile = argument; });
+ parser.AddArgument(
+ "arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [](const char* argument) { s_wasi_argv.push_back(argument); });
parser.Parse(argc, argv);
}
@@ -242,14 +250,31 @@ static Result ReadAndRunModule(const char* module_filename) {
std::vector<const char*> argv;
argv.push_back(module_filename);
+ for (auto& s : s_wasi_argv) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: arg: \"%s\"\n", s.c_str());
+ }
+ argv.push_back(s.c_str());
+ }
argv.push_back(nullptr);
std::vector<const char*> envp;
- for (auto s : s_wasi_env) {
+ for (auto& s : s_wasi_env) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: env: \"%s\"\n", s.c_str());
+ }
envp.push_back(s.c_str());
}
envp.push_back(nullptr);
+ std::vector<uvwasi_preopen_t> dirs;
+ for (auto& dir : s_wasi_dirs) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: dir: \"%s\"\n", dir.c_str());
+ }
+ dirs.push_back({dir.c_str(), dir.c_str()});
+ }
+
/* Setup the initialization options. */
init_options.in = 0;
init_options.out = 1;
@@ -258,8 +283,8 @@ static Result ReadAndRunModule(const char* module_filename) {
init_options.argc = argv.size() - 1;
init_options.argv = argv.data();
init_options.envp = envp.data();
- init_options.preopenc = 0;
- init_options.preopens = NULL;
+ init_options.preopenc = dirs.size();
+ init_options.preopens = dirs.data();
init_options.allocator = NULL;
err = uvwasi_init(&uvwasi, &init_options);
diff --git a/test/help/wasm-interp.txt b/test/help/wasm-interp.txt
index 9412e267..91c91eda 100644
--- a/test/help/wasm-interp.txt
+++ b/test/help/wasm-interp.txt
@@ -1,7 +1,7 @@
;;; RUN: %(wasm-interp)s
;;; ARGS: --help
(;; STDOUT ;;;
-usage: wasm-interp [options] filename
+usage: wasm-interp [options] filename [arg]...
read a file in the wasm binary format, and run in it a stack-based
interpreter.
@@ -42,6 +42,7 @@ options:
-t, --trace Trace execution
--wasi Assume input module is WASI compliant (Export WASI API the the module and invoke _start function)
-e, --env=ENV Pass the given environment string in the WASI runtime
+ -d, --dir=DIR Pass the given directory the the WASI runtime
--run-all-exports Run all the exported functions, in order. Useful for testing
--host-print Include an importable function named "host.print" for printing to stdout
--dummy-import-func Provide a dummy implementation of all imported functions. The function will log the call and return an appropriate zero value.
diff --git a/test/wasi/clock.txt b/test/wasi/clock.txt
new file mode 100644
index 00000000..ec04edbd
--- /dev/null
+++ b/test/wasi/clock.txt
@@ -0,0 +1,21 @@
+;;; TOOL: run-interp-wasi
+;;; ARGS: --trace
+;; __WASI_CLOCKID_REALTIME = 0
+
+(import "wasi_snapshot_preview1" "clock_time_get" (func $clock_time_get (param i32 i64 i32) (result i32)))
+(memory (export "memory") 1)
+(data (i32.const 20) "\00\00\00\00\00\00\00\00")
+
+(func (export "_start")
+ (call $clock_time_get (i32.const 0) (i64.const 0) (i32.const 20))
+ drop
+)
+(;; STDOUT ;;;
+#0. 0: V:0 | i32.const 0
+#0. 8: V:1 | i64.const 0
+#0. 20: V:2 | i32.const 20
+#0. 28: V:3 | call_import $0
+>>> running wasi function "clock_time_get":
+#0. 36: V:1 | drop
+#0. 40: V:0 | return
+;;; STDOUT ;;)
diff --git a/third_party/uvwasi b/third_party/uvwasi
-Subproject b0684ea4182f5e4e11a12ab552d7ba1487ade02
+Subproject 55eff19f4c7e69ec151424a037f951e0ad006ed