summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2023-04-05 01:14:43 +0330
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2023-04-26 03:47:15 +0330
commit7e4e9fdb8ff5ce21b541f1d2df9b9215fc3757d9 (patch)
tree61ef99c006fe070b299fc1e1c9dde6ca7b51abe9 /Userland/Libraries
parenteceb244befb0992c47dd13b80bada954b4689053 (diff)
downloadserenity-7e4e9fdb8ff5ce21b541f1d2df9b9215fc3757d9.zip
LibWasm: Start implementing WASI
This commit starts adding support for WASI, along with the framework to implement all the functions (though only a couple are currently implemented).
Diffstat (limited to 'Userland/Libraries')
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h2
-rw-r--r--Userland/Libraries/LibWasm/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibWasm/Forward.h5
-rw-r--r--Userland/Libraries/LibWasm/WASI/Wasi.cpp1017
-rw-r--r--Userland/Libraries/LibWasm/Wasi.h943
5 files changed, 1967 insertions, 1 deletions
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h
index 4c8ca6261f..dac570e96f 100644
--- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h
+++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h
@@ -121,7 +121,7 @@ public:
ALWAYS_INLINE Value& operator=(Value const& value) = default;
template<typename T>
- ALWAYS_INLINE Optional<T> to()
+ ALWAYS_INLINE Optional<T> to() const
{
Optional<T> result;
m_value.visit(
diff --git a/Userland/Libraries/LibWasm/CMakeLists.txt b/Userland/Libraries/LibWasm/CMakeLists.txt
index 5b78f92da6..729156278c 100644
--- a/Userland/Libraries/LibWasm/CMakeLists.txt
+++ b/Userland/Libraries/LibWasm/CMakeLists.txt
@@ -5,6 +5,7 @@ set(SOURCES
AbstractMachine/Validator.cpp
Parser/Parser.cpp
Printer/Printer.cpp
+ WASI/Wasi.cpp
)
serenity_lib(LibWasm wasm)
diff --git a/Userland/Libraries/LibWasm/Forward.h b/Userland/Libraries/LibWasm/Forward.h
index 4bb2c85f84..84006190ef 100644
--- a/Userland/Libraries/LibWasm/Forward.h
+++ b/Userland/Libraries/LibWasm/Forward.h
@@ -11,5 +11,10 @@ namespace Wasm {
class AbstractMachine;
class Validator;
struct ValidationError;
+struct Interpreter;
+
+namespace Wasi {
+struct Implementation;
+}
}
diff --git a/Userland/Libraries/LibWasm/WASI/Wasi.cpp b/Userland/Libraries/LibWasm/WASI/Wasi.cpp
new file mode 100644
index 0000000000..22cc5b369a
--- /dev/null
+++ b/Userland/Libraries/LibWasm/WASI/Wasi.cpp
@@ -0,0 +1,1017 @@
+/*
+ * Copyright (c) 2023, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/ByteReader.h>
+#include <AK/Debug.h>
+#include <AK/FlyString.h>
+#include <AK/SourceLocation.h>
+#include <AK/Tuple.h>
+#include <LibCore/File.h>
+#include <LibWasm/AbstractMachine/Interpreter.h>
+#include <LibWasm/Printer/Printer.h>
+#include <LibWasm/Wasi.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+namespace Wasm::Wasi::ABI {
+
+template<typename T>
+Wasm::Value CompatibleValue<T>::to_wasm_value() const
+{
+ return Wasm::Value(value);
+}
+
+template<typename T>
+T deserialize(CompatibleValue<T> const& data)
+{
+ return deserialize<T>(Array { ReadonlyBytes { &data.value, sizeof(data.value) } });
+}
+
+template<typename T, size_t N>
+void serialize(T const& value, Array<Bytes, N> bytes)
+{
+ if constexpr (IsEnum<T>)
+ return serialize(to_underlying(value), move(bytes));
+ else if constexpr (IsIntegral<T>)
+ ReadonlyBytes { &value, sizeof(value) }.copy_to(bytes[0]);
+ else if constexpr (IsSpecializationOf<T, DistinctNumeric>)
+ return serialize(value.value(), move(bytes));
+ else
+ return value.serialize_into(move(bytes));
+}
+
+template<typename T, size_t N>
+T deserialize(Array<ReadonlyBytes, N> const& bytes)
+{
+ if constexpr (IsEnum<T>) {
+ return static_cast<T>(deserialize<UnderlyingType<T>>(bytes));
+ } else if constexpr (IsIntegral<T>) {
+ T value;
+ ByteReader::load(bytes[0].data(), value);
+ return value;
+ } else if constexpr (IsSpecializationOf<T, DistinctNumeric>) {
+ return deserialize<RemoveCVReference<decltype(T(0).value())>>(bytes);
+ } else {
+ return T::read_from(bytes);
+ }
+}
+
+template<typename T>
+CompatibleValue<T> to_compatible_value(Wasm::Value const& value)
+{
+ using Type = typename ToCompatibleValue<T>::Type;
+ // Note: the type can't be something else, we've already checked before through the function type's runtime checker.
+ auto converted_value = *value.template to<Type>();
+ return { .value = converted_value };
+}
+
+}
+
+namespace Wasm::Wasi {
+
+void ArgsSizes::serialize_into(Array<Bytes, 2> bytes) const
+{
+ ABI::serialize(count, Array { bytes[0] });
+ ABI::serialize(size, Array { bytes[1] });
+}
+
+void EnvironSizes::serialize_into(Array<Bytes, 2> bytes) const
+{
+ ABI::serialize(count, Array { bytes[0] });
+ ABI::serialize(size, Array { bytes[1] });
+}
+
+void SockRecvResult::serialize_into(Array<Bytes, 2> bytes) const
+{
+ ABI::serialize(size, Array { bytes[0] });
+ ABI::serialize(roflags, Array { bytes[1] });
+}
+
+void ROFlags::serialize_into(Array<Bytes, 1> bytes) const
+{
+ ABI::serialize(data, Array { bytes[0] });
+}
+
+template<typename T>
+void LittleEndian<T>::serialize_into(Array<Bytes, 1> bytes) const
+{
+ ABI::serialize(m_value, move(bytes));
+}
+
+template<typename T>
+LittleEndian<T> LittleEndian<T>::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ auto swapped = ABI::deserialize<T>(bytes);
+ return bit_cast<LittleEndian<T>>(swapped);
+}
+
+Rights Rights::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ Rights rights { .data = 0 };
+ bytes[0].copy_to(rights.data.bytes());
+ return rights;
+}
+
+void Rights::serialize_into(Array<Bytes, 1> bytes) const
+{
+ data.bytes().copy_to(bytes[0]);
+}
+
+void FDFlags::serialize_into(Array<Bytes, 1> bytes) const
+{
+ ReadonlyBytes { &data, sizeof(data) }.copy_to(bytes[0]);
+}
+
+FDFlags FDFlags::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ FDFlags flags { .data = 0 };
+ bytes[0].copy_to(flags.data.bytes());
+ return flags;
+}
+
+FSTFlags FSTFlags::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ FSTFlags flags { .data = 0 };
+ bytes[0].copy_to(flags.data.bytes());
+ return flags;
+}
+
+OFlags OFlags::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ OFlags flags { .data = 0 };
+ bytes[0].copy_to(flags.data.bytes());
+ return flags;
+}
+
+SDFlags SDFlags::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ SDFlags flags { .data = 0 };
+ bytes[0].copy_to(flags.data.bytes());
+ return flags;
+}
+
+void FDStat::serialize_into(Array<Bytes, 1> bytes) const
+{
+ auto data = bytes[0];
+ ABI::serialize(fs_filetype, Array { data.slice(offsetof(FDStat, fs_filetype), sizeof(fs_filetype)) });
+ ABI::serialize(fs_flags, Array { data.slice(offsetof(FDStat, fs_flags), sizeof(fs_flags)) });
+ ABI::serialize(fs_rights_base, Array { data.slice(offsetof(FDStat, fs_rights_base), sizeof(fs_rights_base)) });
+ ABI::serialize(fs_rights_inheriting, Array { data.slice(offsetof(FDStat, fs_rights_inheriting), sizeof(fs_rights_inheriting)) });
+}
+
+void PreStat::serialize_into(Array<Bytes, 1> bytes) const
+{
+ auto data = bytes[0];
+ ABI::serialize(tag, Array { data.slice(0, sizeof(tag)) });
+ if (tag == 0)
+ ABI::serialize(dir, Array { data.slice(offsetof(PreStat, dir), sizeof(dir)) });
+ else
+ VERIFY_NOT_REACHED();
+}
+
+void PreStatDir::serialize_into(Array<Bytes, 1> bytes) const
+{
+ ABI::serialize(pr_name_len, move(bytes));
+}
+
+void FileStat::serialize_into(Array<Bytes, 1> bytes) const
+{
+ auto data = bytes[0];
+ ABI::serialize(dev, Array { data.slice(0, sizeof(dev)) });
+ ABI::serialize(ino, Array { data.slice(offsetof(FileStat, ino), sizeof(ino)) });
+ ABI::serialize(filetype, Array { data.slice(offsetof(FileStat, filetype), sizeof(filetype)) });
+ ABI::serialize(nlink, Array { data.slice(offsetof(FileStat, nlink), sizeof(nlink)) });
+ ABI::serialize(size, Array { data.slice(offsetof(FileStat, size), sizeof(size)) });
+ ABI::serialize(atim, Array { data.slice(offsetof(FileStat, atim), sizeof(atim)) });
+ ABI::serialize(mtim, Array { data.slice(offsetof(FileStat, mtim), sizeof(mtim)) });
+ ABI::serialize(ctim, Array { data.slice(offsetof(FileStat, ctim), sizeof(ctim)) });
+}
+
+RIFlags RIFlags::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ RIFlags flags { .data = 0 };
+ bytes[0].copy_to(flags.data.bytes());
+ return flags;
+}
+
+LookupFlags LookupFlags::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ LookupFlags flags { .data = 0 };
+ bytes[0].copy_to(flags.data.bytes());
+ return flags;
+}
+
+CIOVec CIOVec::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ return CIOVec {
+ .buf = ABI::deserialize<decltype(buf)>(Array { bytes[0].slice(offsetof(CIOVec, buf), sizeof(buf)) }),
+ .buf_len = ABI::deserialize<decltype(buf_len)>(Array { bytes[0].slice(offsetof(CIOVec, buf_len), sizeof(buf_len)) }),
+ };
+}
+
+IOVec IOVec::read_from(Array<ReadonlyBytes, 1> const& bytes)
+{
+ return IOVec {
+ .buf = ABI::deserialize<decltype(buf)>(Array { bytes[0].slice(offsetof(IOVec, buf), sizeof(buf)) }),
+ .buf_len = ABI::deserialize<decltype(buf_len)>(Array { bytes[0].slice(offsetof(IOVec, buf_len), sizeof(buf_len)) }),
+ };
+}
+
+template<typename T>
+ErrorOr<Vector<T>> copy_typed_array(Configuration& configuration, Pointer<T> source, Size count)
+{
+ Vector<T> values;
+ TRY(values.try_ensure_capacity(count));
+ auto* memory = configuration.store().get(MemoryAddress { 0 });
+ if (!memory)
+ return Error::from_errno(ENOMEM);
+
+ UnderlyingPointerType address = source.value();
+ auto size = sizeof(T);
+ if (memory->size() < address || memory->size() <= address + (size * count)) {
+ return Error::from_errno(ENOBUFS);
+ }
+
+ for (Size i = 0; i < count; i += 1) {
+ values.unchecked_append(T::read_from(Array { ReadonlyBytes { memory->data().bytes().slice(address, size) } }));
+ address += size;
+ }
+
+ return values;
+}
+
+template<typename T>
+ErrorOr<void> copy_typed_value_to(Configuration& configuration, T const& value, Pointer<T> destination)
+{
+ auto* memory = configuration.store().get(MemoryAddress { 0 });
+ if (!memory)
+ return Error::from_errno(ENOMEM);
+
+ UnderlyingPointerType address = destination.value();
+ auto size = sizeof(T);
+ if (memory->size() < address || memory->size() <= address + size) {
+ return Error::from_errno(ENOBUFS);
+ }
+
+ ABI::serialize(value, Array { Bytes { memory->data().bytes().slice(address, size) } });
+ return {};
+}
+
+template<typename T>
+ErrorOr<Span<T>> slice_typed_memory(Configuration& configuration, Pointer<T> source, Size count)
+{
+ auto* memory = configuration.store().get(MemoryAddress { 0 });
+ if (!memory)
+ return Error::from_errno(ENOMEM);
+
+ auto address = source.value();
+ auto size = sizeof(T);
+ if (memory->size() < address || memory->size() <= address + (size * count))
+ return Error::from_errno(ENOBUFS);
+
+ auto untyped_slice = memory->data().bytes().slice(address, size * count);
+ return Span<T>(untyped_slice.data(), count);
+}
+
+template<typename T>
+ErrorOr<Span<T const>> slice_typed_memory(Configuration& configuration, ConstPointer<T> source, Size count)
+{
+ auto* memory = configuration.store().get(MemoryAddress { 0 });
+ if (!memory)
+ return Error::from_errno(ENOMEM);
+
+ auto address = source.value();
+ auto size = sizeof(T);
+ if (memory->size() < address || memory->size() <= address + (size * count))
+ return Error::from_errno(ENOBUFS);
+
+ auto untyped_slice = memory->data().bytes().slice(address, size * count);
+ return Span<T const>(untyped_slice.data(), count);
+}
+
+static ErrorOr<size_t> copy_string_including_terminating_null(Configuration& configuration, StringView string, Pointer<u8> target)
+{
+ auto slice = TRY(slice_typed_memory(configuration, target, string.bytes().size() + 1));
+ string.bytes().copy_to(slice);
+ slice[string.bytes().size()] = 0;
+ return slice.size();
+}
+
+static ErrorOr<size_t> copy_string_excluding_terminating_null(Configuration& configuration, StringView string, Pointer<u8> target, Size target_length)
+{
+ auto byte_count = min(string.bytes().size(), target_length);
+ auto slice = TRY(slice_typed_memory(configuration, target, byte_count));
+ string.bytes().copy_trimmed_to(slice);
+ return byte_count;
+}
+
+static Errno errno_value_from_errno(int value);
+
+Vector<AK::String> const& Implementation::arguments() const
+{
+ if (!cache.cached_arguments.has_value()) {
+ cache.cached_arguments.lazy_emplace([&] {
+ if (provide_arguments)
+ return provide_arguments();
+ return Vector<AK::String> {};
+ });
+ }
+
+ return *cache.cached_arguments;
+}
+
+Vector<AK::String> const& Implementation::environment() const
+{
+ if (!cache.cached_environment.has_value()) {
+ cache.cached_environment.lazy_emplace([&] {
+ if (provide_environment)
+ return provide_environment();
+ return Vector<AK::String> {};
+ });
+ }
+
+ return *cache.cached_environment;
+}
+
+Vector<Implementation::MappedPath> const& Implementation::preopened_directories() const
+{
+ if (!cache.cached_preopened_directories.has_value()) {
+ cache.cached_preopened_directories.lazy_emplace([&] {
+ if (provide_preopened_directories)
+ return provide_preopened_directories();
+ return Vector<MappedPath> {};
+ });
+ }
+
+ return *cache.cached_preopened_directories;
+}
+
+Implementation::Descriptor Implementation::map_fd(FD fd)
+{
+ u32 fd_value = fd.value();
+ if (auto* value = m_fd_map.find(fd_value))
+ return value->downcast<Descriptor>();
+
+ return UnmappedDescriptor(fd_value);
+}
+
+ErrorOr<Result<void>> Implementation::impl$args_get(Configuration& configuration, Pointer<Pointer<u8>> argv, Pointer<u8> argv_buf)
+{
+ UnderlyingPointerType raw_argv_buffer = argv_buf.value();
+ UnderlyingPointerType raw_argv = argv.value();
+
+ for (auto& entry : arguments()) {
+ auto ptr = Pointer<u8> { raw_argv_buffer };
+ auto byte_count = TRY(copy_string_including_terminating_null(configuration, entry.bytes_as_string_view(), ptr));
+ raw_argv_buffer += byte_count;
+
+ TRY(copy_typed_value_to(configuration, ptr, Pointer<Pointer<u8>> { raw_argv }));
+ raw_argv += sizeof(ptr);
+ }
+
+ return Result<void> {};
+}
+
+ErrorOr<Result<ArgsSizes>> Implementation::impl$args_sizes_get(Configuration&)
+{
+ size_t count = 0;
+ size_t total_size = 0;
+ for (auto& entry : arguments()) {
+ count += 1;
+ total_size += entry.bytes().size() + 1; // 1 extra byte for terminating null.
+ }
+
+ return Result<ArgsSizes>(ArgsSizes {
+ count,
+ total_size,
+ });
+}
+
+ErrorOr<Result<void>> Implementation::impl$environ_get(Configuration& configuration, Pointer<Pointer<u8>> environ, Pointer<u8> environ_buf)
+{
+ UnderlyingPointerType raw_environ_buffer = environ_buf.value();
+ UnderlyingPointerType raw_environ = environ.value();
+
+ for (auto& entry : environment()) {
+ auto ptr = Pointer<u8> { raw_environ_buffer };
+ auto byte_count = TRY(copy_string_including_terminating_null(configuration, entry.bytes_as_string_view(), ptr));
+ raw_environ_buffer += byte_count;
+
+ TRY(copy_typed_value_to(configuration, ptr, Pointer<Pointer<u8>> { raw_environ }));
+ raw_environ += sizeof(ptr);
+ }
+
+ return Result<void> {};
+}
+
+ErrorOr<Result<EnvironSizes>> Implementation::impl$environ_sizes_get(Configuration&)
+{
+ size_t count = 0;
+ size_t total_size = 0;
+ for (auto& entry : environment()) {
+ count += 1;
+ total_size += entry.bytes().size() + 1; // 1 extra byte for terminating null.
+ }
+
+ return Result<EnvironSizes>(EnvironSizes {
+ count,
+ total_size,
+ });
+}
+
+ErrorOr<Result<void>> Implementation::impl$proc_exit(Configuration&, ExitCode exit_code)
+{
+ exit(exit_code);
+}
+
+ErrorOr<Result<void>> Implementation::impl$fd_close(Configuration&, FD fd)
+{
+ return map_fd(fd).visit(
+ [&](u32 fd) -> Result<void> {
+ if (close(bit_cast<i32>(fd)) != 0)
+ return errno_value_from_errno(errno);
+ return {};
+ },
+ [&](PreopenedDirectoryDescriptor) -> Result<void> {
+ return errno_value_from_errno(EISDIR);
+ },
+ [&](UnmappedDescriptor) -> Result<void> {
+ return errno_value_from_errno(EBADF);
+ });
+}
+
+ErrorOr<Result<Size>> Implementation::impl$fd_write(Configuration& configuration, FD fd, Pointer<CIOVec> iovs, Size iovs_len)
+{
+ auto mapped_fd = map_fd(fd);
+ if (!mapped_fd.has<u32>())
+ return errno_value_from_errno(EBADF);
+
+ u32 fd_value = mapped_fd.get<u32>();
+ Size bytes_written = 0;
+ for (auto& iovec : TRY(copy_typed_array(configuration, iovs, iovs_len))) {
+ auto slice = TRY(slice_typed_memory(configuration, iovec.buf, iovec.buf_len));
+ auto result = write(fd_value, slice.data(), slice.size());
+ if (result < 0)
+ return errno_value_from_errno(errno);
+ bytes_written += static_cast<Size>(result);
+ }
+ return bytes_written;
+}
+
+ErrorOr<Result<PreStat>> Implementation::impl$fd_prestat_get(Configuration&, FD fd)
+{
+ auto& paths = preopened_directories();
+ return map_fd(fd).visit(
+ [&](UnmappedDescriptor unmapped_fd) -> Result<PreStat> {
+ // Map the new fd to the next available directory.
+ if (m_first_unmapped_preopened_directory_index >= paths.size())
+ return errno_value_from_errno(EBADF);
+
+ auto index = m_first_unmapped_preopened_directory_index++;
+ m_fd_map.insert(unmapped_fd.value(), PreopenedDirectoryDescriptor(index));
+ return PreStat {
+ .tag = 0,
+ .dir = PreStatDir {
+ .pr_name_len = paths[index].mapped_path.string().bytes().size(),
+ },
+ };
+ },
+ [&](u32) -> Result<PreStat> {
+ return errno_value_from_errno(EBADF);
+ },
+ [&](PreopenedDirectoryDescriptor fd) -> Result<PreStat> {
+ return PreStat {
+ .tag = 0,
+ .dir = PreStatDir {
+ .pr_name_len = paths[fd.value()].mapped_path.string().bytes().size(),
+ },
+ };
+ });
+}
+
+ErrorOr<Result<void>> Implementation::impl$fd_prestat_dir_name(Configuration& configuration, FD fd, Pointer<u8> path, Size path_len)
+{
+ auto mapped_fd = map_fd(fd);
+ if (!mapped_fd.has<PreopenedDirectoryDescriptor>())
+ return errno_value_from_errno(EBADF);
+
+ auto& entry = preopened_directories()[mapped_fd.get<PreopenedDirectoryDescriptor>().value()];
+ auto byte_count = TRY(copy_string_excluding_terminating_null(configuration, entry.mapped_path.string().view(), path, path_len));
+ if (byte_count < path_len.value())
+ return errno_value_from_errno(ENOBUFS);
+
+ return Result<void> {};
+}
+
+ErrorOr<Result<FileStat>> Implementation::impl$path_filestat_get(Configuration& configuration, FD fd, LookupFlags flags, ConstPointer<u8> path, Size path_len)
+{
+ int dir_fd = AT_FDCWD;
+
+ auto mapped_fd = map_fd(fd);
+ if (mapped_fd.has<PreopenedDirectoryDescriptor>()) {
+ auto& entry = preopened_directories()[mapped_fd.get<PreopenedDirectoryDescriptor>().value()];
+ dir_fd = entry.opened_fd.value_or_lazy_evaluated([&] {
+ DeprecatedString path = entry.host_path.string();
+ return open(path.characters(), O_DIRECTORY, 0755);
+ });
+ entry.opened_fd = dir_fd;
+ }
+
+ if (dir_fd < 0 && dir_fd != AT_FDCWD)
+ return errno_value_from_errno(errno);
+
+ int options = 0;
+ if (!flags.bits.symlink_follow)
+ options |= AT_SYMLINK_NOFOLLOW;
+
+ auto slice = TRY(slice_typed_memory(configuration, path, path_len));
+ auto null_terminated_string = DeprecatedString::copy(slice);
+
+ struct stat stat_buf;
+ if (fstatat(dir_fd, null_terminated_string.characters(), &stat_buf, options) < 0)
+ return errno_value_from_errno(errno);
+
+ constexpr auto file_type_of = [](struct stat const& buf) {
+ if (S_ISDIR(buf.st_mode))
+ return FileType::Directory;
+ if (S_ISCHR(buf.st_mode))
+ return FileType::CharacterDevice;
+ if (S_ISBLK(buf.st_mode))
+ return FileType::BlockDevice;
+ if (S_ISREG(buf.st_mode))
+ return FileType::RegularFile;
+ if (S_ISFIFO(buf.st_mode))
+ return FileType::Unknown; // no Pipe? :yakfused:
+ if (S_ISLNK(buf.st_mode))
+ return FileType::SymbolicLink;
+ if (S_ISSOCK(buf.st_mode))
+ return FileType::SocketDGram; // :shrug:
+ return FileType::Unknown;
+ };
+
+ return Result(FileStat {
+ .dev = stat_buf.st_dev,
+ .ino = stat_buf.st_ino,
+ .filetype = file_type_of(stat_buf),
+ .nlink = stat_buf.st_nlink,
+ .size = stat_buf.st_size,
+ .atim = stat_buf.st_atime,
+ .mtim = stat_buf.st_mtime,
+ .ctim = stat_buf.st_ctime,
+ });
+}
+
+ErrorOr<Result<void>> Implementation::impl$path_create_directory(Configuration& configuration, FD fd, Pointer<u8> path, Size path_len)
+{
+ int dir_fd = AT_FDCWD;
+
+ auto mapped_fd = map_fd(fd);
+ if (mapped_fd.has<PreopenedDirectoryDescriptor>()) {
+ auto& entry = preopened_directories()[mapped_fd.get<PreopenedDirectoryDescriptor>().value()];
+ dir_fd = entry.opened_fd.value_or_lazy_evaluated([&] {
+ DeprecatedString path = entry.host_path.string();
+ return open(path.characters(), O_DIRECTORY, 0755);
+ });
+ entry.opened_fd = dir_fd;
+ }
+
+ if (dir_fd < 0 && dir_fd != AT_FDCWD)
+ return errno_value_from_errno(errno);
+
+ auto slice = TRY(slice_typed_memory(configuration, path, path_len));
+ auto null_terminated_string = DeprecatedString::copy(slice);
+
+ if (mkdirat(dir_fd, null_terminated_string.characters(), 0755) < 0)
+ return errno_value_from_errno(errno);
+
+ return Result<void> {};
+}
+
+ErrorOr<Result<FD>> Implementation::impl$path_open(Configuration& configuration, FD fd, LookupFlags lookup_flags, Pointer<u8> path, Size path_len, OFlags o_flags, Rights, Rights, FDFlags fd_flags)
+{
+ int dir_fd = AT_FDCWD;
+
+ auto mapped_fd = map_fd(fd);
+ if (mapped_fd.has<PreopenedDirectoryDescriptor>()) {
+ auto& entry = preopened_directories()[mapped_fd.get<PreopenedDirectoryDescriptor>().value()];
+ dir_fd = entry.opened_fd.value_or_lazy_evaluated([&] {
+ DeprecatedString path = entry.host_path.string();
+ return open(path.characters(), O_DIRECTORY, 0755);
+ });
+ entry.opened_fd = dir_fd;
+ }
+
+ if (dir_fd < 0 && dir_fd != AT_FDCWD)
+ return errno_value_from_errno(errno);
+
+ // FIXME: What should we do with dsync/rsync?
+
+ int open_flags = 0;
+ if (fd_flags.bits.append)
+ open_flags |= O_APPEND;
+ if (fd_flags.bits.nonblock)
+ open_flags |= O_NONBLOCK;
+ if (fd_flags.bits.sync)
+ open_flags |= O_SYNC;
+
+ if (o_flags.bits.trunc)
+ open_flags |= O_TRUNC;
+ if (o_flags.bits.creat)
+ open_flags |= O_CREAT;
+ if (o_flags.bits.directory)
+ open_flags |= O_DIRECTORY;
+ if (o_flags.bits.excl)
+ open_flags |= O_EXCL;
+
+ if (!lookup_flags.bits.symlink_follow)
+ open_flags |= O_NOFOLLOW;
+
+ auto path_data = TRY(slice_typed_memory(configuration, path, path_len));
+ auto path_string = DeprecatedString::copy(path_data);
+
+ int opened_fd = openat(dir_fd, path_string.characters(), open_flags, 0644);
+ if (opened_fd < 0)
+ return errno_value_from_errno(errno);
+
+ // FIXME: Implement Rights and RightsInheriting.
+
+ return FD(opened_fd);
+}
+
+ErrorOr<Result<Timestamp>> Implementation::impl$clock_time_get(Configuration&, ClockID id, Timestamp precision)
+{
+ constexpr u64 nanoseconds_in_millisecond = 1000'000ull;
+ constexpr u64 nanoseconds_in_second = 1000'000'000ull;
+
+ clockid_t clock_id;
+ switch (id) {
+ case ClockID::Realtime:
+ if (precision >= nanoseconds_in_millisecond)
+ clock_id = CLOCK_REALTIME_COARSE;
+ else
+ clock_id = CLOCK_REALTIME;
+ break;
+ case ClockID::Monotonic:
+ if (precision >= nanoseconds_in_millisecond)
+ clock_id = CLOCK_MONOTONIC_COARSE;
+ else
+ clock_id = CLOCK_MONOTONIC;
+ break;
+ case ClockID::ProcessCPUTimeID:
+ case ClockID::ThreadCPUTimeID:
+ return Errno::NoSys;
+ break;
+ }
+
+ struct timespec ts;
+ if (clock_gettime(clock_id, &ts) < 0)
+ return errno_value_from_errno(errno);
+
+ return Result<Timestamp> { static_cast<u64>(ts.tv_sec) * nanoseconds_in_second + static_cast<u64>(ts.tv_nsec) };
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+ErrorOr<Result<Timestamp>> Implementation::impl$clock_res_get(Configuration&, ClockID id)
+{
+ return Errno::NoSys;
+}
+ErrorOr<Result<void>> Implementation::impl$fd_advise(Configuration&, FD, FileSize offset, FileSize len, Advice) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$fd_allocate(Configuration&, FD, FileSize offset, FileSize len) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$fd_datasync(Configuration&, FD) { return Errno::NoSys; }
+ErrorOr<Result<FDStat>> Implementation::impl$fd_fdstat_get(Configuration&, FD) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$fd_fdstat_set_flags(Configuration&, FD, FDFlags) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$fd_fdstat_set_rights(Configuration&, FD, Rights fs_rights_base, Rights fs_rights_inheriting) { return Errno::NoSys; }
+ErrorOr<Result<FileStat>> Implementation::impl$fd_filestat_get(Configuration&, FD) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$fd_filestat_set_size(Configuration&, FD, FileSize) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$fd_filestat_set_times(Configuration&, FD, Timestamp atim, Timestamp mtim, FSTFlags) { return Errno::NoSys; }
+ErrorOr<Result<Size>> Implementation::impl$fd_pread(Configuration&, FD, Pointer<IOVec> iovs, Size iovs_len, FileSize offset) { return Errno::NoSys; }
+ErrorOr<Result<Size>> Implementation::impl$fd_pwrite(Configuration&, FD, Pointer<CIOVec> iovs, Size iovs_len, FileSize offset) { return Errno::NoSys; }
+ErrorOr<Result<Size>> Implementation::impl$fd_read(Configuration&, FD, Pointer<IOVec> iovs, Size iovs_len) { return Errno::NoSys; }
+ErrorOr<Result<Size>> Implementation::impl$fd_readdir(Configuration&, FD, Pointer<u8> buf, Size buf_len, DirCookie cookie) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$fd_renumber(Configuration&, FD from, FD to) { return Errno::NoSys; }
+ErrorOr<Result<FileSize>> Implementation::impl$fd_seek(Configuration&, FD, FileDelta offset, Whence whence) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$fd_sync(Configuration&, FD) { return Errno::NoSys; }
+ErrorOr<Result<FileSize>> Implementation::impl$fd_tell(Configuration&, FD) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$path_filestat_set_times(Configuration&, FD, LookupFlags, Pointer<u8> path, Size path_len, Timestamp atim, Timestamp mtim, FSTFlags) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$path_link(Configuration&, FD, LookupFlags, Pointer<u8> old_path, Size old_path_len, FD, Pointer<u8> new_path, Size new_path_len) { return Errno::NoSys; }
+ErrorOr<Result<Size>> Implementation::impl$path_readlink(Configuration&, FD, LookupFlags, Pointer<u8> path, Size path_len, Pointer<u8> buf, Size buf_len) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$path_remove_directory(Configuration&, FD, Pointer<u8> path, Size path_len) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$path_rename(Configuration&, FD, Pointer<u8> old_path, Size old_path_len, FD, Pointer<u8> new_path, Size new_path_len) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$path_symlink(Configuration&, Pointer<u8> old_path, Size old_path_len, FD, Pointer<u8> new_path, Size new_path_len) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$path_unlink_file(Configuration&, FD, Pointer<u8> path, Size path_len) { return Errno::NoSys; }
+ErrorOr<Result<Size>> Implementation::impl$poll_oneoff(Configuration&, ConstPointer<Subscription> in, Pointer<Event> out, Size nsubscriptions) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$proc_raise(Configuration&, Signal) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$sched_yield(Configuration&) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$random_get(Configuration&, Pointer<u8> buf, Size buf_len) { return Errno::NoSys; }
+ErrorOr<Result<FD>> Implementation::impl$sock_accept(Configuration&, FD fd, FDFlags fd_flags) { return Errno::NoSys; }
+ErrorOr<Result<SockRecvResult>> Implementation::impl$sock_recv(Configuration&, FD fd, Pointer<IOVec> ri_data, Size ri_data_len, RIFlags ri_flags) { return Errno::NoSys; }
+ErrorOr<Result<Size>> Implementation::impl$sock_send(Configuration&, FD fd, Pointer<CIOVec> si_data, Size si_data_len, SIFlags si_flags) { return Errno::NoSys; }
+ErrorOr<Result<void>> Implementation::impl$sock_shutdown(Configuration&, FD fd, SDFlags how) { return Errno::NoSys; }
+
+#pragma GCC diagnostic pop
+
+template<size_t N>
+static Array<Bytes, N> address_spans(Span<Value> values, Configuration& configuration)
+{
+ Array<Bytes, N> result;
+ auto memory = configuration.store().get(MemoryAddress { 0 })->data().span();
+ for (size_t i = 0; i < N; ++i)
+ result[i] = memory.slice(*values[i].to<i32>());
+ return result;
+}
+
+#define ENUMERATE_FUNCTION_NAMES(M) \
+ M(args_get) \
+ M(args_sizes_get) \
+ M(environ_get) \
+ M(environ_sizes_get) \
+ M(clock_res_get) \
+ M(clock_time_get) \
+ M(fd_advise) \
+ M(fd_allocate) \
+ M(fd_close) \
+ M(fd_datasync) \
+ M(fd_fdstat_get) \
+ M(fd_fdstat_set_flags) \
+ M(fd_fdstat_set_rights) \
+ M(fd_filestat_get) \
+ M(fd_filestat_set_size) \
+ M(fd_filestat_set_times) \
+ M(fd_pread) \
+ M(fd_prestat_get) \
+ M(fd_prestat_dir_name) \
+ M(fd_pwrite) \
+ M(fd_read) \
+ M(fd_readdir) \
+ M(fd_renumber) \
+ M(fd_seek) \
+ M(fd_sync) \
+ M(fd_tell) \
+ M(fd_write) \
+ M(path_create_directory) \
+ M(path_filestat_get) \
+ M(path_filestat_set_times) \
+ M(path_link) \
+ M(path_open) \
+ M(path_readlink) \
+ M(path_remove_directory) \
+ M(path_rename) \
+ M(path_symlink) \
+ M(path_unlink_file) \
+ M(poll_oneoff) \
+ M(proc_exit) \
+ M(proc_raise) \
+ M(sched_yield) \
+ M(random_get) \
+ M(sock_accept) \
+ M(sock_recv) \
+ M(sock_send) \
+ M(sock_shutdown)
+
+struct Names {
+#define NAME(x) FlyString x;
+ ENUMERATE_FUNCTION_NAMES(NAME)
+#undef NAME
+
+ static ErrorOr<Names> construct()
+ {
+ return Names {
+#define NAME(x) .x = TRY(FlyString::from_utf8(#x##sv)),
+ ENUMERATE_FUNCTION_NAMES(NAME)
+#undef NAME
+ };
+ }
+};
+
+ErrorOr<HostFunction> Implementation::function_by_name(StringView name)
+{
+ auto name_for_comparison = TRY(FlyString::from_utf8(name));
+ static auto names = TRY(Names::construct());
+
+#define IMPL(x) \
+ if (name_for_comparison == names.x) \
+ return invocation_of<&Implementation::impl$##x>(#x##sv);
+
+ ENUMERATE_FUNCTION_NAMES(IMPL)
+
+#undef IMPL
+
+ return Error::from_string_literal("No such host function");
+}
+
+namespace ABI {
+
+template<typename T>
+auto CompatibleValueType = IsOneOf<T, i8, i16, i32>
+ ? Wasm::ValueType(Wasm::ValueType::I32)
+ : Wasm::ValueType(Wasm::ValueType::I64);
+
+template<typename R, typename... Args, ErrorOr<Result<R>> (Implementation::*impl)(Configuration&, Args...)>
+struct InvocationOf<impl> {
+ HostFunction operator()(Implementation& self, StringView function_name)
+ {
+ Vector<ValueType> arguments_types { CompatibleValueType<typename ABI::ToCompatibleValue<Args>::Type>... };
+ if constexpr (!IsVoid<R>) {
+ if constexpr (requires { declval<typename R::SerializationComponents>(); }) {
+ for_each_type<typename R::SerializationComponents>([&]<typename T>(TypeWrapper<T>) {
+ arguments_types.append(CompatibleValueType<typename ABI::ToCompatibleValue<Pointer<T>>::Type>);
+ });
+ } else {
+ arguments_types.append(CompatibleValueType<typename ABI::ToCompatibleValue<Pointer<R>>::Type>);
+ }
+ }
+
+ return HostFunction(
+ [&self, function_name](Configuration& configuration, Vector<Value>& arguments) -> Wasm::Result {
+ Tuple args = [&]<typename... Ts, auto... Is>(IndexSequence<Is...>)
+ {
+ return Tuple { ABI::deserialize(ABI::to_compatible_value<Ts>(arguments[Is]))... };
+ }
+ .template operator()<Args...>(MakeIndexSequence<sizeof...(Args)>());
+
+ auto result = args.apply_as_args([&](auto&&... impl_args) { return (self.*impl)(configuration, impl_args...); });
+ dbgln_if(WASI_DEBUG, "WASI: Called {}", function_name);
+
+ if (result.is_error())
+ return Wasm::Trap { DeprecatedString::formatted("Invalid call to {}() = {}", function_name, result.error()) };
+
+ auto value = result.release_value();
+ if (value.is_error())
+ return Wasm::Result { Vector { Value { ValueType(ValueType::I32), static_cast<u64>(to_underlying(value.error().value())) } } };
+
+ if constexpr (!IsVoid<R>) {
+ // Return values are passed as pointers, after the arguments
+ if constexpr (requires { &R::serialize_into; }) {
+ constexpr auto ResultCount = []<auto N>(void(R::*)(Array<Bytes, N>) const) { return N; }
+ (&R::serialize_into);
+ ABI::serialize(*value.result(), address_spans<ResultCount>(arguments.span().slice(sizeof...(Args)), configuration));
+ } else {
+ ABI::serialize(*value.result(), address_spans<1>(arguments.span().slice(sizeof...(Args)), configuration));
+ }
+ }
+ // Return value is errno, we have nothing to return.
+ return Wasm::Result { Vector<Value> { Value(ValueType(ValueType::I32), 0ull) } };
+ },
+ FunctionType {
+ move(arguments_types),
+ { ValueType(ValueType::I32) },
+ });
+ }
+};
+
+};
+
+Errno errno_value_from_errno(int value)
+{
+ switch (value) {
+#ifdef ESUCCESS
+ case ESUCCESS:
+ return Errno::Success;
+#endif
+ case E2BIG:
+ return Errno::TooBig;
+ case EACCES:
+ return Errno::Access;
+ case EADDRINUSE:
+ return Errno::AddressInUse;
+ case EADDRNOTAVAIL:
+ return Errno::AddressNotAvailable;
+ case EAFNOSUPPORT:
+ return Errno::AFNotSupported;
+ case EAGAIN:
+ return Errno::Again;
+ case EALREADY:
+ return Errno::Already;
+ case EBADF:
+ return Errno::BadF;
+ case EBUSY:
+ return Errno::Busy;
+ case ECANCELED:
+ return Errno::Canceled;
+ case ECHILD:
+ return Errno::Child;
+ case ECONNABORTED:
+ return Errno::ConnectionAborted;
+ case ECONNREFUSED:
+ return Errno::ConnectionRefused;
+ case ECONNRESET:
+ return Errno::ConnectionReset;
+ case EDEADLK:
+ return Errno::Deadlock;
+ case EDESTADDRREQ:
+ return Errno::DestinationAddressRequired;
+ case EDOM:
+ return Errno::Domain;
+ case EEXIST:
+ return Errno::Exist;
+ case EFAULT:
+ return Errno::Fault;
+ case EFBIG:
+ return Errno::FBig;
+ case EHOSTUNREACH:
+ return Errno::HostUnreachable;
+ case EILSEQ:
+ return Errno::IllegalSequence;
+ case EINPROGRESS:
+ return Errno::InProgress;
+ case EINTR:
+ return Errno::Interrupted;
+ case EINVAL:
+ return Errno::Invalid;
+ case EIO:
+ return Errno::IO;
+ case EISCONN:
+ return Errno::IsConnected;
+ case EISDIR:
+ return Errno::IsDirectory;
+ case ELOOP:
+ return Errno::Loop;
+ case EMFILE:
+ return Errno::MFile;
+ case EMLINK:
+ return Errno::MLink;
+ case EMSGSIZE:
+ return Errno::MessageSize;
+ case ENAMETOOLONG:
+ return Errno::NameTooLong;
+ case ENETDOWN:
+ return Errno::NetworkDown;
+ case ENETRESET:
+ return Errno::NetworkReset;
+ case ENETUNREACH:
+ return Errno::NetworkUnreachable;
+ case ENFILE:
+ return Errno::NFile;
+ case ENOBUFS:
+ return Errno::NoBufferSpace;
+ case ENODEV:
+ return Errno::NoDevice;
+ case ENOENT:
+ return Errno::NoEntry;
+ case ENOEXEC:
+ return Errno::NoExec;
+ case ENOLCK:
+ return Errno::NoLock;
+ case ENOMEM:
+ return Errno::NoMemory;
+ case ENOPROTOOPT:
+ return Errno::NoProtocolOption;
+ case ENOSPC:
+ return Errno::NoSpace;
+ case ENOSYS:
+ return Errno::NoSys;
+ case ENOTCONN:
+ return Errno::NotConnected;
+ case ENOTDIR:
+ return Errno::NotDirectory;
+ case ENOTEMPTY:
+ return Errno::NotEmpty;
+ case ENOTRECOVERABLE:
+ return Errno::NotRecoverable;
+ case ENOTSOCK:
+ return Errno::NotSocket;
+ case ENOTSUP:
+ return Errno::NotSupported;
+ case ENOTTY:
+ return Errno::NoTTY;
+ case ENXIO:
+ return Errno::NXIO;
+ case EOVERFLOW:
+ return Errno::Overflow;
+ case EPERM:
+ return Errno::Permission;
+ case EPIPE:
+ return Errno::Pipe;
+ case EPROTO:
+ return Errno::Protocol;
+ case EPROTONOSUPPORT:
+ return Errno::ProtocolNotSupported;
+ case EPROTOTYPE:
+ return Errno::ProtocolType;
+ case ERANGE:
+ return Errno::Range;
+ case ESPIPE:
+ return Errno::SPipe;
+ case ESRCH:
+ return Errno::SRCH;
+ case ESTALE:
+ return Errno::Stale;
+ case ETIMEDOUT:
+ return Errno::TimedOut;
+ case ETXTBSY:
+ return Errno::TextBusy;
+ case EXDEV:
+ return Errno::XDev;
+ default:
+ return Errno::Invalid;
+ }
+}
+}
diff --git a/Userland/Libraries/LibWasm/Wasi.h b/Userland/Libraries/LibWasm/Wasi.h
new file mode 100644
index 0000000000..0dedce568d
--- /dev/null
+++ b/Userland/Libraries/LibWasm/Wasi.h
@@ -0,0 +1,943 @@
+/*
+ * Copyright (c) 2023, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/DistinctNumeric.h>
+#include <AK/Endian.h>
+#include <AK/Function.h>
+#include <AK/LexicalPath.h>
+#include <AK/RedBlackTree.h>
+#include <AK/String.h>
+#include <AK/Vector.h>
+#include <LibWasm/AbstractMachine/AbstractMachine.h>
+#include <LibWasm/Forward.h>
+
+namespace Wasm::Wasi::ABI {
+
+// NOTE: The "real" ABI used in the wild is described by [api.h from libc-bottom-half](https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h)
+// This is *not* the same ABI as the one described in the WASI spec, nor is it the same ABI as api.h on wasi-libc/master.
+// The highlights of the ABI are:
+// - (most) structs are passed as pointers to heap.
+// - arrays are fat pointers splat across two arguments
+// - return object locations are also passed as arguments, the number of arguments depends on the return type itself:
+// - ArgsSizes / EnvironSizes / the return type of sock_recv use two arguments
+// - everything else is passed like a normal struct
+
+template<auto impl>
+struct InvocationOf {
+ HostFunction operator()(Implementation&, StringView name);
+};
+
+template<typename T, size_t N>
+void serialize(T const&, Array<Bytes, N>);
+
+template<typename T, size_t N>
+T deserialize(Array<ReadonlyBytes, N> const&);
+
+template<typename T>
+struct ToCompatibleValue {
+ using Type = void;
+};
+
+template<typename T>
+struct CompatibleValue {
+ typename ToCompatibleValue<T>::Type value;
+
+ Wasm::Value to_wasm_value() const;
+};
+
+template<typename T>
+CompatibleValue<T> to_compatible_value(Wasm::Value const&);
+
+template<typename T>
+T deserialize(CompatibleValue<T> const&);
+
+}
+
+namespace Wasm::Wasi {
+
+// NOTE: This is a copy of LittleEndian from Endian.h,
+// we can't use those because they have a packed attribute, and depend on it;
+// but we want proper alignment on these types.
+template<typename T>
+class alignas(T) LittleEndian {
+public:
+ constexpr LittleEndian() = default;
+
+ constexpr LittleEndian(T value)
+ : m_value(AK::convert_between_host_and_little_endian(value))
+ {
+ }
+
+ constexpr operator T() const { return AK::convert_between_host_and_little_endian(m_value); }
+ constexpr T value() const { return AK::convert_between_host_and_little_endian(m_value); }
+
+ LittleEndian& operator+=(T other)
+ {
+ m_value = AK::convert_between_host_and_little_endian(AK::convert_between_host_and_little_endian(m_value) + other);
+ return *this;
+ }
+
+ // This returns the internal representation. In this case, that is the value stored in little endian format.
+ constexpr Bytes bytes() { return Bytes { &m_value, sizeof(m_value) }; }
+ constexpr ReadonlyBytes bytes() const { return ReadonlyBytes { &m_value, sizeof(m_value) }; }
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static LittleEndian read_from(Array<ReadonlyBytes, 1> const& bytes);
+
+private:
+ T m_value { 0 };
+};
+
+using Size = LittleEndian<u32>;
+using FileSize = LittleEndian<u64>;
+using Timestamp = LittleEndian<u64>;
+
+namespace Detail {
+template<typename>
+struct __Pointer_tag;
+template<typename>
+struct __ConstPointer_tag;
+}
+
+// NOTE: Might need to be updated if WASI ever supports memory64.
+using UnderlyingPointerType = u32;
+
+template<typename T>
+using Pointer = DistinctNumeric<LittleEndian<UnderlyingPointerType>, Detail::__Pointer_tag<T>, AK::DistinctNumericFeature::Comparison>;
+template<typename T>
+using ConstPointer = DistinctNumeric<LittleEndian<UnderlyingPointerType>, Detail::__ConstPointer_tag<T>, AK::DistinctNumericFeature::Comparison>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L70
+enum class ClockID : u32 {
+ Realtime,
+ Monotonic,
+ ProcessCPUTimeID,
+ ThreadCPUTimeID,
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L105
+enum class Errno : u16 {
+ Success,
+ TooBig,
+ Access,
+ AddressInUse,
+ AddressNotAvailable,
+ AFNotSupported,
+ Again,
+ Already,
+ BadF,
+ BadMessage,
+ Busy,
+ Canceled,
+ Child,
+ ConnectionAborted,
+ ConnectionRefused,
+ ConnectionReset,
+ Deadlock,
+ DestinationAddressRequired,
+ Domain,
+ DQuot, // Reserved, Unused.
+ Exist,
+ Fault,
+ FBig,
+ HostUnreachable,
+ IdentifierRemoved,
+ IllegalSequence,
+ InProgress,
+ Interrupted,
+ Invalid,
+ IO,
+ IsConnected,
+ IsDirectory,
+ Loop,
+ MFile,
+ MLink,
+ MessageSize,
+ MultiHop, // Reserved, Unused.
+ NameTooLong,
+ NetworkDown,
+ NetworkReset,
+ NetworkUnreachable,
+ NFile,
+ NoBufferSpace,
+ NoDevice,
+ NoEntry,
+ NoExec,
+ NoLock,
+ NoLink,
+ NoMemory,
+ NoMessage,
+ NoProtocolOption,
+ NoSpace,
+ NoSys,
+ NotConnected,
+ NotDirectory,
+ NotEmpty,
+ NotRecoverable,
+ NotSocket,
+ NotSupported,
+ NoTTY,
+ NXIO,
+ Overflow,
+ OwnerDead,
+ Permission,
+ Pipe,
+ Protocol,
+ ProtocolNotSupported,
+ ProtocolType,
+ Range,
+ ReadOnlyFS,
+ SPipe,
+ SRCH,
+ Stale,
+ TimedOut,
+ TextBusy,
+ XDev,
+ NotCapable,
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L498
+struct Rights {
+ using CompatibleType = u64;
+
+ struct Bits {
+ bool fd_datasync : 1;
+ bool fd_read : 1;
+ bool fd_seek : 1;
+ bool fd_fdstat_set_flags : 1;
+ bool fd_sync : 1;
+ bool fd_tell : 1;
+ bool fd_write : 1;
+ bool fd_advise : 1;
+ bool fd_allocate : 1;
+ bool path_create_directory : 1;
+ bool path_create_file : 1;
+ bool path_link_source : 1;
+ bool path_link_target : 1;
+ bool path_open : 1;
+ bool fd_readdir : 1;
+ bool path_readlink : 1;
+ bool path_rename_source : 1;
+ bool path_rename_target : 1;
+ bool path_filestat_get : 1;
+ bool path_filestat_set_size : 1;
+ bool path_filestat_set_times : 1;
+ bool fd_filestat_get : 1;
+ bool fd_filestat_set_size : 1;
+ bool fd_filestat_set_times : 1;
+ bool path_symlink : 1;
+ bool path_remove_directory : 1;
+ bool path_unlink_file : 1;
+ bool poll_fd_readwrite : 1;
+ bool sock_shutdown : 1;
+ bool sock_accept : 1;
+
+ u64 _unused : 34;
+ };
+
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static Rights read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L663
+using FD = DistinctNumeric<LittleEndian<u32>, struct __FD_tag, AK::DistinctNumericFeature::Comparison>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L671
+struct IOVec {
+ Pointer<u8> buf;
+ Size buf_len;
+
+ static IOVec read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L692
+struct CIOVec {
+ ConstPointer<u8> buf;
+ Size buf_len;
+
+ static CIOVec read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L713
+using FileDelta = LittleEndian<i64>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L721
+enum class Whence : u8 {
+ Set,
+ Cur,
+ End,
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L746
+using DirCookie = LittleEndian<u64>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L754
+using DirNameLen = LittleEndian<u32>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L762
+using INode = LittleEndian<u64>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L770
+enum class FileType : u8 {
+ Unknown,
+ BlockDevice,
+ CharacterDevice,
+ Directory,
+ RegularFile,
+ SocketDGram,
+ SocketStream,
+ SymbolicLink,
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L818
+struct DirEnt {
+ DirCookie d_next;
+ INode d_ino;
+ DirNameLen d_namlen;
+ FileType d_type;
+ u8 _padding[3] { 0 }; // Not part of the API, but the struct is required to be 24 bytes - even though it has no explicit padding.
+};
+static_assert(sizeof(DirEnt) == 24);
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L851
+enum class Advice : u8 {
+ Normal,
+ Sequential,
+ Random,
+ WillNeed,
+ DontNeed,
+ NoReuse,
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L889
+struct FDFlags {
+ using CompatibleType = u16;
+
+ struct Bits {
+ bool append : 1;
+ bool dsync : 1;
+ bool nonblock : 1;
+ bool rsync : 1;
+ bool sync : 1;
+ u16 _unused : 11;
+ };
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static FDFlags read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L924
+struct FDStat {
+ FileType fs_filetype;
+ FDFlags fs_flags;
+ Rights fs_rights_base;
+ Rights fs_rights_inheriting;
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+};
+static_assert(sizeof(FDStat) == 24);
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L959
+using Device = LittleEndian<u64>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L967
+struct FSTFlags {
+ using CompatibleType = u16;
+
+ struct Bits {
+ bool atim : 1;
+ bool atim_now : 1;
+ bool mtim : 1;
+ bool mtim_now : 1;
+ u16 _unused : 12;
+ };
+
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static FSTFlags read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+static_assert(sizeof(FSTFlags) == 2);
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L995
+struct LookupFlags {
+ using CompatibleType = u32;
+
+ struct Bits {
+ bool symlink_follow : 1;
+ u32 _unused : 31;
+ };
+
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static LookupFlags read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+static_assert(sizeof(LookupFlags) == 4);
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1008
+struct OFlags {
+ using CompatibleType = u16;
+
+ struct Bits {
+ bool creat : 1;
+ bool directory : 1;
+ bool excl : 1;
+ bool trunc : 1;
+
+ u16 _unused : 12;
+ };
+
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ static OFlags read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+static_assert(sizeof(OFlags) == 2);
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1036
+using LinkCount = LittleEndian<u64>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1044
+struct FileStat {
+ Device dev;
+ INode ino;
+ FileType filetype;
+ LinkCount nlink;
+ FileSize size;
+ Timestamp atim;
+ Timestamp mtim;
+ Timestamp ctim;
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+};
+static_assert(sizeof(FileStat) == 64);
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1102
+using UserData = LittleEndian<u64>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1110
+enum class EventType : u8 {
+ Clock,
+ FDRead,
+ FDWrite,
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1137
+struct EventRWFlags {
+ using CompatibleType = u16;
+
+ struct Bits {
+ bool fd_readwrite_hangup : 1;
+
+ u16 _unused : 15;
+ };
+
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static EventRWFlags read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1151
+struct EventFDReadWrite {
+ FileSize nbytes;
+ EventRWFlags flags;
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static EventFDReadWrite read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1186
+struct Event {
+ UserData userdata;
+ Errno errno_;
+ EventType type;
+ EventFDReadWrite fd_readwrite;
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static Event read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1220
+struct SubClockFlags {
+ using CompatibleType = u16;
+
+ struct Bits {
+ bool subscription_clock_abstime : 1;
+
+ u16 _unused : 15;
+ };
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static SubClockFlags read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1237
+struct SubscriptionClock {
+ ClockID id;
+ Timestamp timeout;
+ Timestamp precision;
+ SubClockFlags flags;
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static SubscriptionClock read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1272
+struct SubscriptionFDReadWrite {
+ FD file_descriptor;
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static SubscriptionFDReadWrite read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1287
+struct SubscriptionU {
+ u8 tag;
+ union {
+ SubscriptionClock clock;
+ SubscriptionFDReadWrite fd_read;
+ SubscriptionFDReadWrite fd_write;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static SubscriptionU read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1306
+struct Subscription {
+ UserData userdata;
+ SubscriptionU u;
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static Subscription read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+static_assert(sizeof(Subscription) == 48);
+static_assert(alignof(Subscription) == 8);
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1334
+using ExitCode = LittleEndian<u32>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1342
+enum class Signal : u8 {
+ None,
+ HUP,
+ INT,
+ QUIT,
+ ILL,
+ TRAP,
+ ABRT,
+ BUS,
+ FPE,
+ KILL,
+ USR1,
+ SEGV,
+ USR2,
+ PIPE,
+ ALRM,
+ TERM,
+ CHLD,
+ CONT,
+ STOP,
+ TSTP,
+ TTIN,
+ TTOU,
+ URG,
+ XCPU,
+ XFSZ,
+ VTALRM,
+ PROF,
+ WINCH,
+ POLL,
+ PWR,
+ SYS,
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1536
+struct RIFlags {
+ using CompatibleType = u16;
+
+ struct Bits {
+ bool recv_peek : 1;
+ bool recv_waitall : 1;
+
+ u16 _unused : 14;
+ };
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ static RIFlags read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1554
+struct ROFlags {
+ using CompatibleType = u16;
+
+ struct Bits {
+ bool recv_data_truncated : 1;
+
+ u16 _unused : 15;
+ };
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1568
+using SIFlags = LittleEndian<u16>;
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1576
+struct SDFlags {
+ using CompatibleType = u8;
+
+ struct Bits {
+ bool rd : 1;
+ bool wr : 1;
+
+ u8 _unused : 6;
+ };
+ static_assert(sizeof(Bits) == sizeof(CompatibleType));
+
+ union {
+ Bits bits;
+ LittleEndian<CompatibleType> data;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static SDFlags read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1594
+enum class PreOpenType : u8 {
+ Dir,
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1607
+struct PreStatDir {
+ Size pr_name_len;
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+ static PreStatDir read_from(Array<ReadonlyBytes, 1> const& bytes);
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1636
+struct PreStat {
+ u8 tag;
+ union {
+ PreStatDir dir;
+ };
+
+ void serialize_into(Array<Bytes, 1> bytes) const;
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1676
+struct ArgsSizes {
+ Size count;
+ Size size;
+
+ using SerializationComponents = TypeList<Size, Size>;
+
+ void serialize_into(Array<Bytes, 2> bytes) const;
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L1708
+struct EnvironSizes {
+ Size count;
+ Size size;
+
+ using SerializationComponents = TypeList<Size, Size>;
+
+ void serialize_into(Array<Bytes, 2> bytes) const;
+};
+
+// https://github.com/WebAssembly/wasi-libc/blob/2c2fc9a2fddd0927a66f1c142e65c8dab6f5c5d7/libc-bottom-half/headers/public/wasi/api.h#L2664
+struct SockRecvResult {
+ Size size;
+ ROFlags roflags;
+
+ using SerializationComponents = TypeList<Size, ROFlags>;
+
+ void serialize_into(Array<Bytes, 2> bytes) const;
+};
+
+template<typename TResult, typename Tag = u32>
+struct Result {
+ Result(TResult&& result)
+ : bits {}
+ , tag(0)
+ {
+ new (&bits) TResult(move(result));
+ }
+
+ Result(Errno&& error)
+ : bits {}
+ , tag(1)
+ {
+ new (&bits) Errno(error);
+ }
+
+ Optional<TResult&> result() const
+ {
+ if (tag == 0)
+ return *bit_cast<TResult*>(&bits[0]);
+ return {};
+ }
+
+ Optional<Errno&> error() const
+ {
+ if (tag == 1)
+ return *bit_cast<Errno*>(&bits[0]);
+ return {};
+ }
+
+ bool is_error() const { return tag == 1; }
+
+ template<size_t N>
+ Errno serialize_into(Array<Bytes, N>&& spans) const
+ {
+ if (tag == 1)
+ return error().value();
+
+ ABI::serialize(*result(), move(spans));
+ return Errno::Success;
+ }
+
+private:
+ alignas(max(alignof(TResult), alignof(Errno))) u8 bits[max(sizeof(TResult), sizeof(Errno))];
+ LittleEndian<Tag> tag;
+};
+
+template<typename Tag>
+struct Result<void, Tag> {
+ Result()
+ : error_bits {}
+ , tag(0)
+ {
+ }
+
+ Result(Errno&& error)
+ : error_bits {}
+ , tag(1)
+ {
+ new (&error_bits) Errno(error);
+ }
+
+ Optional<Empty> result() const
+ {
+ if (tag == 0)
+ return { Empty {} };
+ return {};
+ }
+ Optional<Errno&> error() const
+ {
+ if (tag == 1)
+ return *bit_cast<Errno*>(&error_bits[0]);
+ return {};
+ }
+ bool is_error() const { return tag == 1; }
+
+private:
+ alignas(Errno) u8 error_bits[sizeof(Errno)];
+ LittleEndian<Tag> tag;
+};
+
+struct Implementation {
+ struct MappedPath {
+ LexicalPath host_path;
+ LexicalPath mapped_path;
+ mutable Optional<int> opened_fd {};
+ };
+
+ struct Details {
+ Function<Vector<AK::String>()> provide_arguments;
+ Function<Vector<AK::String>()> provide_environment;
+ Function<Vector<MappedPath>()> provide_preopened_directories;
+ };
+
+ explicit Implementation(Details&& details)
+ : provide_arguments(move(details.provide_arguments))
+ , provide_environment(move(details.provide_environment))
+ , provide_preopened_directories(move(details.provide_preopened_directories))
+ {
+ // Map all of std{in,out,err} by default.
+ m_fd_map.insert(0, 0);
+ m_fd_map.insert(1, 1);
+ m_fd_map.insert(2, 2);
+ }
+
+ ErrorOr<HostFunction> function_by_name(StringView);
+
+private:
+ template<auto impl>
+ HostFunction invocation_of(StringView name) { return ABI::InvocationOf<impl> {}(*this, name); }
+
+ ErrorOr<Result<void>> impl$args_get(Configuration&, Pointer<Pointer<u8>> argv, Pointer<u8> argv_buf);
+ ErrorOr<Result<ArgsSizes>> impl$args_sizes_get(Configuration&);
+ ErrorOr<Result<void>> impl$environ_get(Configuration&, Pointer<Pointer<u8>> environ, Pointer<u8> environ_buf);
+ ErrorOr<Result<EnvironSizes>> impl$environ_sizes_get(Configuration&);
+ ErrorOr<Result<Timestamp>> impl$clock_res_get(Configuration&, ClockID id);
+ ErrorOr<Result<Timestamp>> impl$clock_time_get(Configuration&, ClockID id, Timestamp precision);
+ ErrorOr<Result<void>> impl$fd_advise(Configuration&, FD, FileSize offset, FileSize len, Advice);
+ ErrorOr<Result<void>> impl$fd_allocate(Configuration&, FD, FileSize offset, FileSize len);
+ ErrorOr<Result<void>> impl$fd_close(Configuration&, FD);
+ ErrorOr<Result<void>> impl$fd_datasync(Configuration&, FD);
+ ErrorOr<Result<FDStat>> impl$fd_fdstat_get(Configuration&, FD);
+ ErrorOr<Result<void>> impl$fd_fdstat_set_flags(Configuration&, FD, FDFlags);
+ ErrorOr<Result<void>> impl$fd_fdstat_set_rights(Configuration&, FD, Rights fs_rights_base, Rights fs_rights_inheriting);
+ ErrorOr<Result<FileStat>> impl$fd_filestat_get(Configuration&, FD);
+ ErrorOr<Result<void>> impl$fd_filestat_set_size(Configuration&, FD, FileSize);
+ ErrorOr<Result<void>> impl$fd_filestat_set_times(Configuration&, FD, Timestamp atim, Timestamp mtim, FSTFlags);
+ ErrorOr<Result<Size>> impl$fd_pread(Configuration&, FD, Pointer<IOVec> iovs, Size iovs_len, FileSize offset);
+ ErrorOr<Result<PreStat>> impl$fd_prestat_get(Configuration&, FD);
+ ErrorOr<Result<void>> impl$fd_prestat_dir_name(Configuration&, FD, Pointer<u8> path, Size path_len);
+ ErrorOr<Result<Size>> impl$fd_pwrite(Configuration&, FD, Pointer<CIOVec> iovs, Size iovs_len, FileSize offset);
+ ErrorOr<Result<Size>> impl$fd_read(Configuration&, FD, Pointer<IOVec> iovs, Size iovs_len);
+ ErrorOr<Result<Size>> impl$fd_readdir(Configuration&, FD, Pointer<u8> buf, Size buf_len, DirCookie cookie);
+ ErrorOr<Result<void>> impl$fd_renumber(Configuration&, FD from, FD to);
+ ErrorOr<Result<FileSize>> impl$fd_seek(Configuration&, FD, FileDelta offset, Whence whence);
+ ErrorOr<Result<void>> impl$fd_sync(Configuration&, FD);
+ ErrorOr<Result<FileSize>> impl$fd_tell(Configuration&, FD);
+ ErrorOr<Result<Size>> impl$fd_write(Configuration&, FD, Pointer<CIOVec> iovs, Size iovs_len);
+ ErrorOr<Result<void>> impl$path_create_directory(Configuration&, FD, Pointer<u8> path, Size path_len);
+ ErrorOr<Result<FileStat>> impl$path_filestat_get(Configuration&, FD, LookupFlags, ConstPointer<u8> path, Size path_len);
+ ErrorOr<Result<void>> impl$path_filestat_set_times(Configuration&, FD, LookupFlags, Pointer<u8> path, Size path_len, Timestamp atim, Timestamp mtim, FSTFlags);
+ ErrorOr<Result<void>> impl$path_link(Configuration&, FD, LookupFlags, Pointer<u8> old_path, Size old_path_len, FD, Pointer<u8> new_path, Size new_path_len);
+ ErrorOr<Result<FD>> impl$path_open(Configuration&, FD, LookupFlags, Pointer<u8> path, Size path_len, OFlags, Rights fs_rights_base, Rights fs_rights_inheriting, FDFlags fd_flags);
+ ErrorOr<Result<Size>> impl$path_readlink(Configuration&, FD, LookupFlags, Pointer<u8> path, Size path_len, Pointer<u8> buf, Size buf_len);
+ ErrorOr<Result<void>> impl$path_remove_directory(Configuration&, FD, Pointer<u8> path, Size path_len);
+ ErrorOr<Result<void>> impl$path_rename(Configuration&, FD, Pointer<u8> old_path, Size old_path_len, FD, Pointer<u8> new_path, Size new_path_len);
+ ErrorOr<Result<void>> impl$path_symlink(Configuration&, Pointer<u8> old_path, Size old_path_len, FD, Pointer<u8> new_path, Size new_path_len);
+ ErrorOr<Result<void>> impl$path_unlink_file(Configuration&, FD, Pointer<u8> path, Size path_len);
+ ErrorOr<Result<Size>> impl$poll_oneoff(Configuration&, ConstPointer<Subscription> in, Pointer<Event> out, Size nsubscriptions);
+ ErrorOr<Result<void>> impl$proc_exit(Configuration&, ExitCode); // Note: noreturn.
+ ErrorOr<Result<void>> impl$proc_raise(Configuration&, Signal);
+ ErrorOr<Result<void>> impl$sched_yield(Configuration&);
+ ErrorOr<Result<void>> impl$random_get(Configuration&, Pointer<u8> buf, Size buf_len);
+ ErrorOr<Result<FD>> impl$sock_accept(Configuration&, FD fd, FDFlags fd_flags);
+ ErrorOr<Result<SockRecvResult>> impl$sock_recv(Configuration&, FD fd, Pointer<IOVec> ri_data, Size ri_data_len, RIFlags ri_flags);
+ ErrorOr<Result<Size>> impl$sock_send(Configuration&, FD fd, Pointer<CIOVec> si_data, Size ri_data_len, SIFlags si_flags);
+ ErrorOr<Result<void>> impl$sock_shutdown(Configuration&, FD fd, SDFlags how);
+
+ Vector<AK::String> const& arguments() const;
+ Vector<AK::String> const& environment() const;
+ Vector<MappedPath> const& preopened_directories() const;
+
+ using PreopenedDirectoryDescriptor = DistinctNumeric<LittleEndian<size_t>, struct PreopenedDirectoryDescriptor_tag, AK::DistinctNumericFeature::Comparison, AK::DistinctNumericFeature::CastToUnderlying, AK::DistinctNumericFeature::Increment>;
+ using UnmappedDescriptor = DistinctNumeric<LittleEndian<size_t>, struct UnmappedDescriptor_tag, AK::DistinctNumericFeature::Comparison, AK::DistinctNumericFeature::CastToUnderlying>;
+ using MappedDescriptor = Variant<u32, PreopenedDirectoryDescriptor>;
+ using Descriptor = Variant<u32, PreopenedDirectoryDescriptor, UnmappedDescriptor>;
+
+ Descriptor map_fd(FD);
+
+public:
+ Function<Vector<AK::String>()> provide_arguments;
+ Function<Vector<AK::String>()> provide_environment;
+ Function<Vector<MappedPath>()> provide_preopened_directories;
+
+private:
+ struct Cache {
+ Optional<Vector<AK::String>> cached_arguments;
+ Optional<Vector<AK::String>> cached_environment;
+ Optional<Vector<MappedPath>> cached_preopened_directories;
+ };
+
+ mutable Cache cache {};
+
+ RedBlackTree<u32, MappedDescriptor> m_fd_map;
+ size_t m_first_unmapped_preopened_directory_index { 0 };
+};
+
+#undef IMPL
+
+}
+
+namespace Wasm::Wasi::ABI {
+
+template<typename T, typename... Args>
+struct ToCompatibleValue<DistinctNumeric<T, Args...>> {
+ using Type = typename ToCompatibleValue<T>::Type;
+};
+
+template<typename T>
+struct ToCompatibleValue<LittleEndian<T>> {
+ using Type = MakeSigned<T>;
+};
+
+template<typename T>
+requires(requires { declval<typename T::CompatibleType>(); })
+struct ToCompatibleValue<T> {
+ using Type = MakeSigned<typename T::CompatibleType>;
+};
+
+template<Integral T>
+struct ToCompatibleValue<T> {
+ using Type = MakeSigned<T>;
+};
+
+template<Enum T>
+struct ToCompatibleValue<T> {
+ using Type = MakeSigned<UnderlyingType<T>>;
+};
+
+}
+
+template<typename T>
+struct AK::Formatter<Wasm::Wasi::LittleEndian<T>> : AK::Formatter<T> {
+ ErrorOr<void> format(FormatBuilder& builder, Wasm::Wasi::LittleEndian<T> value)
+ {
+ return Formatter<T>::format(builder, value.operator T());
+ }
+};