From be1d9fb4e05f3c4e685a9db909a1581b50d366c1 Mon Sep 17 00:00:00 2001 From: Adam Laszlo Kulcsar Date: Thu, 30 Nov 2023 12:29:34 +0100 Subject: [PATCH] Implement directory mapping and WASI file functions Refactor the importing of WASI functions to be clearer. Implement path_open, fd_seek, fd_read, environ_sizes_get, environ_get for file acces. Implement the mapping of real directories to virtual ones so that WASI can use different directories. Add flag '--mapdirs :' for mapping directories. Add flag '--env' for sharing host envrionment variables. Add flag '--help' for printing available walrus options. Change test runner to run WASI tests properly. Also improve random_get test to not have a result. Signed-off-by: Adam Laszlo Kulcsar --- src/runtime/SpecTest.h | 29 ++++++++++++++ src/shell/Shell.cpp | 71 ++++++++++++++++++++++----------- src/wasi/Environ.h | 41 +++++++++++++++++++ src/wasi/Fd.h | 41 ++++++++++++++++++- src/wasi/Path.h | 47 ++++++++++++++++++++++ src/wasi/Wasi.cpp | 75 ++++++++++++++++++++++++++++++++--- src/wasi/Wasi.h | 25 +++++++++--- test/wasi/environ.wast | 75 +++++++++++++++++++++++++++++++++++ test/wasi/hello_world.wast | 4 +- test/wasi/random_get.wast | 16 +++++++- test/wasi/read_from_file.wast | 63 +++++++++++++++++++++++++++++ test/wasi/write_to_file.wast | 53 +++++++++++++++++++++++++ test/wasi/write_to_this.txt | 1 + tools/run-tests.py | 3 +- 14 files changed, 504 insertions(+), 40 deletions(-) create mode 100644 src/wasi/Environ.h create mode 100644 src/wasi/Path.h create mode 100644 test/wasi/environ.wast create mode 100644 test/wasi/read_from_file.wast create mode 100644 test/wasi/write_to_file.wast create mode 100644 test/wasi/write_to_this.txt diff --git a/src/runtime/SpecTest.h b/src/runtime/SpecTest.h index 17b9e18a6..46bfdfdf4 100644 --- a/src/runtime/SpecTest.h +++ b/src/runtime/SpecTest.h @@ -29,6 +29,8 @@ class SpecTestFunctionTypes { I32_RI32, I32I32_RI32, I32I32I32I32_RI32, + I32I64I32I32_RI32, + I32I32I32I32I32I64I64I32I32_RI32, RI32, I64R, F32R, @@ -88,6 +90,33 @@ class SpecTestFunctionTypes { result->push_back(Value::Type::I32); m_vector[index++] = new FunctionType(param, result); } + { + // I32I64I32I32_RI32 + param = new ValueTypeVector(); + result = new ValueTypeVector(); + param->push_back(Value::Type::I32); + param->push_back(Value::Type::I64); + param->push_back(Value::Type::I32); + param->push_back(Value::Type::I32); + result->push_back(Value::Type::I32); + m_vector[index++] = new FunctionType(param, result); + } + { + // I32I32I32I32I32I64I64I32I32_RI32 + param = new ValueTypeVector(); + result = new ValueTypeVector(); + param->push_back(Value::Type::I32); + param->push_back(Value::Type::I32); + param->push_back(Value::Type::I32); + param->push_back(Value::Type::I32); + param->push_back(Value::Type::I32); + param->push_back(Value::Type::I64); + param->push_back(Value::Type::I64); + param->push_back(Value::Type::I32); + param->push_back(Value::Type::I32); + result->push_back(Value::Type::I32); + m_vector[index++] = new FunctionType(param, result); + } { // RI32 param = new ValueTypeVector(); diff --git a/src/shell/Shell.cpp b/src/shell/Shell.cpp index 9c15a9b03..8660760d2 100644 --- a/src/shell/Shell.cpp +++ b/src/shell/Shell.cpp @@ -26,6 +26,7 @@ #include "wabt/wast-lexer.h" #include "wabt/wast-parser.h" #include "wabt/binary-writer.h" +#include "string-view-lite/string_view.h" struct spectestseps : std::numpunct { char do_thousands_sep() const { return '_'; } @@ -39,6 +40,7 @@ struct ArgParser { static bool useJIT = false; static int jitVerbose = 0; +static bool shareHostEnv = false; using namespace Walrus; @@ -237,15 +239,7 @@ static Trap::TrapResult executeWASM(Store* store, const std::string& filename, c nullptr)); } } else if (import->moduleName() == "wasi_snapshot_preview1") { - Walrus::WASI::WasiFunc* wasiImportFunc = WASI::find(import->fieldName()); - if (wasiImportFunc != nullptr) { - FunctionType* fn = functionTypes[wasiImportFunc->functionType]; - if (fn->equals(import->functionType())) { - importValues.push_back(WasiFunction::createWasiFunction( - store, - const_cast(import->functionType()), - wasiImportFunc->ptr)); - } + if (WASI::importWasiFunctions(store, import, importValues, functionTypes)) { hasWasiImport = true; } } else if (registeredInstanceMap) { @@ -925,16 +919,7 @@ static void runExports(Store* store, const std::string& filename, const std::vec for (size_t i = 0; i < importTypes.size(); i++) { auto import = importTypes[i]; if (import->moduleName() == "wasi_snapshot_preview1") { - Walrus::WASI::WasiFunc* wasiImportFunc = WASI::find(import->fieldName()); - if (wasiImportFunc != nullptr) { - FunctionType* fn = functionTypes[wasiImportFunc->functionType]; - if (fn->equals(import->functionType())) { - importValues.push_back(WasiFunction::createWasiFunction( - store, - const_cast(import->functionType()), - wasiImportFunc->ptr)); - } - } + WASI::importWasiFunctions(store, import, importValues, functionTypes); } else { fprintf(stderr, "error: module has imports, but imports are not supported\n"); return; @@ -999,12 +984,26 @@ static void runExports(Store* store, const std::string& filename, const std::vec &data); } -static void parseArguments(int argc, char* argv[], ArgParser& argParser) +static void parseArguments(int argc, char* argv[], ArgParser& argParser, std::vector& preopenDirs) { const std::vector args(argv + 1, argv + argc); - for (auto it = args.begin(); it != args.end(); ++it) { - if (*it == "--run-export") { + auto it = args.begin(); + while (it != args.end()) { + if (*it == "--help" || *it == "-h") { + fprintf(stdout, "Run a WebAssembly wasm, wat or wast file.\n\n"); + fprintf(stdout, "Usage: walrus [OPTIONS] \n\n"); + fprintf(stdout, "OPTIONS:\n"); + fprintf(stdout, "\t-h, --help\n\t\tShow this message then exit.\n\n"); + fprintf(stdout, "\t--run-export \n\t\tRun a specific exported function.\n\n"); + fprintf(stdout, "\t--jit\n\t\tEnable just-in-time interpretation.\n\n"); + fprintf(stdout, "\t--jit-verbose\n\t\tEnable verbose output for just-in-time interpretation.\n\n"); + fprintf(stdout, "\t--jit-verbose-color\n\t\tEnable colored verbose output for just-in-time interpretation.\n\n"); + fprintf(stdout, "\t--mapdirs \n\t\tMap real directories to virtual ones for WASI functions to use.\n\n"); + fprintf(stdout, "\t--env\n\t\tShare host environment to walrus WASI.\n\n"); + + exit(0); + } else if (*it == "--run-export") { if (*it == args.back()) { fprintf(stderr, "error: --run-export requires an argument\n"); exit(1); @@ -1018,15 +1017,38 @@ static void parseArguments(int argc, char* argv[], ArgParser& argParser) jitVerbose = 1; } else if (*it == "--jit-verbose-color") { jitVerbose = 2; + } else if (*it == "--mapdirs") { + it++; + while (it != args.end() && nonstd::to_string(*it).find("--") == std::string::npos) { + size_t delimeter = nonstd::to_string(*it).find(":"); + if (delimeter != std::string::npos) { + uvwasi_preopen_s preopen; + preopen.mapped_path = reinterpret_cast(calloc(1, it->length() - delimeter)); + preopen.real_path = reinterpret_cast(calloc(1, delimeter)); + + it->nonstd::sv_lite::string_view::copy(const_cast(preopen.mapped_path), it->length() - delimeter, delimeter + 1); + it->nonstd::sv_lite::string_view::copy(const_cast(preopen.real_path), delimeter, 0); + + preopenDirs.push_back(preopen); + it++; + } else { + break; + } + } + it--; + } else if (*it == "--env") { + shareHostEnv = true; } else { auto arg = nonstd::to_string(*it); if (endsWith(arg, "wat") || endsWith(arg, "wast") || endsWith(arg, "wasm")) { argParser.fileNames.emplace_back(*it); } else { fprintf(stderr, "error: unknown argument: %s\n", it->data()); + fprintf(stderr, "Use '--help' to see walrus options.\n"); exit(1); } } + ++it; } if (argParser.fileNames.empty()) { @@ -1055,12 +1077,13 @@ int main(int argc, char* argv[]) Engine* engine = new Engine(); Store* store = new Store(engine); - WASI* wasi = new WASI(); + std::vector preopenDirs; SpecTestFunctionTypes functionTypes; ArgParser argParser; - parseArguments(argc, argv, argParser); + parseArguments(argc, argv, argParser, preopenDirs); + WASI* wasi = new WASI(preopenDirs, shareHostEnv); for (const auto& filePath : argParser.fileNames) { FILE* fp = fopen(filePath.data(), "r"); diff --git a/src/wasi/Environ.h b/src/wasi/Environ.h new file mode 100644 index 000000000..9c5af5f2e --- /dev/null +++ b/src/wasi/Environ.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023-present Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Walrus { + +void WASI::environ_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + uint32_t count = argv[0].asI32(); + uint32_t buf = argv[1].asI32(); + + uvwasi_size_t* uvCount = reinterpret_cast(instance->memory(0)->buffer() + count); + uvwasi_size_t* uvBufSize = reinterpret_cast(instance->memory(0)->buffer() + buf); + + result[0] = Value(static_cast(uvwasi_environ_sizes_get(WASI::m_uvwasi, uvCount, uvBufSize))); +} + +void WASI::environ_get(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + uint32_t env = argv[0].asI32(); + uint32_t environBuf = argv[1].asI32(); + + char** uvEnviron = reinterpret_cast(instance->memory(0)->buffer() + env); + char* uvEnvironBuf = reinterpret_cast(instance->memory(0)->buffer() + environBuf); + + result[0] = Value(static_cast(uvwasi_environ_get(WASI::m_uvwasi, uvEnviron, uvEnvironBuf))); +} + +} // namespace Walrus diff --git a/src/wasi/Fd.h b/src/wasi/Fd.h index e0c947db7..e48463c7b 100644 --- a/src/wasi/Fd.h +++ b/src/wasi/Fd.h @@ -31,7 +31,7 @@ void WASI::fd_write(ExecutionState& state, Value* argv, Value* result, Instance* 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 = *(instance->memory(0)->buffer() + iovptr + 4 + i * 8); + iovs[i].buf_len = *reinterpret_cast(instance->memory(0)->buffer() + iovptr + 4 + i * 8); } uvwasi_size_t out_addr = *(instance->memory(0)->buffer() + out); @@ -40,4 +40,43 @@ void WASI::fd_write(ExecutionState& state, Value* argv, Value* result, Instance* *(instance->memory(0)->buffer() + out) = out_addr; } +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); + } + + uvwasi_size_t out_addr = *(instance->memory(0)->buffer() + out); + + result[0] = Value(static_cast(uvwasi_fd_read(WASI::m_uvwasi, fd, iovs.data(), iovs.size(), &out_addr))); + *(instance->memory(0)->buffer() + out) = out_addr; +} + +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::m_uvwasi, fd))); +} + +void WASI::fd_seek(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + uint32_t fd = argv[0].asI32(); + uint64_t fileDelta = argv[1].asI32(); + uint32_t whence = argv[2].asI32(); + uint64_t newOffset = argv[3].asI32(); + + uvwasi_filesize_t out_addr = *(instance->memory(0)->buffer() + newOffset); + + result[0] = Value(static_cast(uvwasi_fd_seek(WASI::m_uvwasi, fd, fileDelta, whence, &out_addr))); + *(instance->memory(0)->buffer() + newOffset) = out_addr; +} + } // namespace Walrus diff --git a/src/wasi/Path.h b/src/wasi/Path.h new file mode 100644 index 000000000..be47b68a5 --- /dev/null +++ b/src/wasi/Path.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023-present Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Walrus { + +void WASI::path_open(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + uint32_t fd = argv[0].asI32(); + uint32_t dirflags = argv[1].asI32(); + uint32_t path_offset = argv[2].asI32(); + uint32_t len = argv[3].asI32(); + uint32_t oflags = argv[4].asI32(); + uint64_t rights = argv[5].asI64(); + uint64_t right_inheriting = argv[6].asI64(); + uint32_t fdflags = argv[7].asI32(); + uint32_t ret_fd_offset = argv[8].asI32(); + + uvwasi_fd_t* ret_fd = reinterpret_cast(instance->memory(0)->buffer() + ret_fd_offset); + + const char* path = reinterpret_cast(instance->memory(0)->buffer() + path_offset); + + result[0] = Value((uvwasi_path_open(WASI::m_uvwasi, + fd, + dirflags, + path, + len, + oflags, + rights, + right_inheriting, + fdflags, + ret_fd))); +} + +} // namespace Walrus diff --git a/src/wasi/Wasi.cpp b/src/wasi/Wasi.cpp index 46a60bb95..df2225ba5 100644 --- a/src/wasi/Wasi.cpp +++ b/src/wasi/Wasi.cpp @@ -16,6 +16,8 @@ #include "wasi/Wasi.h" #include "wasi/Fd.h" +#include "wasi/Path.h" +#include "wasi/Environ.h" // https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md @@ -57,6 +59,23 @@ bool WASI::checkMemOffset(Memory* memory, uint32_t memoryOffset, uint32_t length return true; } +bool WASI::importWasiFunctions(Store* store, Walrus::ImportType* import, ExternVector& importValues, SpecTestFunctionTypes& functionTypes) +{ + Walrus::WASI::WasiFunc* wasiImportFunc = WASI::find(import->fieldName()); + if (wasiImportFunc != nullptr) { + FunctionType* fn = functionTypes[wasiImportFunc->functionType]; + if (fn->equals(import->functionType())) { + importValues.push_back(WasiFunction::createWasiFunction( + store, + const_cast(import->functionType()), + wasiImportFunc->ptr)); + } + return true; + } + + return false; +} + void WASI::proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance) { ASSERT(argv[0].type() == Value::I32); @@ -87,12 +106,43 @@ void WASI::fillWasiFuncTable() #undef WASI_FUNC_TABLE } -WASI::WASI() +WASI::WASI(std::vector& preopenDirs, bool shareHostEnv) { fillWasiFuncTable(); - uvwasi_t uvwasi; - WASI::m_uvwasi = reinterpret_cast(malloc(sizeof(uvwasi_t))); + WASI::m_uvwasi = static_cast(malloc(sizeof(uvwasi_t))); + char** envp = nullptr; + + std::vector envVariables = { + "PATH", + "PWD", + "HOME", + "SHELL", + "LANG", + "USER", + "TERM", + "HOSTTYPE", + "LIB", + "TMPDIR", + "TEMP", + "UID", + "LOGNAME", + }; + + if (shareHostEnv) { + unsigned idx = 0; + envp = static_cast(calloc(1, sizeof(char*) * (envVariables.size()))); + for (unsigned i = 0; i < envVariables.size(); i++) { + if (getenv(envVariables[i].c_str()) != nullptr) { + envp[idx] = static_cast(calloc(1, envVariables[i].length() + std::string(getenv(envVariables[i].c_str())).length() + 3)); + std::strcpy(envp[idx], envVariables[i].c_str()); + envp[idx][envVariables[i].length()] = '='; + std::strcat(envp[idx], const_cast((std::string(getenv(envVariables[i].c_str())).c_str()))); + idx++; + } + } + envp[idx++] = nullptr; + } uvwasi_options_t init_options; init_options.in = 0; @@ -101,13 +151,28 @@ WASI::WASI() init_options.fd_table_size = 3; init_options.argc = 0; init_options.argv = nullptr; - init_options.envp = nullptr; - init_options.preopenc = 0; + init_options.envp = const_cast(envp); + init_options.preopenc = preopenDirs.size(); + init_options.preopens = reinterpret_cast(calloc(init_options.preopenc, sizeof(uvwasi_preopen_s))); + for (unsigned i = 0; i < preopenDirs.size(); i++) { + init_options.preopens[i].mapped_path = preopenDirs[i].mapped_path; + init_options.preopens[i].real_path = preopenDirs[i].real_path; + } init_options.preopen_socketc = 0; init_options.allocator = nullptr; uvwasi_errno_t err = uvwasi_init(WASI::m_uvwasi, &init_options); assert(err == UVWASI_ESUCCESS); + + if (shareHostEnv) { + for (unsigned i = 0; i < envVariables.size(); i++) { + if (envp[i] != nullptr) { + free(envp[i]); + } + } + free(envp); + } + free(init_options.preopens); } } // namespace Walrus diff --git a/src/wasi/Wasi.h b/src/wasi/Wasi.h index fb666c23b..cc220718e 100644 --- a/src/wasi/Wasi.h +++ b/src/wasi/Wasi.h @@ -21,6 +21,7 @@ #include "runtime/SpecTest.h" #include "runtime/Memory.h" #include "runtime/Instance.h" +#include "runtime/Module.h" #include namespace Walrus { @@ -116,11 +117,12 @@ class WASI { #undef TO_ENUM // end of type definitions - WASI(); + WASI(std::vector& preopenDirs, bool shareHostEnv); ~WASI() { ::uvwasi_destroy(m_uvwasi); + free(m_uvwasi); } struct WasiFunc { @@ -129,10 +131,16 @@ class WASI { WasiFunction::WasiFunctionCallback ptr; }; -#define FOR_EACH_WASI_FUNC(F) \ - F(proc_exit, I32R) \ - F(random_get, I32I32_RI32) \ - F(fd_write, I32I32I32I32_RI32) +#define FOR_EACH_WASI_FUNC(F) \ + F(proc_exit, I32R) \ + F(random_get, I32I32_RI32) \ + F(fd_write, I32I32I32I32_RI32) \ + F(fd_read, I32I32I32I32_RI32) \ + F(fd_close, I32_RI32) \ + F(fd_seek, I32I64I32I32_RI32) \ + F(path_open, I32I32I32I32I32I64I64I32I32_RI32) \ + F(environ_get, I32I32_RI32) \ + F(environ_sizes_get, I32I32_RI32) enum WasiFuncName : size_t { #define DECLARE_FUNCTION(NAME, FUNCTYPE) NAME##FUNC, @@ -145,9 +153,16 @@ class WASI { static WasiFunc* find(std::string funcName); static bool checkStr(Memory* memory, uint32_t memoryOffset, std::string& str); static bool checkMemOffset(Memory* memory, uint32_t memoryOffset, uint32_t length); + static bool importWasiFunctions(Store* store, Walrus::ImportType* import, ExternVector& importValues, SpecTestFunctionTypes& functionTypes); static void proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance); 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_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); + static void environ_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); static void random_get(ExecutionState& state, Value* argv, Value* result, Instance* instance); static WasiFunc m_wasiFunctions[FuncEnd]; diff --git a/test/wasi/environ.wast b/test/wasi/environ.wast new file mode 100644 index 000000000..3e8e2abd9 --- /dev/null +++ b/test/wasi/environ.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" "environ_get" (func $wasi_environ_get (param i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "environ_sizes_get" (func $wasi_environ_sizes_get (param i32 i32) (result i32))) + (memory 1) + + (export "memory" (memory 0)) + (export "environ" (func $environ)) + (data (i32.const 100) "500" ) + + (func $environ + (local $i i32) + i32.const 0 ;; environment variables count + i32.const 12 ;; environment variables overall size in characters + call $wasi_environ_sizes_get + drop + + i32.const 500 ;; envp + i32.const 500 ;; envp[0] + call $wasi_environ_get + drop + + ;; Memory + 50 = 500, start of output string. + i32.const 50 + i32.const 500 + i32.store + + ;; Memory + 54 = size of output string. + i32.const 54 + i32.const 12 + i32.load + i32.store + + ;; Replace '\0' with '\n' for readable printing. + i32.const 0 + local.set $i + (loop $loop + i32.const 500 + local.get $i + i32.add + i32.load8_u + + i32.eqz + (if + (then + i32.const 500 + 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 50) ;;offset of str offset + (i32.const 1) ;;iovec length + (i32.const 200) ;;result offset + ) + drop + ) +) + +(assert_return (invoke "environ")) diff --git a/test/wasi/hello_world.wast b/test/wasi/hello_world.wast index b59f12918..3e7eb5cb2 100644 --- a/test/wasi/hello_world.wast +++ b/test/wasi/hello_world.wast @@ -1,7 +1,7 @@ (module - (import "wasi_snapshot_preview1" "fd_write" (func $wasi_fd_write (param i32 i32 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_write" (func $wasi_fd_write (param i32 i32 i32 i32) (result i32))) (memory 1) - + (export "memory" (memory 0)) (export "hello_world" (func $hello_world)) (data (i32.const 0) "Hello World!\n") diff --git a/test/wasi/random_get.wast b/test/wasi/random_get.wast index 838b2db27..dd5933783 100644 --- a/test/wasi/random_get.wast +++ b/test/wasi/random_get.wast @@ -1,10 +1,22 @@ (module (import "wasi_snapshot_preview1" "random_get" (func $random (param i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32))) (memory 1) (export "_start" (func $_start)) - (func $_start (result i32) + (func $_start (call $random (i32.const 10) (i32.const 5)) + i32.eqz + (if + (then + i32.const 0 + call $proc_exit + ) + (else + i32.const 1 + call $proc_exit + ) + ) ) ) -(assert_return (invoke "_start") (i32.const 0)) +(assert_return (invoke "_start")) diff --git a/test/wasi/read_from_file.wast b/test/wasi/read_from_file.wast new file mode 100644 index 000000000..2f3f1f372 --- /dev/null +++ b/test/wasi/read_from_file.wast @@ -0,0 +1,63 @@ +(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_write" (func $wasi_fd_write (param i32 i32 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_read" (func $wasi_fd_read (param i32 i32 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32))) + + (memory 1) + + (export "memory" (memory 0)) + (export "read_from_file" (func $read_from_file)) + (data (i32.const 300) "write_to_this.txt") + + (; This test searches for the file 'write_to_this.txt' in the first opened directory + and reads 13 characters out of it. + It will only read from the file if the directories were mapped correctly ;) + + (func $read_from_file + + i32.const 3 ;; Directory file descriptior, by default 3 is the first opened directory + i32.const 1 ;;lookupflags: directory + i32.const 300 ;; Offset of file name in memory + i32.const 19 ;; Length of file name + i32.const 0 ;; oflags: none + i64.const 4098 ;; rights: path_open, fd_read + i64.const 4098 ;; rights_inheriting: path_open, fd_read + i32.const 0 ;; fdflags: none + i32.const 0 ;; Offset to store at the opened file descriptor in memory + call $path_open + + i32.eqz ;; fail if file could not be opened + (if + (then) + (else + i32.const 1 + call $proc_exit + ) + ) + + (i32.store (i32.const 104) (i32.const 13)) + (i32.store (i32.const 100) (i32.const 0)) + (i32.store (i32.const 200) (i32.const 500)) + + (call $wasi_fd_read + (i32.const 0) + (i32.load) ;; opened file descriptor + + (i32.const 100) ;; store content at this location + (i32.const 1) ;; make it into a single buffer + (i32.const 200) ;; store number of read characters to this location + ) + drop + + (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 "read_from_file")) diff --git a/test/wasi/write_to_file.wast b/test/wasi/write_to_file.wast new file mode 100644 index 000000000..1f273cc40 --- /dev/null +++ b/test/wasi/write_to_file.wast @@ -0,0 +1,53 @@ +(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_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32))) + + (memory $memory 1) + (data (i32.const 200) "Hello World!\n") ;; String we want to write to file + (data (i32.const 300) "write_to_this.txt") ;; Filename in current directory (where Walrus is ran from) + + (; This test searches for the file 'write_to_this.txt' in the first opened directory. + It will only write into the file if the directories were mapped correctly. ;) + + (func $write_to_file + i32.const 3 ;; Directory file descriptior, by default 3 is the first opened directory + i32.const 1 ;; lookupflags: directory + i32.const 300 ;; Offset of file name in memory + i32.const 19 ;; Length of file name + i32.const 0 ;; oflags: none + i64.const 8256 ;; rights: path_open, fd_write + i64.const 8256 ;; rights_inheriting: path_open, fd_write + i32.const 0 ;; fdflags: none + i32.const 0 ;; Offset to store at the opened file descriptor in memory + call $path_open + + i32.eqz ;; fail if file could not be opened + (if + (then) + (else + i32.const 1 + call $proc_exit + ) + ) + + i32.const 500 ;; offset of str offset + i32.const 200 ;; offset of str + i32.store + + i32.const 504 ;; str length offset + i32.const 13 + i32.store + + i32.const 0 ;; load file descriptior + i32.load + i32.const 500 ;; offset of str offset + i32.const 1 ;; iovec length + i32.const 0 ;; offset of written characters + call $fd_write + drop + ) + + (export "_start" (func $write_to_file)) + (export "memory" (memory 0)) +) diff --git a/test/wasi/write_to_this.txt b/test/wasi/write_to_this.txt new file mode 100644 index 000000000..980a0d5f1 --- /dev/null +++ b/test/wasi/write_to_this.txt @@ -0,0 +1 @@ +Hello World! diff --git a/tools/run-tests.py b/tools/run-tests.py index b6c5ac65c..2e4f563d7 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -103,8 +103,9 @@ def readfile(filename): def _run_wast_tests(engine, files, is_fail): fails = 0 + mapdirPath = join(PROJECT_SOURCE_DIR, 'test', 'wasi', ':', 'var') for file in files: - proc = Popen([engine, file], stdout=PIPE) if not jit else Popen([engine, '--jit', file], stdout=PIPE) + proc = Popen([engine, '--mapdirs', mapdirPath, '--env', file], stdout=PIPE) if not jit else Popen([engine, '--mapdirs', mapdirPath, '--env', '--jit', file], stdout=PIPE) out, _ = proc.communicate() if is_fail and proc.returncode or not is_fail and not proc.returncode: