From 50f6317f6021907a4035fb1f9bac9abd652773fa Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Mon, 2 Dec 2024 08:12:26 +0000 Subject: [PATCH 1/2] Implement fd_fdstat_get. Improve other WASI functions as well. Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- src/wasi/WASI.cpp | 96 +++++++++++++++++++++++++----------- src/wasi/WASI.h | 2 + test/wasi/fd_fdstat_get.wast | 60 ++++++++++++++++++++++ test/wasi/print.wast | 69 ++++++++++++++++++++++++++ 4 files changed, 197 insertions(+), 30 deletions(-) create mode 100644 test/wasi/fd_fdstat_get.wast create mode 100644 test/wasi/print.wast diff --git a/src/wasi/WASI.cpp b/src/wasi/WASI.cpp index f6e928c50..65b5cd1dc 100644 --- a/src/wasi/WASI.cpp +++ b/src/wasi/WASI.cpp @@ -41,14 +41,23 @@ static void* get_memory_pointer(Instance* instance, Value& value, size_t size) return memory->buffer() + offset; } +template class TemporaryData { public: TemporaryData(size_t size) { + /* Check that more memory is requested than the maximum provided by malloc. */ + if (size > ((~static_cast(0)) / sizeof(T))) { + m_data = nullptr; + return; + } + + size *= sizeof(T); + if (size <= sizeof(m_stackData)) { m_data = m_stackData; } else { - m_data = malloc(size); + m_data = reinterpret_cast(malloc(size)); } } @@ -59,14 +68,14 @@ class TemporaryData { } } - void* data() + T* data() { return m_data; } private: - void* m_stackData[8]; - void* m_data; + T m_stackData[maxSize]; + T* m_data; }; void WASI::initialize(uvwasi_t* uvwasi) @@ -123,51 +132,78 @@ void WASI::clock_time_get(ExecutionState& state, Value* argv, Value* result, Ins void WASI::fd_write(ExecutionState& state, Value* argv, Value* result, Instance* instance) { uint32_t fd = argv[0].asI32(); - uint32_t iovptr = argv[1].asI32(); - uint32_t iovcnt = argv[2].asI32(); - uint32_t out = argv[3].asI32(); + size_t iovsLen = static_cast(argv[2].asI32()); + uint32_t* nwritten = reinterpret_cast(get_memory_pointer(instance, argv[3], sizeof(uint32_t))); + uint32_t* iovptr = reinterpret_cast(get_memory_pointer(instance, argv[1], iovsLen * (sizeof(uint32_t) << 1))); - if (uint64_t(iovptr) + iovcnt >= instance->memory(0)->sizeInByte()) { - result[0] = Value(static_cast(WasiErrNo::inval)); + if (iovptr == nullptr || nwritten == nullptr) { + result[0] = Value(WasiErrNo::inval); return; } - std::vector iovs(iovcnt); - for (uint32_t i = 0; i < iovcnt; i++) { - iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast(instance->memory(0)->buffer() + iovptr + i * 8); - iovs[i].buf_len = *reinterpret_cast(instance->memory(0)->buffer() + iovptr + 4 + i * 8); - } + TemporaryData iovsBuffer(iovsLen); + uvwasi_ciovec_t* iovs = iovsBuffer.data(); + uint64_t sizeInByte = instance->memory(0)->sizeInByte(); + uint8_t* buffer = instance->memory(0)->buffer(); - uvwasi_size_t* out_addr = (uvwasi_size_t*)(instance->memory(0)->buffer() + out); + for (uint32_t i = 0; i < iovsLen; i++) { + if (iovptr[1] > sizeInByte || iovptr[0] > sizeInByte - iovptr[1]) { + result[0] = Value(WasiErrNo::inval); + return; + } + + iovs[i].buf = buffer + iovptr[0]; + iovs[i].buf_len = iovptr[1]; + iovptr += 2; + } - result[0] = Value(static_cast(uvwasi_fd_write(WASI::g_uvwasi, fd, iovs.data(), iovs.size(), out_addr))); - *(instance->memory(0)->buffer() + out) = *out_addr; + result[0] = Value(uvwasi_fd_write(WASI::g_uvwasi, fd, iovs, iovsLen, nwritten)); } void WASI::fd_read(ExecutionState& state, Value* argv, Value* result, Instance* instance) { uint32_t fd = argv[0].asI32(); - uint32_t iovptr = argv[1].asI32(); - uint32_t iovcnt = argv[2].asI32(); - uint32_t out = argv[3].asI32(); - - std::vector iovs(iovcnt); - for (uint32_t i = 0; i < iovcnt; i++) { - iovs[i].buf = instance->memory(0)->buffer() + *reinterpret_cast(instance->memory(0)->buffer() + iovptr + i * 8); - iovs[i].buf_len = *reinterpret_cast(instance->memory(0)->buffer() + iovptr + 4 + i * 8); + size_t iovsLen = static_cast(argv[2].asI32()); + uint32_t* nread = reinterpret_cast(get_memory_pointer(instance, argv[3], sizeof(uint32_t))); + uint32_t* iovptr = reinterpret_cast(get_memory_pointer(instance, argv[1], iovsLen * (sizeof(uint32_t) << 1))); + + if (iovptr == nullptr || nread == nullptr) { + result[0] = Value(WasiErrNo::inval); + return; } - uvwasi_size_t* out_addr = (uvwasi_size_t*)(instance->memory(0)->buffer() + out); + TemporaryData iovsBuffer(iovsLen); + uvwasi_iovec_t* iovs = iovsBuffer.data(); + uint64_t sizeInByte = instance->memory(0)->sizeInByte(); + uint8_t* buffer = instance->memory(0)->buffer(); + + for (uint32_t i = 0; i < iovsLen; i++) { + if (iovptr[1] > sizeInByte || iovptr[0] > sizeInByte - iovptr[1]) { + result[0] = Value(WasiErrNo::inval); + return; + } + + iovs[i].buf = buffer + iovptr[0]; + iovs[i].buf_len = iovptr[1]; + iovptr += 2; + } - result[0] = Value(static_cast(uvwasi_fd_read(WASI::g_uvwasi, fd, iovs.data(), iovs.size(), out_addr))); - *(instance->memory(0)->buffer() + out) = *out_addr; + result[0] = Value(uvwasi_fd_read(WASI::g_uvwasi, fd, iovs, iovsLen, nread)); } void WASI::fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance) { uint32_t fd = argv[0].asI32(); - result[0] = Value(static_cast(uvwasi_fd_close(WASI::g_uvwasi, fd))); + result[0] = Value(uvwasi_fd_close(WASI::g_uvwasi, fd)); +} + +void WASI::fd_fdstat_get(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + uint32_t fd = argv[0].asI32(); + uvwasi_fdstat_t* fdstat = reinterpret_cast(get_memory_pointer(instance, argv[1], sizeof(uvwasi_fdstat_t))); + + result[0] = Value(uvwasi_fd_fdstat_get(WASI::g_uvwasi, fd, fdstat)); } void WASI::fd_seek(ExecutionState& state, Value* argv, Value* result, Instance* instance) @@ -218,7 +254,7 @@ void WASI::environ_get(ExecutionState& state, Value* argv, Value* result, Instan return; } - TemporaryData pointers(count * sizeof(void*)); + TemporaryData pointers(count); if (pointers.data() == nullptr) { result[0] = Value(WasiErrNo::inval); diff --git a/src/wasi/WASI.h b/src/wasi/WASI.h index 7c3dd8d7e..924b215f3 100644 --- a/src/wasi/WASI.h +++ b/src/wasi/WASI.h @@ -44,6 +44,7 @@ class WASI { F(fd_write, I32I32I32I32_RI32) \ F(fd_read, I32I32I32I32_RI32) \ F(fd_close, I32_RI32) \ + F(fd_fdstat_get, I32I32_RI32) \ F(fd_seek, I32I64I32I32_RI32) \ F(path_open, I32I32I32I32I32I64I64I32I32_RI32) \ F(environ_get, I32I32_RI32) \ @@ -159,6 +160,7 @@ class WASI { static void fd_write(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void fd_read(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance); + static void fd_fdstat_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void fd_seek(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void path_open(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void environ_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); diff --git a/test/wasi/fd_fdstat_get.wast b/test/wasi/fd_fdstat_get.wast new file mode 100644 index 000000000..1f1868849 --- /dev/null +++ b/test/wasi/fd_fdstat_get.wast @@ -0,0 +1,60 @@ +(module + (import "wasi_snapshot_preview1" "path_open" (func $path_open (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_close" (func $fd_close (param i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_fdstat_get" (func $fd_fdstat_get (param i32 i32) (result i32))) + + (memory 1 1) + (data (i32.const 100) "./fd_fdstat_get.wast") + + (func (export "fdstatGet") (result i32 i32 i32) + i32.const 3 ;; Directory file descriptior, by default 3 is the first opened directory + i32.const 1 ;; uvwasi_lookupflags_t: UVWASI_LOOKUP_SYMLINK_FOLLOW + i32.const 100 ;; Offset of file name in memory + i32.const 20 ;; Length of file name + i32.const 0 ;; uvwasi_oflags_t: none + i64.const 0x42000 ;; base uvwasi_rights_t: UVWASI_RIGHT_PATH_OPEN, UVWASI_RIGHT_FD_FILESTAT_GET + i64.const 0x42000 ;; inherited uvwasi_rights_t: UVWASI_RIGHT_PATH_OPEN, UVWASI_RIGHT_FD_FILESTAT_GET + i32.const 0 ;; uvwasi_fdflags_t: none + i32.const 0 ;; Offset to store at the opened file descriptor in memory + call $path_open + + i32.const 0 + i32.load + i32.const 200 + call $fd_fdstat_get + + i32.const 0 + i32.load + call $fd_close + ) + + (func (export "fdstatGetBad") (result i32 i32 i32 i32) + i32.const 3 ;; Directory file descriptior, by default 3 is the first opened directory + i32.const 1 ;; uvwasi_lookupflags_t: UVWASI_LOOKUP_SYMLINK_FOLLOW + i32.const 100 ;; Offset of file name in memory + i32.const 20 ;; Length of file name + i32.const 0 ;; uvwasi_oflags_t: none + i64.const 0x42000 ;; base uvwasi_rights_t: UVWASI_RIGHT_PATH_OPEN, UVWASI_RIGHT_FD_FILESTAT_GET + i64.const 0x42000 ;; inherited uvwasi_rights_t: UVWASI_RIGHT_PATH_OPEN, UVWASI_RIGHT_FD_FILESTAT_GET + i32.const 0 ;; uvwasi_fdflags_t: none + i32.const 0 ;; Offset to store at the opened file descriptor in memory + call $path_open + + i32.const 0 + i32.load + i32.const 65535 + call $fd_fdstat_get + + i32.const -1 + i32.const 200 + call $fd_fdstat_get + + i32.const 0 + i32.load + call $fd_close + ) +) + +(assert_return (invoke "fdstatGet") (i32.const 0) (i32.const 0) (i32.const 0)) +(assert_return (invoke "fdstatGetBad") (i32.const 0) (i32.const 28) (i32.const 8) (i32.const 0)) + diff --git a/test/wasi/print.wast b/test/wasi/print.wast new file mode 100644 index 000000000..3917ccabb --- /dev/null +++ b/test/wasi/print.wast @@ -0,0 +1,69 @@ +(module + (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) + + (memory $memory 1 1) + (data (i32.const 100) "Hello ") + (data (i32.const 200) "World!\n") + + (func (export "writeToStdout") (result i32 i32) + ;; Write iovs into memory from offset 16 + i32.const 16 + i32.const 100 + i32.store + + i32.const 20 + i32.const 6 + i32.store + + i32.const 24 + i32.const 200 + i32.store + + i32.const 28 + i32.const 7 + i32.store + + i32.const 1 ;; stdout + i32.const 16 ;; iovs offset + i32.const 2 ;; iovsLen + i32.const 8 ;; nwritten + + call $fd_write + + i32.const 8 + i32.load + ) + + (func (export "badWrite") (result i32 i32 i32 i32) + i32.const 100000 ;; invalid descriptor + i32.const 8 ;; iovs offset + i32.const 1 ;; iovsLen + i32.const 4 ;; nwritten + + call $fd_write + + i32.const 1 ;; stdout + i32.const 65529 ;; bad iovs offset + i32.const 1 ;; iovsLen + i32.const 8 ;; nwritten + + call $fd_write + + i32.const 1 ;; stdout + i32.const 16 ;; iovs offset + i32.const 1 ;; iovsLen + i32.const 65533 ;; bad nwritten + + call $fd_write + + i32.const 1 ;; stdout + i32.const 8 ;; iovs offset + i32.const 8192 ;; bad iovsLen + i32.const 0 ;; nwritten + + call $fd_write + ) +) + +(assert_return (invoke "writeToStdout") (i32.const 0) (i32.const 13)) +(assert_return (invoke "badWrite") (i32.const 8) (i32.const 28) (i32.const 28) (i32.const 28)) From e9a625892aa6c02abfd0894032a4735fd9263842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Tokodi?= Date: Mon, 2 Dec 2024 14:48:16 +0100 Subject: [PATCH 2/2] WASI: add args_get and args_sizes_get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the test script to test passing the arguments as well Signed-off-by: Máté Tokodi mate.tokodi@szteszoftver.hu --- src/shell/Shell.cpp | 19 +++++++++--- src/wasi/WASI.cpp | 37 ++++++++++++++++++++++ src/wasi/WASI.h | 4 +++ test/wasi/args.wast | 75 +++++++++++++++++++++++++++++++++++++++++++++ tools/run-tests.py | 35 ++++++++++++++++++--- 5 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 test/wasi/args.wast diff --git a/src/shell/Shell.cpp b/src/shell/Shell.cpp index f5ba4056d..4b3fd9c2c 100644 --- a/src/shell/Shell.cpp +++ b/src/shell/Shell.cpp @@ -43,6 +43,7 @@ struct ParseOptions { // WASI options std::vector wasi_envs; std::vector> wasi_dirs; + int argsIndex = -1; }; static uint32_t s_JITFlags = 0; @@ -1039,7 +1040,7 @@ static void runExports(Store* store, const std::string& filename, const std::vec &data); } -static void parseArguments(int argc, char* argv[], ParseOptions& options) +static void parseArguments(int argc, const char* argv[], ParseOptions& options) { for (int i = 1; i < argc; i++) { if (strlen(argv[i]) >= 2 && argv[i][0] == '-') { // parse command line option @@ -1083,6 +1084,15 @@ static void parseArguments(int argc, char* argv[], ParseOptions& options) options.wasi_dirs.push_back(std::make_pair(argv[i + 2], argv[i + 1])); i += 2; continue; + } else if (strcmp(argv[i], "--args") == 0) { + if (i + 1 == argc || argv[i + 1][0] == '-') { + fprintf(stderr, "error: --args requires an argument\n"); + exit(1); + } + ++i; + options.fileNames.emplace_back(argv[i]); + options.argsIndex = i; + break; } else if (strcmp(argv[i], "--help") == 0) { fprintf(stdout, "Usage: walrus [OPTIONS] \n\n"); fprintf(stdout, "OPTIONS:\n"); @@ -1095,6 +1105,7 @@ static void parseArguments(int argc, char* argv[], ParseOptions& options) #endif fprintf(stdout, "\t--mapdirs \n\t\tMap real directories to virtual ones for WASI functions to use.\n\t\tExample: ./walrus test.wasm --mapdirs this/real/directory/ this/virtual/directory\n\n"); fprintf(stdout, "\t--env\n\t\tShare host environment to walrus WASI.\n\n"); + fprintf(stdout, "\t--args [ ... ]\n\t\tRun Webassembly module with arguments: must be followed by the name of the Webassembly module file, then optionally following arguments which are passed on to the module\n\t\tExample: ./walrus --args test.wasm 'hello' 'world' 42\n\n"); exit(0); } } @@ -1117,7 +1128,7 @@ static void parseArguments(int argc, char* argv[], ParseOptions& options) } } -int main(int argc, char* argv[]) +int main(int argc, const char* argv[]) { #ifndef NDEBUG setbuf(stdout, NULL); @@ -1164,8 +1175,8 @@ int main(int argc, char* argv[]) init_options.out = 1; init_options.err = 2; init_options.fd_table_size = 3; - init_options.argc = 0; - init_options.argv = nullptr; + init_options.argc = (options.argsIndex == -1 ? 0 : argc - options.argsIndex); + init_options.argv = (options.argsIndex == -1 ? nullptr : argv + options.argsIndex); init_options.envp = envp.data(); init_options.preopenc = dirs.size(); init_options.preopens = dirs.data(); diff --git a/src/wasi/WASI.cpp b/src/wasi/WASI.cpp index 65b5cd1dc..4450afd25 100644 --- a/src/wasi/WASI.cpp +++ b/src/wasi/WASI.cpp @@ -102,6 +102,43 @@ WASI::WasiFuncInfo* WASI::find(const std::string& funcName) return nullptr; } +void WASI::args_get(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + uvwasi_size_t argc; + uvwasi_size_t bufSize; + uvwasi_args_sizes_get(WASI::g_uvwasi, &argc, &bufSize); + + uint32_t* uvArgv = reinterpret_cast(get_memory_pointer(instance, argv[0], argc * sizeof(uint32_t))); + char* uvArgBuf = reinterpret_cast(get_memory_pointer(instance, argv[1], bufSize)); + + if (uvArgv == nullptr || uvArgBuf == nullptr) { + result[0] = Value(WasiErrNo::inval); + return; + } + + TemporaryData pointers(argc); + + if (pointers.data() == nullptr) { + result[0] = Value(WasiErrNo::inval); + return; + } + + char** data = reinterpret_cast(pointers.data()); + result[0] = Value(static_cast(uvwasi_args_get(WASI::g_uvwasi, data, uvArgBuf))); + + for (uvwasi_size_t i = 0; i < argc; i++) { + uvArgv[i] = data[i] - uvArgBuf; + } +} + +void WASI::args_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + uvwasi_size_t* uvArgc = reinterpret_cast(get_memory_pointer(instance, argv[0], sizeof(uint32_t))); + uvwasi_size_t* uvArgvBufSize = reinterpret_cast(get_memory_pointer(instance, argv[1], sizeof(uint32_t))); + + result[0] = Value(static_cast(uvwasi_args_sizes_get(WASI::g_uvwasi, uvArgc, uvArgvBufSize))); +} + void WASI::proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance) { ASSERT(argv[0].type() == Value::I32); diff --git a/src/wasi/WASI.h b/src/wasi/WASI.h index 924b215f3..cca3980fa 100644 --- a/src/wasi/WASI.h +++ b/src/wasi/WASI.h @@ -36,6 +36,8 @@ class WASI { // https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md #define FOR_EACH_WASI_FUNC(F) \ + F(args_get, I32I32_RI32) \ + F(args_sizes_get, I32I32_RI32) \ F(proc_exit, I32R) \ F(proc_raise, I32_RI32) \ F(clock_res_get, I32I32_RI32) \ @@ -153,6 +155,8 @@ class WASI { private: // wasi functions + static void args_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); + static void args_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void proc_raise(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void clock_res_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); diff --git a/test/wasi/args.wast b/test/wasi/args.wast new file mode 100644 index 000000000..078223d4c --- /dev/null +++ b/test/wasi/args.wast @@ -0,0 +1,75 @@ +(module + (import "wasi_snapshot_preview1" "fd_write" (func $wasi_fd_write (param i32 i32 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 i32 i32) (result i32))) + (memory 1) + + (export "memory" (memory 0)) + (export "print_args" (func $print_args)) + (data (i32.const 100) "500" ) + + (func $print_args + (local $i i32) + i32.const 0 ;; args count + i32.const 12 ;; args overall size in characters + call $wasi_args_sizes_get + drop + + i32.const 500 ;; argp + i32.const 600 ;; argp[0] + call $wasi_args_get + drop + + ;; Memory[100] = 600, start of output string. + i32.const 100 + i32.const 600 + i32.store + + ;; Memory[104] = size of output string. + i32.const 104 + i32.const 12 + i32.load + i32.store + + ;; Replace '\0' with '\n' for readable printing. + i32.const 0 + local.set $i + (loop $loop + i32.const 600 + local.get $i + i32.add + i32.load8_u + + i32.eqz + (if + (then + i32.const 600 + local.get $i + i32.add + i32.const 10 + i32.store8 + ) + ) + + local.get $i + i32.const 1 + i32.add + local.tee $i + + i32.const 12 + i32.load + i32.lt_u + br_if $loop + ) + + (call $wasi_fd_write + (i32.const 1) ;;file descriptor + (i32.const 100) ;;offset of str offset + (i32.const 1) ;;iovec length + (i32.const 200) ;;result offset + ) + drop + ) +) + +(assert_return (invoke "print_args")) diff --git a/tools/run-tests.py b/tools/run-tests.py index 892b04052..df37e135e 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -61,7 +61,7 @@ def __call__(self, fn): DEFAULT_RUNNERS.append(self.suite) return fn -def _run_wast_tests(engine, files, is_fail): +def _run_wast_tests(engine, files, is_fail, args=None, expect_stdout=None): fails = 0 for file in files: if jit: @@ -69,14 +69,23 @@ def _run_wast_tests(engine, files, is_fail): if filename in JIT_EXCLUDE_FILES: continue - proc = Popen([engine, "--mapdirs", "./test/wasi", "/var", file], stdout=PIPE) if not jit else Popen([engine, "--mapdirs", "./test/wasi", "/var", "--jit", file], stdout=PIPE) + subprocess_args = [engine, "--mapdirs", "./test/wasi", "/var"] + if jit: subprocess_args.append("--jit") + if args: subprocess_args.append("--args") + subprocess_args.append(file) + if args: subprocess_args.extend(args) + + proc = Popen(subprocess_args, stdout=PIPE) out, _ = proc.communicate() - if is_fail and proc.returncode or not is_fail and not proc.returncode: + if ((is_fail and proc.returncode or not is_fail and not proc.returncode) + and (expect_stdout is None or out == expect_stdout)): print('%sOK: %s%s' % (COLOR_GREEN, file, COLOR_RESET)) else: print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, file, COLOR_RESET)) - print(out) + print(f"Result: {out}") + if expect_stdout is not None: + print(f"Expected: {expect_stdout}") fails += 1 @@ -136,6 +145,24 @@ def run_wasi_tests(engine): if fail_total > 0: raise Exception("basic wasi tests failed") +@runner('wasi-args', default=True) +def run_wasi_args_tests(engine): + TEST_DIR = join(PROJECT_SOURCE_DIR, 'test', 'wasi') + + print('Running wasi-args tests:') + xpass = glob(join(TEST_DIR, 'args.wast')) + + expected_stdout = f"{xpass[0]}\nHello\nWorld!\n".encode('utf-8') + xpass_result = _run_wast_tests(engine, xpass, False, args=["Hello", "World!"], expect_stdout=expected_stdout) + + tests_total = len(xpass) + fail_total = xpass_result + print('TOTAL: %d' % (tests_total)) + print('%sPASS : %d%s' % (COLOR_GREEN, tests_total - fail_total, COLOR_RESET)) + print('%sFAIL : %d%s' % (COLOR_RED, fail_total, COLOR_RESET)) + + if fail_total > 0: + raise Exception("basic wasi-args tests failed") @runner('jit', default=True) def run_jit_tests(engine):