diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2023-04-05 01:14:43 +0330 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2023-04-26 03:47:15 +0330 |
commit | 7e4e9fdb8ff5ce21b541f1d2df9b9215fc3757d9 (patch) | |
tree | 61ef99c006fe070b299fc1e1c9dde6ca7b51abe9 /Userland/Libraries/LibWasm/Wasi.h | |
parent | eceb244befb0992c47dd13b80bada954b4689053 (diff) | |
download | serenity-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/LibWasm/Wasi.h')
-rw-r--r-- | Userland/Libraries/LibWasm/Wasi.h | 943 |
1 files changed, 943 insertions, 0 deletions
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()); + } +}; |