diff --git a/src/runtime/Function.cpp b/src/runtime/Function.cpp index a225f7565..663e00914 100644 --- a/src/runtime/Function.cpp +++ b/src/runtime/Function.cpp @@ -149,4 +149,49 @@ void ImportedFunction::call(ExecutionState& state, Value* argv, Value* result) m_callback(newState, argv, result, m_data); } +WasiFunction* WasiFunction::createWasiFunction(Store* store, + FunctionType* functionType, + WasiFunctionCallback callback, + Instance* instance) +{ + WasiFunction* func = new WasiFunction(functionType, + callback, + instance); + store->appendExtern(func); + return func; +} + +void WasiFunction::interpreterCall(ExecutionState& state, uint8_t* bp, ByteCodeStackOffset* offsets, + uint16_t parameterOffsetCount, uint16_t resultOffsetCount) +{ + const FunctionType* ft = functionType(); + const ValueTypeVector& paramTypeInfo = ft->param(); + const ValueTypeVector& resultTypeInfo = ft->result(); + + ALLOCA(Value, paramVector, sizeof(Value) * paramTypeInfo.size()); + ALLOCA(Value, resultVector, sizeof(Value) * resultTypeInfo.size()); + + size_t offsetIndex = 0; + size_t size = paramTypeInfo.size(); + Value* paramVectorStart = paramVector; + for (size_t i = 0; i < size; i++) { + paramVector[i] = Value(paramTypeInfo[i], bp + offsets[offsetIndex]); + offsetIndex += valueFunctionCopyCount(paramTypeInfo[i]); + } + + call(state, paramVectorStart, resultVector); + + for (size_t i = 0; i < resultTypeInfo.size(); i++) { + resultVector[i].writeToMemory(bp + offsets[offsetIndex]); + offsetIndex += valueFunctionCopyCount(resultTypeInfo[i]); + } +} + +void WasiFunction::call(ExecutionState& state, Value* argv, Value* result) +{ + ExecutionState newState(state, this); + CHECK_STACK_LIMIT(newState); + m_callback(newState, argv, result, this->m_runningInstance); +} + } // namespace Walrus diff --git a/src/runtime/Function.h b/src/runtime/Function.h index c4d8ab4ed..c025eab7f 100644 --- a/src/runtime/Function.h +++ b/src/runtime/Function.h @@ -43,6 +43,7 @@ class FunctionType; class ModuleFunction; class DefinedFunction; class ImportedFunction; +class WasiFunction; class Function : public Extern { public: @@ -71,6 +72,10 @@ class Function : public Extern { { return false; } + virtual bool isWasiFunction() const + { + return false; + } DefinedFunction* asDefinedFunction() { @@ -84,6 +89,12 @@ class Function : public Extern { return reinterpret_cast(this); } + WasiFunction* asWasiFunctions() + { + assert(isWasiFunction()); + return reinterpret_cast(this); + } + protected: Function(FunctionType* functionType) : m_functionType(functionType) @@ -168,6 +179,43 @@ class ImportedFunction : public Function { void* m_data; }; +class WasiFunction : public Function { +public: + typedef std::function WasiFunctionCallback; + + static WasiFunction* createWasiFunction(Store* store, + FunctionType* functionType, + WasiFunctionCallback callback, + Instance* instance); + + virtual bool isWasiFunction() const override + { + return true; + } + + void setRunningInstance(Instance* instance) + { + m_runningInstance = instance; + } + + virtual void call(ExecutionState& state, Value* argv, Value* result) override; + virtual void interpreterCall(ExecutionState& state, uint8_t* bp, ByteCodeStackOffset* offsets, + uint16_t parameterOffsetCount, uint16_t resultOffsetCount) override; + +protected: + WasiFunction(FunctionType* functionType, + WasiFunctionCallback callback, + Instance* instance) + : Function(functionType) + , m_callback(callback) + , m_runningInstance(instance) + { + } + + WasiFunctionCallback m_callback; + Instance* m_runningInstance; +}; + } // namespace Walrus #endif // __WalrusFunction__ diff --git a/src/runtime/Module.cpp b/src/runtime/Module.cpp index 9c24b8e1c..baae2c48f 100644 --- a/src/runtime/Module.cpp +++ b/src/runtime/Module.cpp @@ -27,6 +27,7 @@ #include "interpreter/ByteCode.h" #include "interpreter/Interpreter.h" #include "parser/WASMParser.h" +#include "wasi/Wasi.h" namespace Walrus { @@ -137,7 +138,11 @@ Instance* Module::instantiate(ExecutionState& state, const ExternVector& imports if (!imports[i]->asFunction()->functionType()->equals(m_imports[i]->functionType())) { Trap::throwException(state, "imported function type mismatch"); } - instance->m_functions[funcIndex++] = imports[i]->asFunction(); + instance->m_functions[funcIndex] = imports[i]->asFunction(); + if (imports[i]->asFunction()->isWasiFunction()) { + instance->m_functions[funcIndex]->asWasiFunctions()->setRunningInstance(instance); + } + funcIndex++; break; } case ImportType::Global: { diff --git a/src/runtime/SpecTest.h b/src/runtime/SpecTest.h index 65062014d..8aa227f72 100644 --- a/src/runtime/SpecTest.h +++ b/src/runtime/SpecTest.h @@ -26,12 +26,14 @@ class SpecTestFunctionTypes { // The R is meant to represent the results, after R are the result types. NONE = 0, I32R, + I32_RI32, RI32, I64R, F32R, F64R, I32F32R, F64F64R, + I32I32I32I32I64I64I32_RI32I32, INVALID, INDEX_NUM, }; @@ -57,6 +59,14 @@ class SpecTestFunctionTypes { param->push_back(Value::Type::I32); m_vector[index++] = new FunctionType(param, result); } + { + // I32_RI32 + param = new ValueTypeVector(); + result = new ValueTypeVector(); + param->push_back(Value::Type::I32); + result->push_back(Value::Type::I32); + m_vector[index++] = new FunctionType(param, result); + } { // RI32 param = new ValueTypeVector(); @@ -101,6 +111,21 @@ class SpecTestFunctionTypes { param->push_back(Value::Type::F64); m_vector[index++] = new FunctionType(param, result); } + { + // I32I32I32I32I64I64I32_RI32I32 + 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::I64); + param->push_back(Value::Type::I64); + param->push_back(Value::Type::I32); + result->push_back(Value::Type::I32); + result->push_back(Value::Type::I32); + m_vector[index++] = new FunctionType(param, result); + } { // INVALID param = new ValueTypeVector(); diff --git a/src/shell/Shell.cpp b/src/shell/Shell.cpp index 97c6ea1ba..b33f108f3 100644 --- a/src/shell/Shell.cpp +++ b/src/shell/Shell.cpp @@ -107,7 +107,7 @@ static void printF64(double v) printf("%s : f64\n", formatDecmialString(ss.str()).c_str()); } -static Trap::TrapResult executeWASM(Store* store, const std::string& filename, const std::vector& src, SpecTestFunctionTypes& functionTypes, WASI* wasi, +static Trap::TrapResult executeWASM(Store* store, const std::string& filename, const std::vector& src, SpecTestFunctionTypes& functionTypes, std::map* registeredInstanceMap = nullptr) { auto parseResult = WASMParser::parseBinary(store, filename, src.data(), src.size()); @@ -231,11 +231,11 @@ 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()); + Walrus::WASI::WasiFunc* wasiImportFunc = WASI::find(import->fieldName()); if (wasiImportFunc != nullptr) { FunctionType* fn = functionTypes[wasiImportFunc->functionType]; if (fn->equals(import->functionType())) { - importValues.push_back(ImportedFunction::createImportedFunction( + importValues.push_back(WasiFunction::createWasiFunction( store, const_cast(import->functionType()), wasiImportFunc->ptr, @@ -579,7 +579,7 @@ static Instance* fetchInstance(wabt::Var& moduleVar, std::map return registeredInstanceMap[moduleVar.name()]; } -static void executeWAST(Store* store, const std::string& filename, const std::vector& src, SpecTestFunctionTypes& functionTypes, WASI* wasi) +static void executeWAST(Store* store, const std::string& filename, const std::vector& src, SpecTestFunctionTypes& functionTypes) { auto lexer = wabt::WastLexer::CreateBufferLexer("test.wabt", src.data(), src.size()); if (!lexer) { @@ -607,7 +607,7 @@ static void executeWAST(Store* store, const std::string& filename, const std::ve case wabt::CommandType::ScriptModule: { auto* moduleCommand = static_cast(command.get()); auto buf = readModuleData(&moduleCommand->module); - auto trapResult = executeWASM(store, filename, buf->data, functionTypes, wasi, ®isteredInstanceMap); + auto trapResult = executeWASM(store, filename, buf->data, functionTypes, ®isteredInstanceMap); RELEASE_ASSERT(!trapResult.exception); instanceMap[commandCount] = store->getLastInstance(); if (moduleCommand->module.name.size()) { @@ -668,7 +668,7 @@ static void executeWAST(Store* store, const std::string& filename, const std::ve auto tsm = dynamic_cast(m); RELEASE_ASSERT(tsm); auto buf = readModuleData(&tsm->module); - auto trapResult = executeWASM(store, filename, buf->data, functionTypes, wasi, ®isteredInstanceMap); + auto trapResult = executeWASM(store, filename, buf->data, functionTypes, ®isteredInstanceMap); RELEASE_ASSERT(trapResult.exception); std::string& s = trapResult.exception->message(); RELEASE_ASSERT(s.find(assertModuleUninstantiable->text) == 0); @@ -705,7 +705,7 @@ static void executeWAST(Store* store, const std::string& filename, const std::ve } else { buf = dsm->data; } - auto trapResult = executeWASM(store, filename, buf, functionTypes, wasi); + auto trapResult = executeWASM(store, filename, buf, functionTypes); RELEASE_ASSERT(trapResult.exception); std::string& actual = trapResult.exception->message(); printf("assertModuleInvalid (expect compile error: '%s', actual '%s'(line: %d)) : OK\n", assertModuleInvalid->text.data(), actual.data(), assertModuleInvalid->module->location().line); @@ -728,7 +728,7 @@ static void executeWAST(Store* store, const std::string& filename, const std::ve } else { buf = dsm->data; } - auto trapResult = executeWASM(store, filename, buf, functionTypes, wasi); + auto trapResult = executeWASM(store, filename, buf, functionTypes); RELEASE_ASSERT(trapResult.exception); break; } @@ -905,14 +905,14 @@ int main(int argc, char* argv[]) if (!argParser.exportToRun.empty()) { runExports(store, filePath, buf, argParser.exportToRun); } else { - auto trapResult = executeWASM(store, filePath, buf, functionTypes, wasi); + auto trapResult = executeWASM(store, filePath, buf, functionTypes); if (trapResult.exception) { fprintf(stderr, "Uncaught Exception: %s\n", trapResult.exception->message().data()); return -1; } } } else if (endsWith(filePath, "wat") || endsWith(filePath, "wast")) { - executeWAST(store, filePath, buf, functionTypes, wasi); + executeWAST(store, filePath, buf, functionTypes); } } else { printf("Cannot open file %s\n", filePath.data()); diff --git a/src/wasi/Fd.h b/src/wasi/Fd.h new file mode 100644 index 000000000..f069d711b --- /dev/null +++ b/src/wasi/Fd.h @@ -0,0 +1,37 @@ +/* + * 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::fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance) +{ + WASI::wasi_fd_t fd = static_cast(argv[0].asI32()); + for (auto file : WASI::m_wasiFiles) { + if (file->directory) { + result[0] = Value(static_cast(WASI::wasi_errno::success)); + } + if (file->fd == fd) { + file->ptr->close(); + if (file->ptr->bad()) { + result[0] = Value(static_cast(WASI::wasi_errno::noent)); + } + result[0] = Value(static_cast(WASI::wasi_errno::success)); + } + } + result[0] = Value(static_cast(WASI::wasi_errno::inval)); +} + +} // namespace Walrus diff --git a/src/wasi/Path.h b/src/wasi/Path.h new file mode 100644 index 000000000..acc7824c1 --- /dev/null +++ b/src/wasi/Path.h @@ -0,0 +1,126 @@ +/* + * 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) +{ + WASI::wasi_fd_t dirfd = static_cast(argv[0].asI32()); + WASI::wasi_lookupflags_t dirflags = static_cast(argv[1].asI32()); + std::string str; + if (!checkStr(instance->memory(0), argv[2].asI32(), str)) { + result[1] = Value(static_cast(0)); + result[0] = Value(static_cast(WASI::wasi_errno::inval)); + } + fs::path* path = new fs::path(str); + WASI::wasi_oflags_t oflags = static_cast(argv[3].asI32()); + WASI::wasi_rights_t fs_rights_base = static_cast(argv[4].asI64()); + WASI::wasi_rights_t fs_rights_inheriting = static_cast(argv[5].asI64()); + WASI::wasi_fdflags_t fdflags = static_cast(argv[6].asI32()); + + bool exists = fs::exists(*path); + bool is_dir = fs::is_directory(*path); + + if (0 == (fs_rights_base & WASI::wasi_rights::right_path_open)) { + result[1] = Value(static_cast(0)); + result[0] = Value(static_cast(WASI::wasi_errno::acces)); + } + + // lookup flags + if (0 != (dirflags & WASI::wasi_lookupflags::symlink_follow)) { + if (fs::is_symlink(*path)) { + *path = fs::read_symlink(*path); + if (path->empty()) { + result[1] = Value(static_cast(0)); + result[0] = Value(static_cast(WASI::wasi_errno::badf)); + } + } + } + + std::fstream::openmode open_mode = static_cast(0); + + // file right flags + if (0 != (fs_rights_base & (wasi_rights::right_fd_read | wasi_rights::right_fd_readdir))) { + open_mode = open_mode | std::fstream::in; + } + if (0 != (fs_rights_base & (wasi_rights::right_fd_write | wasi_rights::right_fd_datasync | wasi_rights::right_fd_allocate | wasi_rights::right_fd_filestat_set_size))) { + open_mode = open_mode | std::fstream::out; + } + + // open-mode flags + if (0 != (oflags & wasi_oflags::oflag_directory) && !is_dir) { + result[1] = Value(static_cast(0)); + result[0] = Value(static_cast(WASI::wasi_errno::notdir)); + } else if (0 != (oflags & wasi_oflags::oflag_excl) && exists) { + result[1] = Value(static_cast(0)); + result[0] = Value(static_cast(WASI::wasi_errno::exist)); + } else if (0 != (oflags & wasi_oflags::oflag_trunc) || 0 != (fs_rights_base & wasi_rights::right_path_filestat_set_size)) { + open_mode = open_mode | std::fstream::trunc; + } else if (0 != (fs_rights_base & wasi_rights::right_path_create_file) && !exists) { + if ((0 == (oflags & wasi_oflags::oflag_create))) { + result[1] = Value(static_cast(0)); + result[0] = Value(static_cast(WASI::wasi_errno::acces)); + } + } + + // file descriptor flags + if (0 != (fdflags & wasi_fdflags::append)) { + open_mode = open_mode | std::fstream::app; + } + + std::fstream* fs = new std::fstream(); + // if file does not exists and it needs to be created, create it then open it with open_mode options + if (0 != (oflags & wasi_oflags::oflag_create) && !exists && !is_dir) { + fs->open(*path, std::fstream::out); + fs->close(); + } else if (0 == (oflags & wasi_oflags::oflag_create)) { + result[1] = Value(static_cast(0)); + result[0] = Value(static_cast(WASI::wasi_errno::acces)); + } + + fs->open(*path, open_mode); + if (fs->bad()) { + result[1] = Value(static_cast(0)); + result[0] = Value(static_cast(WASI::wasi_errno::noent)); + } + + std::default_random_engine generator; + std::uniform_int_distribution distribution(1, static_cast(std::pow(2, 31) - 1)); + distribution(generator); + WASI::wasi_file_ptr_t* file = new WASI::wasi_file_ptr(); + file->fd = distribution(generator); + for (auto& file_ : WASI::m_wasiFiles) { + while (file_->fd == file->fd) { + file->fd = distribution(generator); + } + } + if (is_dir) { + file->directory = true; + } else { + file->ptr = fs; + } + file->oflags = oflags; + file->rights = fs_rights_base; + file->rights_inheriting = fs_rights_inheriting; + file->fdflags = fdflags; + file->path = path; + WASI::m_wasiFiles.pushBack(file); + + result[1] = Value(static_cast(file->fd)); + result[0] = Value(static_cast(WASI::wasi_errno::success)); +} + +} // namespace Walrus diff --git a/src/wasi/Wasi.cpp b/src/wasi/Wasi.cpp index 48a60a028..f2de5d7b4 100644 --- a/src/wasi/Wasi.cpp +++ b/src/wasi/Wasi.cpp @@ -15,11 +15,16 @@ */ #include "wasi/Wasi.h" +#include "wasi/Path.h" +#include "wasi/Fd.h" // https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md namespace Walrus { +Vector WASI::m_wasiFiles; +WASI::WasiFunc WASI::m_wasiFunctions[FuncEnd]; + WASI::WasiFunc* WASI::find(std::string funcName) { for (unsigned i = 0; i < WasiFuncName::FuncEnd; ++i) { @@ -30,7 +35,21 @@ WASI::WasiFunc* WASI::find(std::string funcName) return nullptr; } -void WASI::proc_exit(ExecutionState& state, Value* argv, Value* result, void* data) +bool WASI::checkStr(Memory* memory, WASI::wasi_size_t memoryOffset, std::string& str) +{ + for (uint32_t i = memoryOffset; i < memory->sizeInByte(); ++i) { + if (memoryOffset >= memory->sizeInByte()) { + return false; + } else if (*reinterpret_cast(memory->buffer() + memoryOffset + i) == '\0') { + str = std::string(reinterpret_cast(memory->buffer() + memoryOffset)); + break; + } + } + + return true; +} + +void WASI::proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance) { ASSERT(argv[0].type() == Value::I32); exit(argv[0].asI32()); diff --git a/src/wasi/Wasi.h b/src/wasi/Wasi.h index 78a5857da..7d5a92317 100644 --- a/src/wasi/Wasi.h +++ b/src/wasi/Wasi.h @@ -19,6 +19,17 @@ #include "runtime/Function.h" #include "runtime/ObjectType.h" #include "runtime/SpecTest.h" +#include "runtime/Memory.h" +#include "runtime/Instance.h" +#include +#if __cpp_lib_filesystem +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif +#include namespace Walrus { @@ -112,22 +123,136 @@ class WASI { } wasi_errno_t; #undef TO_ENUM + typedef uint32_t wasi_size_t; + + typedef uint32_t wasi_fd_t; + + typedef enum wasi_oflags : uint16_t { + oflag_create, + oflag_directory, + oflag_excl, + oflag_trunc, + } wasi_oflags_t; + + typedef enum wasi_lookupflags : uint32_t { + symlink_follow, + } wasi_lookupflags_t; + + typedef enum wasi_rights : uint64_t { + right_fd_datasync = (0 << 1), + right_fd_read, + right_fd_seek, + right_fd_fdstat_set_flags, + right_fd_sync, + right_fd_tell, + right_fd_write, + right_fd_allocate, + right_path_create_directory, + right_path_create_file, + right_path_link_source, + right_path_link_target, + right_path_open, + right_fd_readdir, + right_path_readlink, + right_path_rename_source, + right_path_rename_target, + right_path_filestat_get, + right_path_filestat_set_size, + right_path_filesta_set_time, + right_fd_filestat_get, + right_fd_filestat_set_size, + right_fd_filestat_set_times, + right_path_symlink, + right_path_remove_directory, + right_poll_fd_readwrite, + right_sock_shutdown, + right_sock_accept, + } wasi_rights_t; + + typedef enum wasi_fdflags : uint16_t { + append, + dsync, + nonblock, + rsync, + sync, + } wasi_fdflags_t; + + typedef uint64_t wasi_device_t; + + typedef uint64_t wasi_inode_t; + + typedef enum wasi_filetype { + filetype_unknown, + filetype_block_device, + filetype_character_device, + filetype_directory, + filetype_regular_file, + filetype_socket_dgram, + filetype_socket_stream, + filetype_symbolic_link + } wasi_filetype_t; + + typedef uint64_t wasi_linkcount_t; + + typedef uint64_t wasi_filesize_t; + + typedef uint64_t wasi_timestamp_t; + + typedef struct wasi_filestat { + 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; + + typedef struct wasi_file_ptr { + wasi_fd_t fd; + wasi_oflags_t oflags; + wasi_rights_t rights; + wasi_rights_t rights_inheriting; + wasi_fdflags_t fdflags; + bool directory; + std::fstream* ptr; + fs::path* path; + wasi_filestat_t* stat; + + wasi_file_ptr() + { + ptr = nullptr; + path = nullptr; + stat = nullptr; + } + } wasi_file_ptr_t; + // end of type definitions WASI(); ~WASI() { + for (auto& file : m_wasiFiles) { + delete file->ptr; + delete file->path; + delete file->stat; + delete file; + } + m_wasiFiles.clear(); } struct WasiFunc { std::string name; SpecTestFunctionTypes::Index functionType; - ImportedFunction::ImportedFunctionCallback ptr; + WasiFunction::WasiFunctionCallback ptr; }; #define FOR_EACH_WASI_FUNC(F) \ - F(proc_exit, I32R) + F(proc_exit, I32R) \ + F(fd_close, I32_RI32) \ + F(path_open, I32I32I32I32I64I64I32_RI32I32) enum WasiFuncName : size_t { #define DECLARE_FUNCTION(NAME, FUNCTYPE) NAME##FUNC, @@ -137,11 +262,15 @@ class WASI { }; void fillWasiFuncTable(); - WasiFunc* find(std::string funcName); + static WasiFunc* find(std::string funcName); + static bool checkStr(Memory* memory, WASI::wasi_size_t memoryOffset, std::string& str); - static void proc_exit(ExecutionState& state, Value* argv, Value* result, void* data); + static void proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance); + static void path_open(ExecutionState& state, Value* argv, Value* result, Instance* instance); + static void fd_close(ExecutionState& state, Value* argv, Value* result, Instance* instance); - WasiFunc m_wasiFunctions[FuncEnd]; + static Vector m_wasiFiles; + static WasiFunc m_wasiFunctions[FuncEnd]; }; } // namespace Walrus diff --git a/test/wasi/create_test.wast b/test/wasi/create_test.wast new file mode 100644 index 000000000..e54d3f212 --- /dev/null +++ b/test/wasi/create_test.wast @@ -0,0 +1,40 @@ +(; + Creates a text file, named test.txt at the folder the environment is ran from. + Will be updated to function like touch as more functions are implemented. +;) + +(module + ;; path open: i32:fd, i32:lookupflags, i32:path index, i32:oflags, i64:rights, i64:rights, i32:fdflags + (import "wasi_snapshot_preview1" "path_open" (func $path_open (param i32 i32 i32 i32 i64 i64 i32) (result i32 i32))) + ;; fd_close: i32:fd + (import "wasi_snapshot_preview1" "fd_close" (func $fd_close (param i32) (result i32))) + (memory $mem 1) + (data $files (memory $mem) (i32.const 0) "test.txt\u{0}" "test2.txt\u{0}" "test3.txt\u{0}" "./\u{0}" "TestDir\u{0}") + (global $oflag_create i32 (i32.const 1)) + (global $fs_path_open i64 (i64.const 8192)) + (global $fs_read i64 (i64.const 2)) + + (func (export "create_test") + (local i64 i32) ;; rights + + global.get $fs_path_open + global.get $fs_read + i64.or + local.set 0 + + ;; only create file for reading + i32.const 0 + i32.const 0 + i32.const 0 + global.get $oflag_create + local.get 0 + i64.const 0 + i32.const 0 + call $path_open + call $fd_close + drop + drop + ) +) + +(assert_return (invoke "create_test"))