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 d58b82bb6..4f98de23c 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; @@ -999,11 +1001,12 @@ 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) { + auto it = args.begin(); + while (it != args.end()) { if (*it == "--run-export") { if (*it == args.back()) { fprintf(stderr, "error: --run-export requires an argument\n"); @@ -1018,6 +1021,40 @@ 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) { + if (it + 1 == args.end()) { + break; + } + + uvwasi_preopen_s preopen; + preopen.real_path = reinterpret_cast(calloc(1, it->length() + 1)); + it->nonstd::sv_lite::string_view::copy(const_cast(preopen.real_path), it->length()); + + it++; + + preopen.mapped_path = reinterpret_cast(calloc(1, it->length() + 1)); + it->nonstd::sv_lite::string_view::copy(const_cast(preopen.mapped_path), it->length()); + preopenDirs.push_back(preopen); + it++; + } + it--; + } else if (*it == "--env") { + shareHostEnv = true; + } else 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--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\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"); + + + exit(0); } else { auto arg = nonstd::to_string(*it); if (endsWith(arg, "wat") || endsWith(arg, "wast") || endsWith(arg, "wasm")) { @@ -1027,6 +1064,7 @@ static void parseArguments(int argc, char* argv[], ArgParser& argParser) exit(1); } } + ++it; } if (argParser.fileNames.empty()) { @@ -1055,12 +1093,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..a676898a9 100644 --- a/src/wasi/Fd.h +++ b/src/wasi/Fd.h @@ -31,13 +31,52 @@ 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); + uvwasi_size_t* out_addr = (uvwasi_size_t*)(instance->memory(0)->buffer() + out); - result[0] = Value(static_cast(uvwasi_fd_write(WASI::m_uvwasi, fd, iovs.data(), iovs.size(), &out_addr))); - *(instance->memory(0)->buffer() + out) = out_addr; + result[0] = Value(static_cast(uvwasi_fd_write(WASI::m_uvwasi, fd, iovs.data(), iovs.size(), out_addr))); + *(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 = (uvwasi_size_t*)(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..de84afed6 --- /dev/null +++ b/src/wasi/Path.h @@ -0,0 +1,48 @@ +/* + * 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(static_cast( + 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 ac0401f61..d97ac033f 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 @@ -93,11 +95,10 @@ 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))); uvwasi_options_t init_options; @@ -108,12 +109,83 @@ WASI::WASI() init_options.argc = 0; init_options.argv = nullptr; init_options.envp = nullptr; - init_options.preopenc = 0; + if (shareHostEnv) { + std::vector variables = { + // Common Unix environment variables. + "PATH", + "LIB", + "PWD", + "SHELL", + "TERM", + "HOME", + "LOGNAME", + "HOSTNAME", + "UID", + "TEMP", + "EDITOR" + "LANG", + "LC_TIME", + // Common Windows environment variables. + "APPDATA", + "USERPROFILE", + "USERDOMAIN", + "windir", + "ProgramFiles", + "OS" + "LOCALAPPDATA", + "SystemRoot", + "SystemDrive", + }; + + char** envp = (char**)malloc(sizeof(char**) * variables.size()); + + size_t j = 0; + for (size_t i = 0; i < variables.size(); i++) { + if (getenv(variables[i].c_str()) == nullptr) { + continue; + } + + size_t length = strlen(variables[i].c_str()); + length += strlen(getenv(variables[i].c_str())); + length += 2; + envp[j] = (char*)malloc(length); + strcpy(envp[j], variables[i].c_str()); + envp[j][variables[i].size()] = '='; + strcpy(envp[j] + variables[i].size() + 1, getenv(variables[i].c_str())); + envp[j][length - 1] = '\0'; + j++; + } + + while (j < variables.size()) { + envp[j] = nullptr; + j++; + } + + init_options.envp = const_cast(envp); + } + init_options.preopenc = preopenDirs.size(); + init_options.preopens = preopenDirs.data(); 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 (init_options.envp != nullptr) { + for (size_t i = 0; init_options.envp[i] != nullptr; i++) { + free((void*)init_options.envp[i]); + } + free(init_options.envp); + } + + for (auto& elem : preopenDirs) { + if (elem.mapped_path != nullptr) { + free((void*)elem.mapped_path); + } + if (elem.real_path != nullptr) { + free((void*)elem.real_path); + } + } } } // namespace Walrus diff --git a/src/wasi/Wasi.h b/src/wasi/Wasi.h index bbbdb8e06..1af06bbe9 100644 --- a/src/wasi/Wasi.h +++ b/src/wasi/Wasi.h @@ -116,11 +116,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,11 +130,17 @@ class WASI { WasiFunction::WasiFunctionCallback ptr; }; -#define FOR_EACH_WASI_FUNC(F) \ - F(proc_exit, I32R) \ - F(proc_raise, I32_RI32) \ - F(random_get, I32I32_RI32) \ - F(fd_write, I32I32I32I32_RI32) +#define FOR_EACH_WASI_FUNC(F) \ + F(proc_exit, I32R) \ + F(proc_raise, I32_RI32) \ + 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, @@ -150,6 +157,12 @@ class WASI { 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 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..6202e111c --- /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..941f8feab 100644 --- a/test/wasi/hello_world.wast +++ b/test/wasi/hello_world.wast @@ -1,5 +1,5 @@ (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)) diff --git a/test/wasi/random_get.wast b/test/wasi/random_get.wast index 70e743000..dd5933783 100644 --- a/test/wasi/random_get.wast +++ b/test/wasi/random_get.wast @@ -1,10 +1,21 @@ (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 (call $random (i32.const 10) (i32.const 5)) - drop + i32.eqz + (if + (then + i32.const 0 + call $proc_exit + ) + (else + i32.const 1 + call $proc_exit + ) + ) ) ) diff --git a/test/wasi/read_from_file.wast b/test/wasi/read_from_file.wast new file mode 100644 index 000000000..64154c7eb --- /dev/null +++ b/test/wasi/read_from_file.wast @@ -0,0 +1,64 @@ +(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..54a521b00 --- /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..52500da1c 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -20,7 +20,8 @@ import traceback import sys import time -import re, fnmatch +import re +import fnmatch from argparse import ArgumentParser from difflib import unified_diff @@ -101,10 +102,11 @@ def readfile(filename): with open(filename, 'r') as f: return f.readlines() + def _run_wast_tests(engine, files, is_fail): fails = 0 for file in files: - proc = Popen([engine, file], stdout=PIPE) if not jit else Popen([engine, '--jit', file], stdout=PIPE) + proc = Popen([engine, "--mapdirs", "./test/wasi", "/var", file], stdout=PIPE) if not jit else Popen([engine, "--mapdirs", "./test/wasi", "/var", "--jit", file], stdout=PIPE) out, _ = proc.communicate() if is_fail and proc.returncode or not is_fail and not proc.returncode: @@ -135,6 +137,7 @@ def run_basic_tests(engine): if fail_total > 0: raise Exception("basic tests failed") + @runner('wasm-test-core', default=True) def run_core_tests(engine): TEST_DIR = join(PROJECT_SOURCE_DIR, 'test', 'wasm-spec', 'core') @@ -152,6 +155,7 @@ def run_core_tests(engine): if fail_total > 0: raise Exception("wasm-test-core failed") + @runner('wasi', default=True) def run_basic_tests(engine): TEST_DIR = join(PROJECT_SOURCE_DIR, 'test', 'wasi') @@ -169,6 +173,7 @@ def run_basic_tests(engine): if fail_total > 0: raise Exception("basic wasi tests failed") + @runner('jit', default=True) def run_basic_tests(engine): TEST_DIR = join(PROJECT_SOURCE_DIR, 'test', 'jit') @@ -186,6 +191,7 @@ def run_basic_tests(engine): if fail_total > 0: raise Exception("basic wasm-test-core failed") + def main(): parser = ArgumentParser(description='Walrus Test Suite Runner') parser.add_argument('--engine', metavar='PATH', default=DEFAULT_WALRUS,