mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 18:12:43 +00:00 
			
		
		
		
	 7e4e9fdb8f
			
		
	
	
		7e4e9fdb8f
		
	
	
	
	
		
			
			This commit starts adding support for WASI, along with the framework to implement all the functions (though only a couple are currently implemented).
		
			
				
	
	
		
			943 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			943 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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());
 | |
|     }
 | |
| };
 |