diff --git a/LICENSE b/LICENSE index f57c3dc0c37194..db59472ba13151 100644 --- a/LICENSE +++ b/LICENSE @@ -1526,3 +1526,28 @@ The externally maintained libraries used by Node.js are: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ + +- uvwasi, located at deps/uvwasi, is licensed as follows: + """ + MIT License + + Copyright (c) 2019 Colin Ihrig and Contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ diff --git a/deps/uvwasi/LICENSE b/deps/uvwasi/LICENSE new file mode 100644 index 00000000000000..dfb8546af52b03 --- /dev/null +++ b/deps/uvwasi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Colin Ihrig and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deps/uvwasi/include/clocks.h b/deps/uvwasi/include/clocks.h new file mode 100644 index 00000000000000..7437d03df6bfa7 --- /dev/null +++ b/deps/uvwasi/include/clocks.h @@ -0,0 +1,13 @@ +#ifndef __UVWASI_CLOCKS_H__ +#define __UVWASI_CLOCKS_H__ + +#include "wasi_types.h" + +uvwasi_errno_t uvwasi__clock_gettime_realtime(uvwasi_timestamp_t* time); +uvwasi_errno_t uvwasi__clock_gettime_process_cputime(uvwasi_timestamp_t* time); +uvwasi_errno_t uvwasi__clock_gettime_thread_cputime(uvwasi_timestamp_t* time); + +uvwasi_errno_t uvwasi__clock_getres_process_cputime(uvwasi_timestamp_t* time); +uvwasi_errno_t uvwasi__clock_getres_thread_cputime(uvwasi_timestamp_t* time); + +#endif /* __UVWASI_CLOCKS_H__ */ diff --git a/deps/uvwasi/include/fd_table.h b/deps/uvwasi/include/fd_table.h new file mode 100644 index 00000000000000..ef87a3ff8f93c5 --- /dev/null +++ b/deps/uvwasi/include/fd_table.h @@ -0,0 +1,60 @@ +#ifndef __UVWASI_FD_TABLE_H__ +#define __UVWASI_FD_TABLE_H__ + +#include +#include "uv.h" +#include "wasi_types.h" +#include "uv_mapping.h" + +/* TODO(cjihrig): PATH_MAX_BYTES shouldn't be stack allocated. On Windows, paths + can be 32k long, and this PATH_MAX_BYTES is an artificial limitation. */ +#ifdef _WIN32 +/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ +# define PATH_MAX_BYTES (MAX_PATH * 4) +#else +# include +# define PATH_MAX_BYTES (PATH_MAX) +#endif + + +struct uvwasi_fd_wrap_t { + uvwasi_fd_t id; + uv_file fd; + char path[PATH_MAX_BYTES]; + char real_path[PATH_MAX_BYTES]; + uvwasi_filetype_t type; + uvwasi_rights_t rights_base; + uvwasi_rights_t rights_inheriting; + int preopen; + int valid; +}; + +struct uvwasi_fd_table_t { + struct uvwasi_fd_wrap_t* fds; + uint32_t size; + uint32_t used; +}; + +uvwasi_errno_t uvwasi_fd_table_init(struct uvwasi_fd_table_t* table, + uint32_t init_size); +void uvwasi_fd_table_free(struct uvwasi_fd_table_t* table); +uvwasi_errno_t uvwasi_fd_table_insert_preopen(struct uvwasi_fd_table_t* table, + const uv_file fd, + const char* path, + const char* real_path); +uvwasi_errno_t uvwasi_fd_table_insert_fd(struct uvwasi_fd_table_t* table, + const uv_file fd, + const int flags, + const char* path, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting, + struct uvwasi_fd_wrap_t* wrap); +uvwasi_errno_t uvwasi_fd_table_get(const struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id, + struct uvwasi_fd_wrap_t** wrap, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting); +uvwasi_errno_t uvwasi_fd_table_remove(struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id); + +#endif /* __UVWASI_FD_TABLE_H__ */ diff --git a/deps/uvwasi/include/uv_mapping.h b/deps/uvwasi/include/uv_mapping.h new file mode 100644 index 00000000000000..d835ca507a4856 --- /dev/null +++ b/deps/uvwasi/include/uv_mapping.h @@ -0,0 +1,15 @@ +#ifndef __UVWASI_UV_MAPPING_H__ +#define __UVWASI_UV_MAPPING_H__ + +#include "uv.h" +#include "wasi_types.h" + +#define NANOS_PER_SEC 1000000000 + +uvwasi_errno_t uvwasi__translate_uv_error(int err); +int uvwasi__translate_to_uv_signal(uvwasi_signal_t sig); +uvwasi_timestamp_t uvwasi__timespec_to_timestamp(const uv_timespec_t* ts); +uvwasi_filetype_t uvwasi__stat_to_filetype(const uv_stat_t* stat); +void uvwasi__stat_to_filestat(const uv_stat_t* stat, uvwasi_filestat_t* fs); + +#endif /* __UVWASI_UV_MAPPING_H__ */ diff --git a/deps/uvwasi/include/uvwasi.h b/deps/uvwasi/include/uvwasi.h new file mode 100644 index 00000000000000..a7695734ac2ac6 --- /dev/null +++ b/deps/uvwasi/include/uvwasi.h @@ -0,0 +1,252 @@ +#ifndef __UVWASI_H__ +#define __UVWASI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wasi_types.h" +#include "uv_mapping.h" +#include "fd_table.h" + +#define UVWASI_VERSION_MAJOR 0 +#define UVWASI_VERSION_MINOR 0 +#define UVWASI_VERSION_PATCH 1 +#define UVWASI_VERSION_HEX ((UVWASI_VERSION_MAJOR << 16) | \ + (UVWASI_VERSION_MINOR << 8) | \ + (UVWASI_VERSION_PATCH)) +#define UVWASI_STRINGIFY(v) UVWASI_STRINGIFY_HELPER(v) +#define UVWASI_STRINGIFY_HELPER(v) #v +#define UVWASI_VERSION_STRING UVWASI_STRINGIFY(UVWASI_VERSION_MAJOR) "." \ + UVWASI_STRINGIFY(UVWASI_VERSION_MINOR) "." \ + UVWASI_STRINGIFY(UVWASI_VERSION_PATCH) + + +typedef struct uvwasi_s { + struct uvwasi_fd_table_t fds; + size_t argc; + char** argv; + char* argv_buf; + size_t argv_buf_size; + size_t envc; + char** env; + char* env_buf; + size_t env_buf_size; +} uvwasi_t; + +typedef struct uvwasi_preopen_s { + char* mapped_path; + char* real_path; +} uvwasi_preopen_t; + +typedef struct uvwasi_options_s { + size_t fd_table_size; + size_t preopenc; + uvwasi_preopen_t* preopens; + size_t argc; + char** argv; + char** envp; +} uvwasi_options_t; + + +// Embedder API. +uvwasi_errno_t uvwasi_init(uvwasi_t* uvwasi, uvwasi_options_t* options); +void uvwasi_destroy(uvwasi_t* uvwasi); +uvwasi_errno_t uvwasi_embedder_remap_fd(uvwasi_t* uvwasi, + const uvwasi_fd_t fd, + uv_file new_host_fd); + + +// WASI system call API. +uvwasi_errno_t uvwasi_args_get(uvwasi_t* uvwasi, char** argv, char* argv_buf); +uvwasi_errno_t uvwasi_args_sizes_get(uvwasi_t* uvwasi, + size_t* argc, + size_t* argv_buf_size); +uvwasi_errno_t uvwasi_clock_res_get(uvwasi_t* uvwasi, + uvwasi_clockid_t clock_id, + uvwasi_timestamp_t* resolution); +uvwasi_errno_t uvwasi_clock_time_get(uvwasi_t* uvwasi, + uvwasi_clockid_t clock_id, + uvwasi_timestamp_t precision, + uvwasi_timestamp_t* time); +uvwasi_errno_t uvwasi_environ_get(uvwasi_t* uvwasi, + char** environment, + char* environ_buf); +uvwasi_errno_t uvwasi_environ_sizes_get(uvwasi_t* uvwasi, + size_t* environ_count, + size_t* environ_buf_size); +uvwasi_errno_t uvwasi_fd_advise(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t offset, + uvwasi_filesize_t len, + uvwasi_advice_t advice); +uvwasi_errno_t uvwasi_fd_allocate(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t offset, + uvwasi_filesize_t len); +uvwasi_errno_t uvwasi_fd_close(uvwasi_t* uvwasi, uvwasi_fd_t fd); +uvwasi_errno_t uvwasi_fd_datasync(uvwasi_t* uvwasi, uvwasi_fd_t fd); +uvwasi_errno_t uvwasi_fd_fdstat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_fdstat_t* buf); +uvwasi_errno_t uvwasi_fd_fdstat_set_flags(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_fdflags_t flags); +uvwasi_errno_t uvwasi_fd_fdstat_set_rights(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_rights_t fs_rights_base, + uvwasi_rights_t fs_rights_inheriting + ); +uvwasi_errno_t uvwasi_fd_filestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filestat_t* buf); +uvwasi_errno_t uvwasi_fd_filestat_set_size(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t st_size); +uvwasi_errno_t uvwasi_fd_filestat_set_times(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_timestamp_t st_atim, + uvwasi_timestamp_t st_mtim, + uvwasi_fstflags_t fst_flags); +uvwasi_errno_t uvwasi_fd_pread(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_iovec_t* iovs, + size_t iovs_len, + uvwasi_filesize_t offset, + size_t* nread); +uvwasi_errno_t uvwasi_fd_prestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_prestat_t* buf); +uvwasi_errno_t uvwasi_fd_prestat_dir_name(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + char* path, + size_t path_len); +uvwasi_errno_t uvwasi_fd_pwrite(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_ciovec_t* iovs, + size_t iovs_len, + uvwasi_filesize_t offset, + size_t* nwritten); +uvwasi_errno_t uvwasi_fd_read(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_iovec_t* iovs, + size_t iovs_len, + size_t* nread); +uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + void* buf, + size_t buf_len, + uvwasi_dircookie_t cookie, + size_t* bufused); +uvwasi_errno_t uvwasi_fd_renumber(uvwasi_t* uvwasi, + uvwasi_fd_t from, + uvwasi_fd_t to); +uvwasi_errno_t uvwasi_fd_seek(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filedelta_t offset, + uvwasi_whence_t whence, + uvwasi_filesize_t* newoffset); +uvwasi_errno_t uvwasi_fd_sync(uvwasi_t* uvwasi, uvwasi_fd_t fd); +uvwasi_errno_t uvwasi_fd_tell(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t* offset); +uvwasi_errno_t uvwasi_fd_write(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_ciovec_t* iovs, + size_t iovs_len, + size_t* nwritten); +uvwasi_errno_t uvwasi_path_create_directory(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len); +uvwasi_errno_t uvwasi_path_filestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_filestat_t* buf); +uvwasi_errno_t uvwasi_path_filestat_set_times(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_timestamp_t st_atim, + uvwasi_timestamp_t st_mtim, + uvwasi_fstflags_t fst_flags); +uvwasi_errno_t uvwasi_path_link(uvwasi_t* uvwasi, + uvwasi_fd_t old_fd, + uvwasi_lookupflags_t old_flags, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t new_fd, + const char* new_path, + size_t new_path_len); +uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, + uvwasi_fd_t dirfd, + uvwasi_lookupflags_t dirflags, + const char* path, + size_t path_len, + uvwasi_oflags_t o_flags, + uvwasi_rights_t fs_rights_base, + uvwasi_rights_t fs_rights_inheriting, + uvwasi_fdflags_t fs_flags, + uvwasi_fd_t* fd); +uvwasi_errno_t uvwasi_path_readlink(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len, + char* buf, + size_t buf_len, + size_t* bufused); +uvwasi_errno_t uvwasi_path_remove_directory(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len); +uvwasi_errno_t uvwasi_path_rename(uvwasi_t* uvwasi, + uvwasi_fd_t old_fd, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t new_fd, + const char* new_path, + size_t new_path_len); +uvwasi_errno_t uvwasi_path_symlink(uvwasi_t* uvwasi, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t fd, + const char* new_path, + size_t new_path_len); +uvwasi_errno_t uvwasi_path_unlink_file(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len); +uvwasi_errno_t uvwasi_poll_oneoff(uvwasi_t* uvwasi, + const uvwasi_subscription_t* in, + uvwasi_event_t* out, + size_t nsubscriptions, + size_t* nevents); +uvwasi_errno_t uvwasi_proc_exit(uvwasi_t* uvwasi, uvwasi_exitcode_t rval); +uvwasi_errno_t uvwasi_proc_raise(uvwasi_t* uvwasi, uvwasi_signal_t sig); +uvwasi_errno_t uvwasi_random_get(uvwasi_t* uvwasi, void* buf, size_t buf_len); +uvwasi_errno_t uvwasi_sched_yield(uvwasi_t* uvwasi); +uvwasi_errno_t uvwasi_sock_recv(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + const uvwasi_iovec_t* ri_data, + size_t ri_data_len, + uvwasi_riflags_t ri_flags, + size_t* ro_datalen, + uvwasi_roflags_t* ro_flags); +uvwasi_errno_t uvwasi_sock_send(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + const uvwasi_ciovec_t* si_data, + size_t si_data_len, + uvwasi_siflags_t si_flags, + size_t* so_datalen); +uvwasi_errno_t uvwasi_sock_shutdown(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + uvwasi_sdflags_t how); + +#ifdef __cplusplus +} +#endif + +#endif /* __UVWASI_H__ */ diff --git a/deps/uvwasi/include/wasi_types.h b/deps/uvwasi/include/wasi_types.h new file mode 100644 index 00000000000000..34b5291c577627 --- /dev/null +++ b/deps/uvwasi/include/wasi_types.h @@ -0,0 +1,323 @@ +#ifndef __UVWASI_WASI_TYPES_H__ +#define __UVWASI_WASI_TYPES_H__ + +#include +#include + +/* API: https://github.com/WebAssembly/WASI/blob/master/phases/unstable/docs/wasi_unstable_preview0.md */ + +typedef uint8_t uvwasi_advice_t; +#define UVWASI_ADVICE_NORMAL 0 +#define UVWASI_ADVICE_SEQUENTIAL 1 +#define UVWASI_ADVICE_RANDOM 2 +#define UVWASI_ADVICE_WILLNEED 3 +#define UVWASI_ADVICE_DONTNEED 4 +#define UVWASI_ADVICE_NOREUSE 5 + +typedef struct uvwasi_ciovec_s { + const void* buf; + size_t buf_len; +} uvwasi_ciovec_t; + +typedef uint32_t uvwasi_clockid_t; +#define UVWASI_CLOCK_REALTIME 0 +#define UVWASI_CLOCK_MONOTONIC 1 +#define UVWASI_CLOCK_PROCESS_CPUTIME_ID 2 +#define UVWASI_CLOCK_THREAD_CPUTIME_ID 3 + +typedef uint64_t uvwasi_device_t; + +typedef uint64_t uvwasi_dircookie_t; +#define UVWASI_DIRCOOKIE_START 0 + +typedef uint16_t uvwasi_errno_t; +#define UVWASI_ESUCCESS 0 +#define UVWASI_E2BIG 1 +#define UVWASI_EACCES 2 +#define UVWASI_EADDRINUSE 3 +#define UVWASI_EADDRNOTAVAIL 4 +#define UVWASI_EAFNOSUPPORT 5 +#define UVWASI_EAGAIN 6 +#define UVWASI_EALREADY 7 +#define UVWASI_EBADF 8 +#define UVWASI_EBADMSG 9 +#define UVWASI_EBUSY 10 +#define UVWASI_ECANCELED 11 +#define UVWASI_ECHILD 12 +#define UVWASI_ECONNABORTED 13 +#define UVWASI_ECONNREFUSED 14 +#define UVWASI_ECONNRESET 15 +#define UVWASI_EDEADLK 16 +#define UVWASI_EDESTADDRREQ 17 +#define UVWASI_EDOM 18 +#define UVWASI_EDQUOT 19 +#define UVWASI_EEXIST 20 +#define UVWASI_EFAULT 21 +#define UVWASI_EFBIG 22 +#define UVWASI_EHOSTUNREACH 23 +#define UVWASI_EIDRM 24 +#define UVWASI_EILSEQ 25 +#define UVWASI_EINPROGRESS 26 +#define UVWASI_EINTR 27 +#define UVWASI_EINVAL 28 +#define UVWASI_EIO 29 +#define UVWASI_EISCONN 30 +#define UVWASI_EISDIR 31 +#define UVWASI_ELOOP 32 +#define UVWASI_EMFILE 33 +#define UVWASI_EMLINK 34 +#define UVWASI_EMSGSIZE 35 +#define UVWASI_EMULTIHOP 36 +#define UVWASI_ENAMETOOLONG 37 +#define UVWASI_ENETDOWN 38 +#define UVWASI_ENETRESET 39 +#define UVWASI_ENETUNREACH 40 +#define UVWASI_ENFILE 41 +#define UVWASI_ENOBUFS 42 +#define UVWASI_ENODEV 43 +#define UVWASI_ENOENT 44 +#define UVWASI_ENOEXEC 45 +#define UVWASI_ENOLCK 46 +#define UVWASI_ENOLINK 47 +#define UVWASI_ENOMEM 48 +#define UVWASI_ENOMSG 49 +#define UVWASI_ENOPROTOOPT 50 +#define UVWASI_ENOSPC 51 +#define UVWASI_ENOSYS 52 +#define UVWASI_ENOTCONN 53 +#define UVWASI_ENOTDIR 54 +#define UVWASI_ENOTEMPTY 55 +#define UVWASI_ENOTRECOVERABLE 56 +#define UVWASI_ENOTSOCK 57 +#define UVWASI_ENOTSUP 58 +#define UVWASI_ENOTTY 59 +#define UVWASI_ENXIO 60 +#define UVWASI_EOVERFLOW 61 +#define UVWASI_EOWNERDEAD 62 +#define UVWASI_EPERM 63 +#define UVWASI_EPIPE 64 +#define UVWASI_EPROTO 65 +#define UVWASI_EPROTONOSUPPORT 66 +#define UVWASI_EPROTOTYPE 67 +#define UVWASI_ERANGE 68 +#define UVWASI_EROFS 69 +#define UVWASI_ESPIPE 70 +#define UVWASI_ESRCH 71 +#define UVWASI_ESTALE 72 +#define UVWASI_ETIMEDOUT 73 +#define UVWASI_ETXTBSY 74 +#define UVWASI_EXDEV 75 +#define UVWASI_ENOTCAPABLE 76 + +typedef uint16_t uvwasi_eventrwflags_t; /* Bitfield */ +#define UVWASI_EVENT_FD_READWRITE_HANGUP (1 << 0) + +typedef uint8_t uvwasi_eventtype_t; +#define UVWASI_EVENTTYPE_CLOCK 0 +#define UVWASI_EVENTTYPE_FD_READ 1 +#define UVWASI_EVENTTYPE_FD_WRITE 2 + +typedef uint32_t uvwasi_exitcode_t; + +typedef uint32_t uvwasi_fd_t; + +typedef uint16_t uvwasi_fdflags_t; /* Bitfield */ +#define UVWASI_FDFLAG_APPEND (1 << 0) +#define UVWASI_FDFLAG_DSYNC (1 << 1) +#define UVWASI_FDFLAG_NONBLOCK (1 << 2) +#define UVWASI_FDFLAG_RSYNC (1 << 3) +#define UVWASI_FDFLAG_SYNC (1 << 4) + +typedef int64_t uvwasi_filedelta_t; + +typedef uint64_t uvwasi_filesize_t; + +typedef uint8_t uvwasi_filetype_t; +#define UVWASI_FILETYPE_UNKNOWN 0 +#define UVWASI_FILETYPE_BLOCK_DEVICE 1 +#define UVWASI_FILETYPE_CHARACTER_DEVICE 2 +#define UVWASI_FILETYPE_DIRECTORY 3 +#define UVWASI_FILETYPE_REGULAR_FILE 4 +#define UVWASI_FILETYPE_SOCKET_DGRAM 5 +#define UVWASI_FILETYPE_SOCKET_STREAM 6 +#define UVWASI_FILETYPE_SYMBOLIC_LINK 7 + +typedef uint16_t uvwasi_fstflags_t; /* Bitfield */ +#define UVWASI_FILESTAT_SET_ATIM (1 << 0) +#define UVWASI_FILESTAT_SET_ATIM_NOW (1 << 1) +#define UVWASI_FILESTAT_SET_MTIM (1 << 2) +#define UVWASI_FILESTAT_SET_MTIM_NOW (1 << 3) + +typedef uint64_t uvwasi_inode_t; + +typedef struct uvwasi_iovec_s { + void* buf; + size_t buf_len; +} uvwasi_iovec_t; + +typedef uint32_t uvwasi_linkcount_t; + +typedef uint32_t uvwasi_lookupflags_t; /* Bitfield */ +#define UVWASI_LOOKUP_SYMLINK_FOLLOW (1 << 0) + +typedef uint16_t uvwasi_oflags_t; /* Bitfield */ +#define UVWASI_O_CREAT (1 << 0) +#define UVWASI_O_DIRECTORY (1 << 1) +#define UVWASI_O_EXCL (1 << 2) +#define UVWASI_O_TRUNC (1 << 3) + +typedef uint8_t uvwasi_preopentype_t; +#define UVWASI_PREOPENTYPE_DIR 0 + +typedef struct uvwasi_prestat_s { + uvwasi_preopentype_t pr_type; + union uvwasi_prestat_u { + struct uvwasi_prestat_dir_t { + size_t pr_name_len; + } dir; + } u; +} uvwasi_prestat_t; + +typedef uint16_t uvwasi_riflags_t; /* Bitfield */ +#define UVWASI_SOCK_RECV_PEEK (1 << 0) +#define UVWASI_SOCK_RECV_WAITALL (1 << 1) + +typedef uint64_t uvwasi_rights_t; /* Bitfield */ +#define UVWASI_RIGHT_FD_DATASYNC (1 << 0) +#define UVWASI_RIGHT_FD_READ (1 << 1) +#define UVWASI_RIGHT_FD_SEEK (1 << 2) +#define UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS (1 << 3) +#define UVWASI_RIGHT_FD_SYNC (1 << 4) +#define UVWASI_RIGHT_FD_TELL (1 << 5) +#define UVWASI_RIGHT_FD_WRITE (1 << 6) +#define UVWASI_RIGHT_FD_ADVISE (1 << 7) +#define UVWASI_RIGHT_FD_ALLOCATE (1 << 8) +#define UVWASI_RIGHT_PATH_CREATE_DIRECTORY (1 << 9) +#define UVWASI_RIGHT_PATH_CREATE_FILE (1 << 10) +#define UVWASI_RIGHT_PATH_LINK_SOURCE (1 << 11) +#define UVWASI_RIGHT_PATH_LINK_TARGET (1 << 12) +#define UVWASI_RIGHT_PATH_OPEN (1 << 13) +#define UVWASI_RIGHT_FD_READDIR (1 << 14) +#define UVWASI_RIGHT_PATH_READLINK (1 << 15) +#define UVWASI_RIGHT_PATH_RENAME_SOURCE (1 << 16) +#define UVWASI_RIGHT_PATH_RENAME_TARGET (1 << 17) +#define UVWASI_RIGHT_PATH_FILESTAT_GET (1 << 18) +#define UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE (1 << 19) +#define UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES (1 << 20) +#define UVWASI_RIGHT_FD_FILESTAT_GET (1 << 21) +#define UVWASI_RIGHT_FD_FILESTAT_SET_SIZE (1 << 22) +#define UVWASI_RIGHT_FD_FILESTAT_SET_TIMES (1 << 23) +#define UVWASI_RIGHT_PATH_SYMLINK (1 << 24) +#define UVWASI_RIGHT_PATH_REMOVE_DIRECTORY (1 << 25) +#define UVWASI_RIGHT_PATH_UNLINK_FILE (1 << 26) +#define UVWASI_RIGHT_POLL_FD_READWRITE (1 << 27) +#define UVWASI_RIGHT_SOCK_SHUTDOWN (1 << 28) + +typedef uint16_t uvwasi_roflags_t; /* Bitfield */ +#define UVWASI_SOCK_RECV_DATA_TRUNCATED (1 << 0) + +typedef uint8_t uvwasi_sdflags_t; /* Bitfield */ +#define UVWASI_SHUT_RD (1 << 0) +#define UVWASI_SHUT_WR (1 << 1) + +typedef uint16_t uvwasi_siflags_t; /* Bitfield */ + +typedef uint8_t uvwasi_signal_t; +#define UVWASI_SIGHUP 1 +#define UVWASI_SIGINT 2 +#define UVWASI_SIGQUIT 3 +#define UVWASI_SIGILL 4 +#define UVWASI_SIGTRAP 5 +#define UVWASI_SIGABRT 6 +#define UVWASI_SIGBUS 7 +#define UVWASI_SIGFPE 8 +#define UVWASI_SIGKILL 9 +#define UVWASI_SIGUSR1 10 +#define UVWASI_SIGSEGV 11 +#define UVWASI_SIGUSR2 12 +#define UVWASI_SIGPIPE 13 +#define UVWASI_SIGALRM 14 +#define UVWASI_SIGTERM 15 +#define UVWASI_SIGCHLD 16 +#define UVWASI_SIGCONT 17 +#define UVWASI_SIGSTOP 18 +#define UVWASI_SIGTSTP 19 +#define UVWASI_SIGTTIN 20 +#define UVWASI_SIGTTOU 21 +#define UVWASI_SIGURG 22 +#define UVWASI_SIGXCPU 23 +#define UVWASI_SIGXFSZ 24 +#define UVWASI_SIGVTALRM 25 +#define UVWASI_SIGPROF 26 +#define UVWASI_SIGWINCH 27 +#define UVWASI_SIGPOLL 28 +#define UVWASI_SIGPWR 29 +#define UVWASI_SIGSYS 30 + +typedef uint16_t uvwasi_subclockflags_t; /* Bitfield */ +#define UVWASI_SUBSCRIPTION_CLOCK_ABSTIME (1 << 0) + +typedef uint64_t uvwasi_timestamp_t; + +typedef uint64_t uvwasi_userdata_t; + +typedef struct uvwasi_subscription_s { + uvwasi_userdata_t userdata; + uvwasi_eventtype_t type; + union { + struct { + uvwasi_userdata_t identifier; + uvwasi_clockid_t clock_id; + uvwasi_timestamp_t timeout; + uvwasi_timestamp_t precision; + uvwasi_subclockflags_t flags; + } clock; + struct { + uvwasi_fd_t fd; + } fd_readwrite; + } u; +} uvwasi_subscription_t; + +typedef struct uvwasi_dirent_s { + uvwasi_dircookie_t d_next; + uvwasi_inode_t d_ino; + uint32_t d_namlen; + uvwasi_filetype_t d_type; +} uvwasi_dirent_t; + +typedef struct uvwasi_fdstat_s { + uvwasi_filetype_t fs_filetype; + uvwasi_fdflags_t fs_flags; + uvwasi_rights_t fs_rights_base; + uvwasi_rights_t fs_rights_inheriting; +} uvwasi_fdstat_t; + +typedef struct uvwasi_filestat_s { + uvwasi_device_t st_dev; + uvwasi_inode_t st_ino; + uvwasi_filetype_t st_filetype; + uvwasi_linkcount_t st_nlink; + uvwasi_filesize_t st_size; + uvwasi_timestamp_t st_atim; + uvwasi_timestamp_t st_mtim; + uvwasi_timestamp_t st_ctim; +} uvwasi_filestat_t; + +typedef struct uvwasi_event_s { + uvwasi_userdata_t userdata; + uvwasi_errno_t error; + uvwasi_eventtype_t type; + union { + struct { + uvwasi_filesize_t nbytes; + uvwasi_eventrwflags_t flags; + } fd_readwrite; + } u; +} uvwasi_event_t; + +typedef uint8_t uvwasi_whence_t; +#define UVWASI_WHENCE_CUR 0 +#define UVWASI_WHENCE_END 1 +#define UVWASI_WHENCE_SET 2 + +#endif /* __UVWASI_WASI_TYPES_H__ */ diff --git a/deps/uvwasi/src/clocks.c b/deps/uvwasi/src/clocks.c new file mode 100644 index 00000000000000..e1fbc696b62f05 --- /dev/null +++ b/deps/uvwasi/src/clocks.c @@ -0,0 +1,194 @@ +#ifndef _WIN32 +# include +# include +# include +# include +#endif /* _WIN32 */ + +#include "uv.h" +#include "wasi_types.h" +#include "uv_mapping.h" + + +#define UVWASI__WIN_TIME_AND_RETURN(handle, time) \ + do { \ + FILETIME create; \ + FILETIME exit; \ + FILETIME system; \ + FILETIME user; \ + SYSTEMTIME sys_system; \ + SYSTEMTIME sys_user; \ + if (0 == GetProcessTimes((handle), &create, &exit, &system, &user)) { \ + return uvwasi__translate_uv_error( \ + uv_translate_sys_error(GetLastError()) \ + ); \ + } \ + \ + if (0 == FileTimeToSystemTime(&system, &sys_system)) { \ + return uvwasi__translate_uv_error( \ + uv_translate_sys_error(GetLastError()) \ + ); \ + } \ + \ + if (0 == FileTimeToSystemTime(&user, &sys_user)) { \ + return uvwasi__translate_uv_error( \ + uv_translate_sys_error(GetLastError()) \ + ); \ + } \ + \ + (time) = (((sys_system.wHour * 3600) + (sys_system.wMinute * 60) + \ + sys_system.wSecond) * NANOS_PER_SEC) + \ + (sys_system.wMilliseconds * 1000000) + \ + (((sys_user.wHour * 3600) + (sys_user.wMinute * 60) + \ + sys_user.wSecond) * NANOS_PER_SEC) + \ + (sys_user.wMilliseconds * 1000000); \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__CLOCK_GETTIME_AND_RETURN(clk, time) \ + do { \ + struct timespec ts; \ + if (0 != clock_gettime((clk), &ts)) \ + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); \ + (time) = (ts.tv_sec * NANOS_PER_SEC) + ts.tv_nsec; \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__GETRUSAGE_AND_RETURN(who, time) \ + do { \ + struct rusage ru; \ + if (0 != getrusage((who), &ru)) \ + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); \ + (time) = (ru.ru_utime.tv_sec * NANOS_PER_SEC) + \ + (ru.ru_utime.tv_usec * 1000) + \ + (ru.ru_stime.tv_sec * NANOS_PER_SEC) + \ + (ru.ru_stime.tv_usec * 1000); \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__OSX_THREADTIME_AND_RETURN(time) \ + do { \ + mach_port_t thread; \ + thread_basic_info_data_t info; \ + mach_msg_type_number_t count; \ + count = THREAD_BASIC_INFO_COUNT; \ + thread = pthread_mach_thread_np(pthread_self()); \ + if (KERN_SUCCESS != thread_info(thread, \ + THREAD_BASIC_INFO, \ + (thread_info_t) &info, \ + &count)) { \ + return UVWASI_ENOSYS; \ + } \ + (time) = (info.user_time.seconds * NANOS_PER_SEC) + \ + (info.user_time.microseconds * 1000) + \ + (info.system_time.seconds * NANOS_PER_SEC) + \ + (info.system_time.microseconds * 1000); \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__WIN_GETRES_AND_RETURN(time) \ + do { \ + /* The GetProcessTimes() docs claim a resolution of 100 ns. */ \ + (time) = 100; \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__CLOCK_GETRES_AND_RETURN(clk, time) \ + do { \ + struct timespec ts; \ + /* Try calling clock_getres(). If it doesn't succeed, then default to \ + 1000000. We implement all of the clocks, and some platforms (such as \ + SmartOS) don't support all of the clocks, even though they define \ + the constants for them. */ \ + if (0 != clock_getres((clk), &ts)) \ + (time) = 1000000; \ + else \ + (time) = (ts.tv_sec * NANOS_PER_SEC) + ts.tv_nsec; \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__SLOW_GETRES_AND_RETURN(time) \ + do { \ + /* Assume a "worst case" of 1000000 ns resolution. */ \ + (time) = 1000000; \ + return UVWASI_ESUCCESS; \ + } while (0) + + +uvwasi_errno_t uvwasi__clock_gettime_realtime(uvwasi_timestamp_t* time) { + uv_timeval64_t tv; + int r; + + r = uv_gettimeofday(&tv); + if (r != 0) + return uvwasi__translate_uv_error(r); + + *time = (tv.tv_sec * NANOS_PER_SEC) + (tv.tv_usec * 1000); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi__clock_gettime_process_cputime(uvwasi_timestamp_t* time) { +#if defined(_WIN32) + UVWASI__WIN_TIME_AND_RETURN(GetCurrentProcess(), *time); +#elif defined(CLOCK_PROCESS_CPUTIME_ID) && \ + !defined(__APPLE__) && \ + !defined(__sun) + UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_PROCESS_CPUTIME_ID, *time); +#else + UVWASI__GETRUSAGE_AND_RETURN(RUSAGE_SELF, *time); +#endif +} + + +uvwasi_errno_t uvwasi__clock_gettime_thread_cputime(uvwasi_timestamp_t* time) { +#if defined(_WIN32) + UVWASI__WIN_TIME_AND_RETURN(GetCurrentThread(), *time); +#elif defined(__APPLE__) + UVWASI__OSX_THREADTIME_AND_RETURN(*time); +#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun) + UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_THREAD_CPUTIME_ID, *time); +#else +# if defined(RUSAGE_LWP) + UVWASI__GETRUSAGE_AND_RETURN(RUSAGE_LWP, *time); +# elif defined(RUSAGE_THREAD) + UVWASI__GETRUSAGE_AND_RETURN(RUSAGE_THREAD, *time); +# else + return UVWASI_ENOSYS; +# endif /* RUSAGE_LWP */ +#endif +} + + +uvwasi_errno_t uvwasi__clock_getres_process_cputime(uvwasi_timestamp_t* time) { +#if defined(_WIN32) + UVWASI__WIN_GETRES_AND_RETURN(*time); +#elif defined(CLOCK_PROCESS_CPUTIME_ID) && \ + !defined(__APPLE__) && \ + !defined(__sun) + UVWASI__CLOCK_GETRES_AND_RETURN(CLOCK_PROCESS_CPUTIME_ID, *time); +#else + UVWASI__SLOW_GETRES_AND_RETURN(*time); +#endif +} + + +uvwasi_errno_t uvwasi__clock_getres_thread_cputime(uvwasi_timestamp_t* time) { +#if defined(_WIN32) + UVWASI__WIN_GETRES_AND_RETURN(*time); +#elif defined(__APPLE__) + UVWASI__SLOW_GETRES_AND_RETURN(*time); +#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun) + UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_THREAD_CPUTIME_ID, *time); +#elif defined(RUSAGE_THREAD) || defined(RUSAGE_LWP) + UVWASI__SLOW_GETRES_AND_RETURN(*time); +#else + return UVWASI_ENOSYS; +#endif +} diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c new file mode 100644 index 00000000000000..9343868074d10c --- /dev/null +++ b/deps/uvwasi/src/fd_table.c @@ -0,0 +1,422 @@ +#include +#include +#include + +#ifndef _WIN32 +# include +#endif /* _WIN32 */ + +#include "uv.h" +#include "fd_table.h" +#include "wasi_types.h" +#include "uv_mapping.h" + + +#define UVWASI__RIGHTS_ALL (UVWASI_RIGHT_FD_DATASYNC | \ + UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_SEEK | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_TELL | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_FD_ALLOCATE | \ + UVWASI_RIGHT_PATH_CREATE_DIRECTORY | \ + UVWASI_RIGHT_PATH_CREATE_FILE | \ + UVWASI_RIGHT_PATH_LINK_SOURCE | \ + UVWASI_RIGHT_PATH_LINK_TARGET | \ + UVWASI_RIGHT_PATH_OPEN | \ + UVWASI_RIGHT_FD_READDIR | \ + UVWASI_RIGHT_PATH_READLINK | \ + UVWASI_RIGHT_PATH_RENAME_SOURCE | \ + UVWASI_RIGHT_PATH_RENAME_TARGET | \ + UVWASI_RIGHT_PATH_FILESTAT_GET | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_SYMLINK | \ + UVWASI_RIGHT_PATH_UNLINK_FILE | \ + UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + UVWASI_RIGHT_POLL_FD_READWRITE | \ + UVWASI_RIGHT_SOCK_SHUTDOWN) + +#define UVWASI__RIGHTS_BLOCK_DEVICE_BASE UVWASI__RIGHTS_ALL +#define UVWASI__RIGHTS_BLOCK_DEVICE_INHERITING UVWASI__RIGHTS_ALL + +#define UVWASI__RIGHTS_CHARACTER_DEVICE_BASE UVWASI__RIGHTS_ALL +#define UVWASI__RIGHTS_CHARACTER_DEVICE_INHERITING UVWASI__RIGHTS_ALL + +#define UVWASI__RIGHTS_REGULAR_FILE_BASE (UVWASI_RIGHT_FD_DATASYNC | \ + UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_SEEK | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_TELL | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_FD_ALLOCATE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES |\ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_REGULAR_FILE_INHERITING 0 + +#define UVWASI__RIGHTS_DIRECTORY_BASE (UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_PATH_CREATE_DIRECTORY | \ + UVWASI_RIGHT_PATH_CREATE_FILE | \ + UVWASI_RIGHT_PATH_LINK_SOURCE | \ + UVWASI_RIGHT_PATH_LINK_TARGET | \ + UVWASI_RIGHT_PATH_OPEN | \ + UVWASI_RIGHT_FD_READDIR | \ + UVWASI_RIGHT_PATH_READLINK | \ + UVWASI_RIGHT_PATH_RENAME_SOURCE | \ + UVWASI_RIGHT_PATH_RENAME_TARGET | \ + UVWASI_RIGHT_PATH_FILESTAT_GET | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_PATH_SYMLINK | \ + UVWASI_RIGHT_PATH_UNLINK_FILE | \ + UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_DIRECTORY_INHERITING (UVWASI__RIGHTS_DIRECTORY_BASE | \ + UVWASI__RIGHTS_REGULAR_FILE_BASE) + +#define UVWASI__RIGHTS_SOCKET_BASE (UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_POLL_FD_READWRITE | \ + UVWASI_RIGHT_SOCK_SHUTDOWN) +#define UVWASI__RIGHTS_SOCKET_INHERITING UVWASI__RIGHTS_ALL; + +#define UVWASI__RIGHTS_TTY_BASE (UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_TTY_INHERITING 0 + +static uvwasi_errno_t uvwasi__get_type_and_rights(uv_file fd, + int flags, + uvwasi_filetype_t* type, + uvwasi_rights_t* rights_base, + uvwasi_rights_t* rights_inheriting) { + uv_fs_t req; + uvwasi_filetype_t filetype; + int read_or_write_only; + int r; + + r = uv_fs_fstat(NULL, &req, fd, NULL); + filetype = uvwasi__stat_to_filetype(&req.statbuf); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + *type = filetype; + switch (filetype) { + case UVWASI_FILETYPE_REGULAR_FILE: + *rights_base = UVWASI__RIGHTS_REGULAR_FILE_BASE; + *rights_inheriting = UVWASI__RIGHTS_REGULAR_FILE_INHERITING; + break; + + case UVWASI_FILETYPE_DIRECTORY: + *rights_base = UVWASI__RIGHTS_DIRECTORY_BASE; + *rights_inheriting = UVWASI__RIGHTS_DIRECTORY_INHERITING; + break; + + /* uvwasi__stat_to_filetype() cannot differentiate socket types. It only + returns UVWASI_FILETYPE_SOCKET_STREAM. */ + case UVWASI_FILETYPE_SOCKET_STREAM: + if (uv_guess_handle(fd) == UV_UDP) + *type = UVWASI_FILETYPE_SOCKET_DGRAM; + + *rights_base = UVWASI__RIGHTS_SOCKET_BASE; + *rights_inheriting = UVWASI__RIGHTS_SOCKET_INHERITING; + break; + + case UVWASI_FILETYPE_CHARACTER_DEVICE: + if (uv_guess_handle(fd) == UV_TTY) { + *rights_base = UVWASI__RIGHTS_TTY_BASE; + *rights_inheriting = UVWASI__RIGHTS_TTY_INHERITING; + } else { + *rights_base = UVWASI__RIGHTS_CHARACTER_DEVICE_BASE; + *rights_inheriting = UVWASI__RIGHTS_CHARACTER_DEVICE_INHERITING; + } + break; + + case UVWASI_FILETYPE_BLOCK_DEVICE: + *rights_base = UVWASI__RIGHTS_BLOCK_DEVICE_BASE; + *rights_inheriting = UVWASI__RIGHTS_BLOCK_DEVICE_INHERITING; + break; + + default: + *rights_base = 0; + *rights_inheriting = 0; + } + + if (*type == UVWASI_FILETYPE_UNKNOWN) + return UVWASI_EINVAL; + + /* Disable read/write bits depending on access mode. */ + read_or_write_only = flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + + if (read_or_write_only == UV_FS_O_RDONLY) + *rights_base &= ~UVWASI_RIGHT_FD_WRITE; + else if (read_or_write_only == UV_FS_O_WRONLY) + *rights_base &= ~UVWASI_RIGHT_FD_READ; + + return UVWASI_ESUCCESS; +} + + +static uvwasi_errno_t uvwasi__fd_table_insert(struct uvwasi_fd_table_t* table, + uv_file fd, + const char* mapped_path, + const char* real_path, + uvwasi_filetype_t type, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting, + int preopen, + struct uvwasi_fd_wrap_t** wrap) { + struct uvwasi_fd_wrap_t* entry; + struct uvwasi_fd_wrap_t* new_fds; + uint32_t new_size; + int index; + uint32_t i; + + /* Check that there is room for a new item. If there isn't, grow the table. */ + if (table->used >= table->size) { + new_size = table->size * 2; + new_fds = realloc(table->fds, new_size * sizeof(*new_fds)); + if (new_fds == NULL) + return UVWASI_ENOMEM; + + for (i = table->size; i < new_size; ++i) + new_fds[i].valid = 0; + + index = table->size; + table->fds = new_fds; + table->size = new_size; + } else { + /* The table is big enough, so find an empty slot for the new data. */ + index = -1; + for (i = 0; i < table->size; ++i) { + if (table->fds[i].valid != 1) { + index = i; + break; + } + } + + /* index should never be -1. */ + if (index == -1) + return UVWASI_ENOSPC; + } + + entry = &table->fds[index]; + entry->id = index; + entry->fd = fd; + strcpy(entry->path, mapped_path); + strcpy(entry->real_path, real_path); + entry->type = type; + entry->rights_base = rights_base; + entry->rights_inheriting = rights_inheriting; + entry->preopen = preopen; + entry->valid = 1; + table->used++; + + if (wrap != NULL) + *wrap = entry; + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_init(struct uvwasi_fd_table_t* table, + uint32_t init_size) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_filetype_t type; + uvwasi_rights_t base; + uvwasi_rights_t inheriting; + uvwasi_errno_t err; + uvwasi_fd_t i; + + /* Require an initial size of at least three to store the stdio FDs. */ + if (table == NULL || init_size < 3) + return UVWASI_EINVAL; + + table->used = 0; + table->size = init_size; + table->fds = calloc(init_size, sizeof(struct uvwasi_fd_wrap_t)); + + if (table->fds == NULL) + return UVWASI_ENOMEM; + + /* Create the stdio FDs. */ + for (i = 0; i < 3; ++i) { + err = uvwasi__get_type_and_rights(i, + UV_FS_O_RDWR, + &type, + &base, + &inheriting); + if (err != UVWASI_ESUCCESS) + goto error_exit; + + err = uvwasi__fd_table_insert(table, + i, + "", + "", + type, + base, + inheriting, + 0, + &wrap); + if (err != UVWASI_ESUCCESS) + goto error_exit; + + if (wrap->id != i || wrap->id != (uvwasi_fd_t) wrap->fd) { + err = UVWASI_EBADF; + goto error_exit; + } + } + + return UVWASI_ESUCCESS; +error_exit: + uvwasi_fd_table_free(table); + return err; +} + + +void uvwasi_fd_table_free(struct uvwasi_fd_table_t* table) { + if (table == NULL) + return; + + free(table->fds); + table->fds = NULL; + table->size = 0; + table->used = 0; +} + + +uvwasi_errno_t uvwasi_fd_table_insert_preopen(struct uvwasi_fd_table_t* table, + const uv_file fd, + const char* path, + const char* real_path) { + uvwasi_filetype_t type; + uvwasi_rights_t base; + uvwasi_rights_t inheriting; + uvwasi_errno_t err; + + if (table == NULL || path == NULL || real_path == NULL) + return UVWASI_EINVAL; + + err = uvwasi__get_type_and_rights(fd, 0, &type, &base, &inheriting); + if (err != UVWASI_ESUCCESS) + return err; + + if (type != UVWASI_FILETYPE_DIRECTORY) + return UVWASI_ENOTDIR; + + err = uvwasi__fd_table_insert(table, + fd, + path, + real_path, + UVWASI_FILETYPE_DIRECTORY, + UVWASI__RIGHTS_DIRECTORY_BASE, + UVWASI__RIGHTS_DIRECTORY_INHERITING, + 1, + NULL); + if (err != UVWASI_ESUCCESS) + return err; + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_insert_fd(struct uvwasi_fd_table_t* table, + const uv_file fd, + const int flags, + const char* path, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting, + struct uvwasi_fd_wrap_t* wrap) { + struct uvwasi_fd_wrap_t* fd_wrap; + uvwasi_filetype_t type; + uvwasi_rights_t max_base; + uvwasi_rights_t max_inheriting; + uvwasi_errno_t r; + + if (table == NULL || path == NULL || wrap == NULL) + return UVWASI_EINVAL; + + r = uvwasi__get_type_and_rights(fd, flags, &type, &max_base, &max_inheriting); + if (r != UVWASI_ESUCCESS) + return r; + + r = uvwasi__fd_table_insert(table, + fd, + path, + path, + type, + rights_base & max_base, + rights_inheriting & max_inheriting, + 0, + &fd_wrap); + if (r != UVWASI_ESUCCESS) + return r; + + *wrap = *fd_wrap; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_get(const struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id, + struct uvwasi_fd_wrap_t** wrap, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting) { + struct uvwasi_fd_wrap_t* entry; + + if (table == NULL || wrap == NULL) + return UVWASI_EINVAL; + if (id >= table->size) + return UVWASI_EBADF; + + entry = &table->fds[id]; + + if (entry->valid != 1 || entry->id != id) + return UVWASI_EBADF; + + /* Validate that the fd has the necessary rights. */ + if ((~entry->rights_base & rights_base) != 0 || + (~entry->rights_inheriting & rights_inheriting) != 0) + return UVWASI_ENOTCAPABLE; + + *wrap = entry; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_remove(struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id) { + struct uvwasi_fd_wrap_t* entry; + + if (table == NULL) + return UVWASI_EINVAL; + if (id >= table->size) + return UVWASI_EBADF; + + entry = &table->fds[id]; + + if (entry->valid != 1 || entry->id != id) + return UVWASI_EBADF; + + entry->valid = 0; + table->used--; + return UVWASI_ESUCCESS; +} diff --git a/deps/uvwasi/src/uv_mapping.c b/deps/uvwasi/src/uv_mapping.c new file mode 100644 index 00000000000000..846dcedbeb6b4f --- /dev/null +++ b/deps/uvwasi/src/uv_mapping.c @@ -0,0 +1,243 @@ +#include + +#ifndef _WIN32 +# include +#endif /* _WIN32 */ + +#include "uv.h" +#include "wasi_types.h" +#include "uv_mapping.h" + +#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif + +#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +#if !defined(S_ISCHR) && defined(S_IFMT) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif + +#if !defined(S_ISLNK) && defined(S_IFMT) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif + + +uvwasi_errno_t uvwasi__translate_uv_error(int err) { + switch (err) { + case UV_E2BIG: return UVWASI_E2BIG; + case UV_EACCES: return UVWASI_EACCES; + case UV_EADDRINUSE: return UVWASI_EADDRINUSE; + case UV_EADDRNOTAVAIL: return UVWASI_EADDRNOTAVAIL; + case UV_EAFNOSUPPORT: return UVWASI_EAFNOSUPPORT; + case UV_EAGAIN: return UVWASI_EAGAIN; + case UV_EALREADY: return UVWASI_EALREADY; + case UV_EBADF: return UVWASI_EBADF; + case UV_EBUSY: return UVWASI_EBUSY; + case UV_ECANCELED: return UVWASI_ECANCELED; + case UV_ECONNABORTED: return UVWASI_ECONNABORTED; + case UV_ECONNREFUSED: return UVWASI_ECONNREFUSED; + case UV_ECONNRESET: return UVWASI_ECONNRESET; + case UV_EDESTADDRREQ: return UVWASI_EDESTADDRREQ; + case UV_EEXIST: return UVWASI_EEXIST; + case UV_EFAULT: return UVWASI_EFAULT; + case UV_EFBIG: return UVWASI_EFBIG; + case UV_EHOSTUNREACH: return UVWASI_EHOSTUNREACH; + case UV_EINTR: return UVWASI_EINTR; + case UV_EINVAL: return UVWASI_EINVAL; + case UV_EIO: return UVWASI_EIO; + case UV_EISCONN: return UVWASI_EISCONN; + case UV_EISDIR: return UVWASI_EISDIR; + case UV_ELOOP: return UVWASI_ELOOP; + case UV_EMFILE: return UVWASI_EMFILE; + case UV_EMLINK: return UVWASI_EMLINK; + case UV_EMSGSIZE: return UVWASI_EMSGSIZE; + case UV_ENAMETOOLONG: return UVWASI_ENAMETOOLONG; + case UV_ENETDOWN: return UVWASI_ENETDOWN; + case UV_ENETUNREACH: return UVWASI_ENETUNREACH; + case UV_ENFILE: return UVWASI_ENFILE; + case UV_ENOBUFS: return UVWASI_ENOBUFS; + case UV_ENODEV: return UVWASI_ENODEV; + case UV_ENOENT: return UVWASI_ENOENT; + case UV_ENOMEM: return UVWASI_ENOMEM; + case UV_ENOPROTOOPT: return UVWASI_ENOPROTOOPT; + case UV_ENOSPC: return UVWASI_ENOSPC; + case UV_ENOSYS: return UVWASI_ENOSYS; + case UV_ENOTCONN: return UVWASI_ENOTCONN; + case UV_ENOTDIR: return UVWASI_ENOTDIR; + /* On at least some AIX machines, ENOTEMPTY and EEXIST are equivalent. */ +#if ENOTEMPTY != EEXIST + case UV_ENOTEMPTY: return UVWASI_ENOTEMPTY; +#endif /* ENOTEMPTY != EEXIST */ + case UV_ENOTSOCK: return UVWASI_ENOTSOCK; + case UV_ENOTSUP: return UVWASI_ENOTSUP; + case UV_ENXIO: return UVWASI_ENXIO; + case UV_EPERM: return UVWASI_EPERM; + case UV_EPIPE: return UVWASI_EPIPE; + case UV_EPROTO: return UVWASI_EPROTO; + case UV_EPROTONOSUPPORT: return UVWASI_EPROTONOSUPPORT; + case UV_EPROTOTYPE: return UVWASI_EPROTOTYPE; + case UV_ERANGE: return UVWASI_ERANGE; + case UV_EROFS: return UVWASI_EROFS; + case UV_ESPIPE: return UVWASI_ESPIPE; + case UV_ESRCH: return UVWASI_ESRCH; + case UV_ETIMEDOUT: return UVWASI_ETIMEDOUT; + case UV_ETXTBSY: return UVWASI_ETXTBSY; + case UV_EXDEV: return UVWASI_EXDEV; + case 0: return UVWASI_ESUCCESS; + /* The following libuv error codes have no corresponding WASI error code: + UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS, UV_EAI_BADHINTS, + UV_EAI_CANCELED, UV_EAI_FAIL, UV_EAI_FAMILY, UV_EAI_MEMORY, + UV_EAI_NODATA, UV_EAI_NONAME, UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, + UV_EAI_SERVICE, UV_EAI_SOCKTYPE, UV_ECHARSET, UV_ENONET, UV_EOF, + UV_ESHUTDOWN, UV_UNKNOWN + */ + default: + /* libuv errors are < 0 */ + if (err > 0) + return err; + + return UVWASI_ENOSYS; + } +} + + +int uvwasi__translate_to_uv_signal(uvwasi_signal_t sig) { + switch (sig) { +#ifdef SIGABRT + case UVWASI_SIGABRT: return SIGABRT; +#endif +#ifdef SIGALRM + case UVWASI_SIGALRM: return SIGALRM; +#endif +#ifdef SIGBUS + case UVWASI_SIGBUS: return SIGBUS; +#endif +#ifdef SIGCHLD + case UVWASI_SIGCHLD: return SIGCHLD; +#endif +#ifdef SIGCONT + case UVWASI_SIGCONT: return SIGCONT; +#endif +#ifdef SIGFPE + case UVWASI_SIGFPE: return SIGFPE; +#endif +#ifdef SIGHUP + case UVWASI_SIGHUP: return SIGHUP; +#endif +#ifdef SIGILL + case UVWASI_SIGILL: return SIGILL; +#endif +#ifdef SIGINT + case UVWASI_SIGINT: return SIGINT; +#endif +#ifdef SIGKILL + case UVWASI_SIGKILL: return SIGKILL; +#endif +#ifdef SIGPIPE + case UVWASI_SIGPIPE: return SIGPIPE; +#endif +#ifdef SIGQUIT + case UVWASI_SIGQUIT: return SIGQUIT; +#endif +#ifdef SIGSEGV + case UVWASI_SIGSEGV: return SIGSEGV; +#endif +#ifdef SIGSTOP + case UVWASI_SIGSTOP: return SIGSTOP; +#endif +#ifdef SIGSYS + case UVWASI_SIGSYS: return SIGSYS; +#endif +#ifdef SIGTERM + case UVWASI_SIGTERM: return SIGTERM; +#endif +#ifdef SIGTRAP + case UVWASI_SIGTRAP: return SIGTRAP; +#endif +#ifdef SIGTSTP + case UVWASI_SIGTSTP: return SIGTSTP; +#endif +#ifdef SIGTTIN + case UVWASI_SIGTTIN: return SIGTTIN; +#endif +#ifdef SIGTTOU + case UVWASI_SIGTTOU: return SIGTTOU; +#endif +#ifdef SIGURG + case UVWASI_SIGURG: return SIGURG; +#endif +#ifdef SIGUSR1 + case UVWASI_SIGUSR1: return SIGUSR1; +#endif +#ifdef SIGUSR2 + case UVWASI_SIGUSR2: return SIGUSR2; +#endif +#ifdef SIGVTALRM + case UVWASI_SIGVTALRM: return SIGVTALRM; +#endif +#ifdef SIGXCPU + case UVWASI_SIGXCPU: return SIGXCPU; +#endif +#ifdef SIGXFSZ + case UVWASI_SIGXFSZ: return SIGXFSZ; +#endif + default: return -1; + } +} + + +uvwasi_timestamp_t uvwasi__timespec_to_timestamp(const uv_timespec_t* ts) { + /* TODO(cjihrig): Handle overflow. */ + return (uvwasi_timestamp_t) ts->tv_sec * NANOS_PER_SEC + ts->tv_nsec; +} + + +uvwasi_filetype_t uvwasi__stat_to_filetype(const uv_stat_t* stat) { + uint64_t mode; + + mode = stat->st_mode; + + if (S_ISREG(mode)) + return UVWASI_FILETYPE_REGULAR_FILE; + + if (S_ISDIR(mode)) + return UVWASI_FILETYPE_DIRECTORY; + + if (S_ISCHR(mode)) + return UVWASI_FILETYPE_CHARACTER_DEVICE; + + if (S_ISLNK(mode)) + return UVWASI_FILETYPE_SYMBOLIC_LINK; + +#ifdef S_ISSOCK + if (S_ISSOCK(mode)) + return UVWASI_FILETYPE_SOCKET_STREAM; +#endif /* S_ISSOCK */ + +#ifdef S_ISFIFO + if (S_ISFIFO(mode)) + return UVWASI_FILETYPE_SOCKET_STREAM; +#endif /* S_ISFIFO */ + +#ifdef S_ISBLK + if (S_ISBLK(mode)) + return UVWASI_FILETYPE_BLOCK_DEVICE; +#endif /* S_ISBLK */ + + return UVWASI_FILETYPE_UNKNOWN; +} + + +void uvwasi__stat_to_filestat(const uv_stat_t* stat, uvwasi_filestat_t* fs) { + fs->st_dev = stat->st_dev; + fs->st_ino = stat->st_ino; + fs->st_nlink = stat->st_nlink; + fs->st_size = stat->st_size; + fs->st_filetype = uvwasi__stat_to_filetype(stat); + fs->st_atim = uvwasi__timespec_to_timestamp(&stat->st_atim); + fs->st_mtim = uvwasi__timespec_to_timestamp(&stat->st_mtim); + fs->st_ctim = uvwasi__timespec_to_timestamp(&stat->st_ctim); +} diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c new file mode 100644 index 00000000000000..39a94b6d217c52 --- /dev/null +++ b/deps/uvwasi/src/uvwasi.c @@ -0,0 +1,1918 @@ +#include +#include + +#ifndef _WIN32 +# include +# include +# include +# include +# include +# define SLASH '/' +# define SLASH_STR "/" +# define IS_SLASH(c) ((c) == '/') +#else +# define SLASH '\\' +# define SLASH_STR "\\" +# define IS_SLASH(c) ((c) == '/' || (c) == '\\') +#endif /* _WIN32 */ + +#define UVWASI__READDIR_NUM_ENTRIES 1 + +#include "uvwasi.h" +#include "uv.h" +#include "uv_mapping.h" +#include "fd_table.h" +#include "clocks.h" + + +static int uvwasi__is_absolute_path(const char* path, size_t path_len) { + /* TODO(cjihrig): Add a Windows implementation. */ + return path != NULL && path_len > 0 && path[0] == SLASH; +} + + +static uvwasi_errno_t uvwasi__resolve_path(const struct uvwasi_fd_wrap_t* fd, + const char* path, + size_t path_len, + char* resolved_path, + uvwasi_lookupflags_t flags) { + /* TODO(cjihrig): path_len is treated as a size. Need to verify if path_len is + really a string length or a size. Also need to verify if it is null + terminated. */ + uv_fs_t realpath_req; + uvwasi_errno_t err; + char* abs_path; + char* tok; + char* ptr; + int realpath_size; + int abs_size; + int input_is_absolute; + int r; +#ifdef _WIN32 + int i; +#endif /* _WIN32 */ + + err = UVWASI_ESUCCESS; + input_is_absolute = uvwasi__is_absolute_path(path, path_len); + + if (1 == input_is_absolute) { + /* TODO(cjihrig): Revisit this. Copying is probably not necessary here. */ + abs_size = path_len; + abs_path = malloc(abs_size); + if (abs_path == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + memcpy(abs_path, path, abs_size); + } else { + /* Resolve the relative path to fd's real path. */ + abs_size = path_len + strlen(fd->real_path) + 2; + abs_path = malloc(abs_size); + if (abs_path == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + r = snprintf(abs_path, abs_size, "%s/%s", fd->real_path, path); + if (r <= 0) { + err = uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + goto exit; + } + } + +#ifdef _WIN32 + /* On Windows, convert slashes to backslashes. */ + for (i = 0; i < abs_size; ++i) { + if (abs_path[i] == '/') + abs_path[i] = SLASH; + } +#endif /* _WIN32 */ + + ptr = resolved_path; + tok = strtok(abs_path, SLASH_STR); + for (; tok != NULL; tok = strtok(NULL, SLASH_STR)) { + if (0 == strcmp(tok, ".")) + continue; + + if (0 == strcmp(tok, "..")) { + while (*ptr != SLASH && ptr != resolved_path) + ptr--; + *ptr = '\0'; + continue; + } + +#ifdef _WIN32 + /* On Windows, prevent a leading slash in the path. */ + if (ptr == resolved_path) + r = sprintf(ptr, "%s", tok); + else +#endif /* _WIN32 */ + r = sprintf(ptr, "%c%s", SLASH, tok); + + if (r < 1) { /* At least one character should have been written. */ + err = uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + goto exit; + } + + ptr += r; + } + + if ((flags & UVWASI_LOOKUP_SYMLINK_FOLLOW) == UVWASI_LOOKUP_SYMLINK_FOLLOW) { + r = uv_fs_realpath(NULL, &realpath_req, resolved_path, NULL); + if (r == 0) { + realpath_size = strlen(realpath_req.ptr) + 1; + if (realpath_size > PATH_MAX_BYTES) { + err = UVWASI_ENOBUFS; + uv_fs_req_cleanup(&realpath_req); + goto exit; + } + + memcpy(resolved_path, realpath_req.ptr, realpath_size); + } else if (r != UV_ENOENT) { + /* Report errors except ENOENT. */ + err = uvwasi__translate_uv_error(r); + uv_fs_req_cleanup(&realpath_req); + goto exit; + } + + uv_fs_req_cleanup(&realpath_req); + } + + /* Verify that the resolved path is still in the sandbox. */ + if (resolved_path != strstr(resolved_path, fd->real_path)) { + err = UVWASI_ENOTCAPABLE; + goto exit; + } + +exit: + free(abs_path); + return err; +} + + +static uvwasi_errno_t uvwasi__lseek(uv_file fd, + uvwasi_filedelta_t offset, + uvwasi_whence_t whence, + uvwasi_filesize_t* newoffset) { + int real_whence; + + if (whence == UVWASI_WHENCE_CUR) + real_whence = SEEK_CUR; + else if (whence == UVWASI_WHENCE_END) + real_whence = SEEK_END; + else if (whence == UVWASI_WHENCE_SET) + real_whence = SEEK_SET; + else + return UVWASI_EINVAL; + +#ifdef _WIN32 + int64_t r; + + r = _lseeki64(fd, offset, real_whence); + if (-1L == r) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); +#else + off_t r; + + r = lseek(fd, offset, real_whence); + if ((off_t) -1 == r) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); +#endif /* _WIN32 */ + + *newoffset = r; + return UVWASI_ESUCCESS; +} + + +static uvwasi_errno_t uvwasi__setup_iovs(uv_buf_t** buffers, + const uvwasi_iovec_t* iovs, + size_t iovs_len) { + uv_buf_t* bufs; + size_t i; + + bufs = malloc(iovs_len * sizeof(*bufs)); + if (bufs == NULL) + return UVWASI_ENOMEM; + + for (i = 0; i < iovs_len; ++i) + bufs[i] = uv_buf_init(iovs[i].buf, iovs[i].buf_len); + + *buffers = bufs; + return UVWASI_ESUCCESS; +} + + +static uvwasi_errno_t uvwasi__setup_ciovs(uv_buf_t** buffers, + const uvwasi_ciovec_t* iovs, + size_t iovs_len) { + uv_buf_t* bufs; + size_t i; + + bufs = malloc(iovs_len * sizeof(*bufs)); + if (bufs == NULL) + return UVWASI_ENOMEM; + + for (i = 0; i < iovs_len; ++i) + bufs[i] = uv_buf_init((char*)iovs[i].buf, iovs[i].buf_len); + + *buffers = bufs; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_init(uvwasi_t* uvwasi, uvwasi_options_t* options) { + uv_fs_t realpath_req; + uv_fs_t open_req; + uvwasi_errno_t err; + size_t args_size; + size_t size; + size_t offset; + size_t env_count; + size_t env_buf_size; + size_t i; + int r; + + if (uvwasi == NULL || options == NULL || options->fd_table_size == 0) + return UVWASI_EINVAL; + + uvwasi->argv_buf = NULL; + uvwasi->argv = NULL; + uvwasi->env_buf = NULL; + uvwasi->env = NULL; + uvwasi->fds.fds = NULL; + + args_size = 0; + for (i = 0; i < options->argc; ++i) + args_size += strlen(options->argv[i]) + 1; + + uvwasi->argc = options->argc; + uvwasi->argv_buf_size = args_size; + + if (args_size > 0) { + uvwasi->argv_buf = malloc(args_size); + if (uvwasi->argv_buf == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + uvwasi->argv = calloc(options->argc, sizeof(char*)); + if (uvwasi->argv == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + offset = 0; + for (i = 0; i < options->argc; ++i) { + size = strlen(options->argv[i]) + 1; + memcpy(uvwasi->argv_buf + offset, options->argv[i], size); + uvwasi->argv[i] = uvwasi->argv_buf + offset; + offset += size; + } + } + + env_count = 0; + env_buf_size = 0; + if (options->envp != NULL) { + while (options->envp[env_count] != NULL) { + env_buf_size += strlen(options->envp[env_count]) + 1; + env_count++; + } + } + + uvwasi->envc = env_count; + uvwasi->env_buf_size = env_buf_size; + + if (env_buf_size > 0) { + uvwasi->env_buf = malloc(env_buf_size); + if (uvwasi->env_buf == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + uvwasi->env = calloc(env_count, sizeof(char*)); + if (uvwasi->env == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + offset = 0; + for (i = 0; i < env_count; ++i) { + size = strlen(options->envp[i]) + 1; + memcpy(uvwasi->env_buf + offset, options->envp[i], size); + uvwasi->env[i] = uvwasi->env_buf + offset; + offset += size; + } + } + + for (i = 0; i < options->preopenc; ++i) { + if (options->preopens[i].real_path == NULL || + options->preopens[i].mapped_path == NULL) { + err = UVWASI_EINVAL; + goto exit; + } + } + + err = uvwasi_fd_table_init(&uvwasi->fds, options->fd_table_size); + if (err != UVWASI_ESUCCESS) + goto exit; + + for (i = 0; i < options->preopenc; ++i) { + r = uv_fs_realpath(NULL, + &realpath_req, + options->preopens[i].real_path, + NULL); + if (r != 0) { + err = uvwasi__translate_uv_error(r); + uv_fs_req_cleanup(&realpath_req); + goto exit; + } + + r = uv_fs_open(NULL, &open_req, realpath_req.ptr, 0, 0666, NULL); + if (r < 0) { + err = uvwasi__translate_uv_error(r); + uv_fs_req_cleanup(&realpath_req); + uv_fs_req_cleanup(&open_req); + goto exit; + } + + err = uvwasi_fd_table_insert_preopen(&uvwasi->fds, + open_req.result, + options->preopens[i].mapped_path, + realpath_req.ptr); + uv_fs_req_cleanup(&realpath_req); + uv_fs_req_cleanup(&open_req); + + if (err != UVWASI_ESUCCESS) + goto exit; + } + + return UVWASI_ESUCCESS; + +exit: + uvwasi_destroy(uvwasi); + return err; +} + + +void uvwasi_destroy(uvwasi_t* uvwasi) { + if (uvwasi == NULL) + return; + + uvwasi_fd_table_free(&uvwasi->fds); + free(uvwasi->argv_buf); + free(uvwasi->argv); + free(uvwasi->env_buf); + free(uvwasi->env); + uvwasi->argv_buf = NULL; + uvwasi->argv = NULL; + uvwasi->env_buf = NULL; + uvwasi->env = NULL; +} + + +uvwasi_errno_t uvwasi_embedder_remap_fd(uvwasi_t* uvwasi, + const uvwasi_fd_t fd, + uv_file new_host_fd) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + wrap->fd = new_host_fd; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_args_get(uvwasi_t* uvwasi, char** argv, char* argv_buf) { + size_t i; + + if (uvwasi == NULL || argv == NULL || argv_buf == NULL) + return UVWASI_EINVAL; + + for (i = 0; i < uvwasi->argc; ++i) { + argv[i] = argv_buf + (uvwasi->argv[i] - uvwasi->argv_buf); + } + + memcpy(argv_buf, uvwasi->argv_buf, uvwasi->argv_buf_size); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_args_sizes_get(uvwasi_t* uvwasi, + size_t* argc, + size_t* argv_buf_size) { + if (uvwasi == NULL || argc == NULL || argv_buf_size == NULL) + return UVWASI_EINVAL; + + *argc = uvwasi->argc; + *argv_buf_size = uvwasi->argv_buf_size; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_clock_res_get(uvwasi_t* uvwasi, + uvwasi_clockid_t clock_id, + uvwasi_timestamp_t* resolution) { + if (uvwasi == NULL || resolution == NULL) + return UVWASI_EINVAL; + + switch (clock_id) { + case UVWASI_CLOCK_MONOTONIC: + case UVWASI_CLOCK_REALTIME: + *resolution = 1; /* Nanosecond precision. */ + return UVWASI_ESUCCESS; + case UVWASI_CLOCK_PROCESS_CPUTIME_ID: + return uvwasi__clock_getres_process_cputime(resolution); + case UVWASI_CLOCK_THREAD_CPUTIME_ID: + return uvwasi__clock_getres_thread_cputime(resolution); + default: + return UVWASI_EINVAL; + } +} + + +uvwasi_errno_t uvwasi_clock_time_get(uvwasi_t* uvwasi, + uvwasi_clockid_t clock_id, + uvwasi_timestamp_t precision, + uvwasi_timestamp_t* time) { + if (uvwasi == NULL || time == NULL) + return UVWASI_EINVAL; + + switch (clock_id) { + case UVWASI_CLOCK_MONOTONIC: + *time = uv_hrtime(); + return UVWASI_ESUCCESS; + case UVWASI_CLOCK_REALTIME: + return uvwasi__clock_gettime_realtime(time); + case UVWASI_CLOCK_PROCESS_CPUTIME_ID: + return uvwasi__clock_gettime_process_cputime(time); + case UVWASI_CLOCK_THREAD_CPUTIME_ID: + return uvwasi__clock_gettime_thread_cputime(time); + default: + return UVWASI_EINVAL; + } +} + + +uvwasi_errno_t uvwasi_environ_get(uvwasi_t* uvwasi, + char** environment, + char* environ_buf) { + size_t i; + + if (uvwasi == NULL || environment == NULL || environ_buf == NULL) + return UVWASI_EINVAL; + + for (i = 0; i < uvwasi->envc; ++i) { + environment[i] = environ_buf + (uvwasi->env[i] - uvwasi->env_buf); + } + + memcpy(environ_buf, uvwasi->env_buf, uvwasi->env_buf_size); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_environ_sizes_get(uvwasi_t* uvwasi, + size_t* environ_count, + size_t* environ_buf_size) { + if (uvwasi == NULL || environ_count == NULL || environ_buf_size == NULL) + return UVWASI_EINVAL; + + *environ_count = uvwasi->envc; + *environ_buf_size = uvwasi->env_buf_size; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_advise(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t offset, + uvwasi_filesize_t len, + uvwasi_advice_t advice) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; +#ifdef POSIX_FADV_NORMAL + int mapped_advice; + int r; +#endif /* POSIX_FADV_NORMAL */ + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + switch (advice) { + case UVWASI_ADVICE_DONTNEED: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_DONTNEED; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_NOREUSE: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_NOREUSE; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_NORMAL: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_NORMAL; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_RANDOM: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_RANDOM; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_SEQUENTIAL: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_SEQUENTIAL; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_WILLNEED: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_WILLNEED; +#endif /* POSIX_FADV_NORMAL */ + break; + default: + return UVWASI_EINVAL; + } + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_ADVISE, 0); + if (err != UVWASI_ESUCCESS) + return err; + +#ifdef POSIX_FADV_NORMAL + r = posix_fadvise(wrap->fd, offset, len, mapped_advice); + if (r != 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(r)); +#endif /* POSIX_FADV_NORMAL */ + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_allocate(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t offset, + uvwasi_filesize_t len) { +#if !defined(__POSIX__) + uv_fs_t req; + uint64_t st_size; +#endif /* !__POSIX__ */ + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_ALLOCATE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* Try to use posix_fallocate(). If that's not an option, fall back to the + race condition prone combination of fstat() + ftruncate(). */ +#if defined(__POSIX__) + r = posix_fallocate(wrap->fd, offset, len); + if (r != 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(r)); +#else + r = uv_fs_fstat(NULL, &req, wrap->fd, NULL); + st_size = req.statbuf.st_size; + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + if (st_size < offset + len) { + r = uv_fs_ftruncate(NULL, &req, wrap->fd, offset + len, NULL); + if (r != 0) + return uvwasi__translate_uv_error(r); + } +#endif /* __POSIX__ */ + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_close(uvwasi_t* uvwasi, uvwasi_fd_t fd) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_close(NULL, &req, wrap->fd, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return uvwasi_fd_table_remove(&uvwasi->fds, fd); +} + + +uvwasi_errno_t uvwasi_fd_datasync(uvwasi_t* uvwasi, uvwasi_fd_t fd) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_DATASYNC, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_fdatasync(NULL, &req, wrap->fd, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_fdstat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_fdstat_t* buf) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; +#ifndef _WIN32 + int r; +#endif + + if (uvwasi == NULL || buf == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + buf->fs_filetype = wrap->type; + buf->fs_rights_base = wrap->rights_base; + buf->fs_rights_inheriting = wrap->rights_inheriting; +#ifdef _WIN32 + buf->fs_flags = 0; /* TODO(cjihrig): Missing Windows support. */ +#else + r = fcntl(wrap->fd, F_GETFL); + if (r < 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + buf->fs_flags = r; +#endif /* _WIN32 */ + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_fdstat_set_flags(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_fdflags_t flags) { +#ifdef _WIN32 + /* TODO(cjihrig): Missing Windows support. */ + return UVWASI_ENOSYS; +#else + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + int mapped_flags; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + mapped_flags = 0; + + if ((flags & UVWASI_FDFLAG_APPEND) == UVWASI_FDFLAG_APPEND) + mapped_flags |= O_APPEND; + + if ((flags & UVWASI_FDFLAG_DSYNC) == UVWASI_FDFLAG_DSYNC) +#ifdef O_DSYNC + mapped_flags |= O_DSYNC; +#else + mapped_flags |= O_SYNC; +#endif /* O_DSYNC */ + + if ((flags & UVWASI_FDFLAG_NONBLOCK) == UVWASI_FDFLAG_NONBLOCK) + mapped_flags |= O_NONBLOCK; + + if ((flags & UVWASI_FDFLAG_RSYNC) == UVWASI_FDFLAG_RSYNC) +#ifdef O_RSYNC + mapped_flags |= O_RSYNC; +#else + mapped_flags |= O_SYNC; +#endif /* O_RSYNC */ + + if ((flags & UVWASI_FDFLAG_SYNC) == UVWASI_FDFLAG_SYNC) + mapped_flags |= O_SYNC; + + r = fcntl(wrap->fd, F_SETFL, mapped_flags); + if (r < 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + + return UVWASI_ESUCCESS; +#endif /* _WIN32 */ +} + + +uvwasi_errno_t uvwasi_fd_fdstat_set_rights(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_rights_t fs_rights_base, + uvwasi_rights_t fs_rights_inheriting + ) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* Check for attempts to add new permissions. */ + if ((fs_rights_base | wrap->rights_base) > wrap->rights_base) + return UVWASI_ENOTCAPABLE; + if ((fs_rights_inheriting | wrap->rights_inheriting) > + wrap->rights_inheriting) { + return UVWASI_ENOTCAPABLE; + } + + wrap->rights_base = fs_rights_base; + wrap->rights_inheriting = fs_rights_inheriting; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_filestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filestat_t* buf) { + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || buf == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_FILESTAT_GET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_fstat(NULL, &req, wrap->fd, NULL); + if (r != 0) { + uv_fs_req_cleanup(&req); + return uvwasi__translate_uv_error(r); + } + + uvwasi__stat_to_filestat(&req.statbuf, buf); + uv_fs_req_cleanup(&req); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_filestat_set_size(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t st_size) { + /* TODO(cjihrig): uv_fs_ftruncate() takes an int64_t. st_size is uint64_t. */ + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_ftruncate(NULL, &req, wrap->fd, st_size, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_filestat_set_times(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_timestamp_t st_atim, + uvwasi_timestamp_t st_mtim, + uvwasi_fstflags_t fst_flags) { + /* TODO(cjihrig): libuv does not currently support nanosecond precision. */ + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + if (fst_flags & ~(UVWASI_FILESTAT_SET_ATIM | UVWASI_FILESTAT_SET_ATIM_NOW | + UVWASI_FILESTAT_SET_MTIM | UVWASI_FILESTAT_SET_MTIM_NOW)) { + return UVWASI_EINVAL; + } + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* TODO(cjihrig): st_atim and st_mtim should not be unconditionally passed. */ + r = uv_fs_futime(NULL, &req, wrap->fd, st_atim, st_mtim, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_pread(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_iovec_t* iovs, + size_t iovs_len, + uvwasi_filesize_t offset, + size_t* nread) { + struct uvwasi_fd_wrap_t* wrap; + uv_buf_t* bufs; + uv_fs_t req; + uvwasi_errno_t err; + size_t uvread; + int r; + + if (uvwasi == NULL || iovs == NULL || nread == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_READ | UVWASI_RIGHT_FD_SEEK, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__setup_iovs(&bufs, iovs, iovs_len); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_read(NULL, &req, wrap->fd, bufs, iovs_len, offset, NULL); + uvread = req.result; + uv_fs_req_cleanup(&req); + free(bufs); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + *nread = uvread; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_prestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_prestat_t* buf) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL || buf == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + if (wrap->preopen != 1) + return UVWASI_EINVAL; + + buf->pr_type = UVWASI_PREOPENTYPE_DIR; + buf->u.dir.pr_name_len = strlen(wrap->path) + 1; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_prestat_dir_name(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + char* path, + size_t path_len) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + size_t size; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + if (wrap->preopen != 1) + return UVWASI_EBADF; + + size = strlen(wrap->path) + 1; + if (size > path_len) + return UVWASI_ENOBUFS; + + memcpy(path, wrap->path, size); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_pwrite(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_ciovec_t* iovs, + size_t iovs_len, + uvwasi_filesize_t offset, + size_t* nwritten) { + struct uvwasi_fd_wrap_t* wrap; + uv_buf_t* bufs; + uv_fs_t req; + uvwasi_errno_t err; + size_t uvwritten; + int r; + + if (uvwasi == NULL || iovs == NULL || nwritten == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_WRITE | UVWASI_RIGHT_FD_SEEK, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__setup_ciovs(&bufs, iovs, iovs_len); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_write(NULL, &req, wrap->fd, bufs, iovs_len, offset, NULL); + uvwritten = req.result; + uv_fs_req_cleanup(&req); + free(bufs); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + *nwritten = uvwritten; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_read(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_iovec_t* iovs, + size_t iovs_len, + size_t* nread) { + struct uvwasi_fd_wrap_t* wrap; + uv_buf_t* bufs; + uv_fs_t req; + uvwasi_errno_t err; + size_t uvread; + int r; + + if (uvwasi == NULL || iovs == NULL || nread == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_READ, 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__setup_iovs(&bufs, iovs, iovs_len); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_read(NULL, &req, wrap->fd, bufs, iovs_len, -1, NULL); + uvread = req.result; + uv_fs_req_cleanup(&req); + free(bufs); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + *nread = uvread; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + void* buf, + size_t buf_len, + uvwasi_dircookie_t cookie, + size_t* bufused) { + /* TODO(cjihrig): Support Windows where seekdir() and telldir() are used. */ + /* TODO(cjihrig): Avoid opening and closing the directory on each call. */ + struct uvwasi_fd_wrap_t* wrap; + uvwasi_dirent_t dirent; + uv_dirent_t dirents[UVWASI__READDIR_NUM_ENTRIES]; + uv_dir_t* dir; + uv_fs_t req; + uvwasi_errno_t err; + size_t name_len; + size_t available; + size_t size_to_cp; + long tell; + int i; + int r; + + if (uvwasi == NULL || buf == NULL || bufused == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_READDIR, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* Open the directory. */ + r = uv_fs_opendir(NULL, &req, wrap->real_path, NULL); + if (r != 0) + return uvwasi__translate_uv_error(r); + + /* Setup for reading the directory. */ + dir = req.ptr; + dir->dirents = dirents; + dir->nentries = UVWASI__READDIR_NUM_ENTRIES; + uv_fs_req_cleanup(&req); + +#ifndef _WIN32 + /* TODO(cjihrig): Need a Windows equivalent of this logic. */ + /* Seek to the proper location in the directory. */ + if (cookie != UVWASI_DIRCOOKIE_START) + seekdir(dir->dir, cookie); +#endif + + /* Read the directory entries into the provided buffer. */ + err = UVWASI_ESUCCESS; + *bufused = 0; + while (0 != (r = uv_fs_readdir(NULL, &req, dir, NULL))) { + if (r < 0) { + err = uvwasi__translate_uv_error(r); + uv_fs_req_cleanup(&req); + goto exit; + } + + for (i = 0; i < r; i++) { + /* TODO(cjihrig): This should probably be serialized to the buffer + consistently across platforms. In other words, d_next should always + be 8 bytes, d_ino should always be 8 bytes, d_namlen should always be + 4 bytes, and d_type should always be 1 byte. */ +#ifndef _WIN32 + tell = telldir(dir->dir); + if (tell < 0) { + err = uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + uv_fs_req_cleanup(&req); + goto exit; + } +#else + tell = 0; /* TODO(cjihrig): Need to support Windows. */ +#endif /* _WIN32 */ + + name_len = strlen(dirents[i].name); + dirent.d_next = (uvwasi_dircookie_t) tell; + /* TODO(cjihrig): Missing ino libuv (and Windows) support. fstat()? */ + dirent.d_ino = 0; + dirent.d_namlen = name_len; + + switch (dirents[i].type) { + case UV_DIRENT_FILE: + dirent.d_type = UVWASI_FILETYPE_REGULAR_FILE; + break; + case UV_DIRENT_DIR: + dirent.d_type = UVWASI_FILETYPE_DIRECTORY; + break; + case UV_DIRENT_SOCKET: + dirent.d_type = UVWASI_FILETYPE_SOCKET_STREAM; + break; + case UV_DIRENT_LINK: + dirent.d_type = UVWASI_FILETYPE_SYMBOLIC_LINK; + break; + case UV_DIRENT_CHAR: + dirent.d_type = UVWASI_FILETYPE_CHARACTER_DEVICE; + break; + case UV_DIRENT_BLOCK: + dirent.d_type = UVWASI_FILETYPE_BLOCK_DEVICE; + break; + case UV_DIRENT_FIFO: + case UV_DIRENT_UNKNOWN: + default: + dirent.d_type = UVWASI_FILETYPE_UNKNOWN; + break; + } + + /* Write dirent to the buffer. */ + available = buf_len - *bufused; + size_to_cp = sizeof(dirent) > available ? available : sizeof(dirent); + memcpy((char*)buf + *bufused, &dirent, size_to_cp); + *bufused += size_to_cp; + /* Write the entry name to the buffer. */ + available = buf_len - *bufused; + size_to_cp = name_len > available ? available : name_len; + memcpy((char*)buf + *bufused, &dirents[i].name, size_to_cp); + *bufused += size_to_cp; + } + + uv_fs_req_cleanup(&req); + + if (*bufused >= buf_len) + break; + } + +exit: + /* Close the directory. */ + r = uv_fs_closedir(NULL, &req, dir, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return err; +} + + +uvwasi_errno_t uvwasi_fd_renumber(uvwasi_t* uvwasi, + uvwasi_fd_t from, + uvwasi_fd_t to) { + struct uvwasi_fd_wrap_t* to_wrap; + struct uvwasi_fd_wrap_t* from_wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, from, &from_wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi_fd_table_get(&uvwasi->fds, to, &to_wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_close(NULL, &req, to_wrap->fd, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + memcpy(to_wrap, from_wrap, sizeof(*to_wrap)); + to_wrap->id = to; + + return uvwasi_fd_table_remove(&uvwasi->fds, from); +} + + +uvwasi_errno_t uvwasi_fd_seek(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filedelta_t offset, + uvwasi_whence_t whence, + uvwasi_filesize_t* newoffset) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL || newoffset == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_SEEK, 0); + if (err != UVWASI_ESUCCESS) + return err; + + return uvwasi__lseek(wrap->fd, offset, whence, newoffset); +} + + +uvwasi_errno_t uvwasi_fd_sync(uvwasi_t* uvwasi, uvwasi_fd_t fd) { + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_SYNC, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_fsync(NULL, &req, wrap->fd, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_tell(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t* offset) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL || offset == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_TELL, 0); + if (err != UVWASI_ESUCCESS) + return err; + + return uvwasi__lseek(wrap->fd, 0, UVWASI_WHENCE_CUR, offset); +} + + +uvwasi_errno_t uvwasi_fd_write(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_ciovec_t* iovs, + size_t iovs_len, + size_t* nwritten) { + struct uvwasi_fd_wrap_t* wrap; + uv_buf_t* bufs; + uv_fs_t req; + uvwasi_errno_t err; + size_t uvwritten; + int r; + + if (uvwasi == NULL || iovs == NULL || nwritten == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_WRITE, 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__setup_ciovs(&bufs, iovs, iovs_len); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_write(NULL, &req, wrap->fd, bufs, iovs_len, -1, NULL); + uvwritten = req.result; + uv_fs_req_cleanup(&req); + free(bufs); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + *nwritten = uvwritten; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_create_directory(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_CREATE_DIRECTORY, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_mkdir(NULL, &req, resolved_path, 0777, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_filestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_filestat_t* buf) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL || buf == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_FILESTAT_GET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, flags); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_stat(NULL, &req, resolved_path, NULL); + if (r != 0) { + uv_fs_req_cleanup(&req); + return uvwasi__translate_uv_error(r); + } + + uvwasi__stat_to_filestat(&req.statbuf, buf); + uv_fs_req_cleanup(&req); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_filestat_set_times(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_timestamp_t st_atim, + uvwasi_timestamp_t st_mtim, + uvwasi_fstflags_t fst_flags) { + /* TODO(cjihrig): libuv does not currently support nanosecond precision. */ + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + if (fst_flags & ~(UVWASI_FILESTAT_SET_ATIM | UVWASI_FILESTAT_SET_ATIM_NOW | + UVWASI_FILESTAT_SET_MTIM | UVWASI_FILESTAT_SET_MTIM_NOW)) { + return UVWASI_EINVAL; + } + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, flags); + if (err != UVWASI_ESUCCESS) + return err; + + /* TODO(cjihrig): st_atim and st_mtim should not be unconditionally passed. */ + r = uv_fs_utime(NULL, &req, resolved_path, st_atim, st_mtim, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_link(uvwasi_t* uvwasi, + uvwasi_fd_t old_fd, + uvwasi_lookupflags_t old_flags, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t new_fd, + const char* new_path, + size_t new_path_len) { + char resolved_old_path[PATH_MAX_BYTES]; + char resolved_new_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* old_wrap; + struct uvwasi_fd_wrap_t* new_wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL || old_path == NULL || new_path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + old_fd, + &old_wrap, + UVWASI_RIGHT_PATH_LINK_SOURCE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi_fd_table_get(&uvwasi->fds, + new_fd, + &new_wrap, + UVWASI_RIGHT_PATH_LINK_TARGET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(old_wrap, + old_path, + old_path_len, + resolved_old_path, + old_flags); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(new_wrap, + new_path, + new_path_len, + resolved_new_path, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_link(NULL, &req, resolved_old_path, resolved_new_path, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, + uvwasi_fd_t dirfd, + uvwasi_lookupflags_t dirflags, + const char* path, + size_t path_len, + uvwasi_oflags_t o_flags, + uvwasi_rights_t fs_rights_base, + uvwasi_rights_t fs_rights_inheriting, + uvwasi_fdflags_t fs_flags, + uvwasi_fd_t* fd) { + char resolved_path[PATH_MAX_BYTES]; + uvwasi_rights_t needed_inheriting; + uvwasi_rights_t needed_base; + struct uvwasi_fd_wrap_t* dirfd_wrap; + struct uvwasi_fd_wrap_t wrap; + uvwasi_errno_t err; + uv_fs_t req; + int flags; + int read; + int write; + int r; + + if (uvwasi == NULL || path == NULL || fd == NULL) + return UVWASI_EINVAL; + + read = 0 != (fs_rights_base & (UVWASI_RIGHT_FD_READ | + UVWASI_RIGHT_FD_READDIR)); + write = 0 != (fs_rights_base & (UVWASI_RIGHT_FD_DATASYNC | + UVWASI_RIGHT_FD_WRITE | + UVWASI_RIGHT_FD_ALLOCATE | + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE)); + flags = write ? read ? UV_FS_O_RDWR : UV_FS_O_WRONLY : UV_FS_O_RDONLY; + needed_base = UVWASI_RIGHT_PATH_OPEN; + needed_inheriting = fs_rights_base | fs_rights_inheriting; + + if ((o_flags & UVWASI_O_CREAT) != 0) { + flags |= UV_FS_O_CREAT; + needed_base |= UVWASI_RIGHT_PATH_CREATE_FILE; + } + if ((o_flags & UVWASI_O_DIRECTORY) != 0) + flags |= UV_FS_O_DIRECTORY; + if ((o_flags & UVWASI_O_EXCL) != 0) + flags |= UV_FS_O_EXCL; + if ((o_flags & UVWASI_O_TRUNC) != 0) { + flags |= UV_FS_O_TRUNC; + needed_base |= UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE; + } + + if ((fs_flags & UVWASI_FDFLAG_APPEND) != 0) + flags |= UV_FS_O_APPEND; + if ((fs_flags & UVWASI_FDFLAG_DSYNC) != 0) { + flags |= UV_FS_O_DSYNC; + needed_inheriting |= UVWASI_RIGHT_FD_DATASYNC; + } + if ((fs_flags & UVWASI_FDFLAG_NONBLOCK) != 0) + flags |= UV_FS_O_NONBLOCK; + if ((fs_flags & UVWASI_FDFLAG_RSYNC) != 0) { +#ifdef O_RSYNC + flags |= O_RSYNC; /* libuv has no UV_FS_O_RSYNC. */ +#else + flags |= UV_FS_O_SYNC; +#endif + needed_inheriting |= UVWASI_RIGHT_FD_SYNC; + } + if ((fs_flags & UVWASI_FDFLAG_SYNC) != 0) { + flags |= UV_FS_O_SYNC; + needed_inheriting |= UVWASI_RIGHT_FD_SYNC; + } + if (write && (flags & (UV_FS_O_APPEND | UV_FS_O_TRUNC)) == 0) + needed_inheriting |= UVWASI_RIGHT_FD_SEEK; + + err = uvwasi_fd_table_get(&uvwasi->fds, + dirfd, + &dirfd_wrap, + needed_base, + needed_inheriting); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(dirfd_wrap, + path, + path_len, + resolved_path, + dirflags); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_open(NULL, &req, resolved_path, flags, 0666, NULL); + uv_fs_req_cleanup(&req); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + err = uvwasi_fd_table_insert_fd(&uvwasi->fds, + r, + flags, + resolved_path, + fs_rights_base, + fs_rights_inheriting, + &wrap); + if (err != UVWASI_ESUCCESS) + goto close_file_and_error_exit; + + /* Not all platforms support UV_FS_O_DIRECTORY, so enforce it here as well. */ + if ((o_flags & UVWASI_O_DIRECTORY) != 0 && + wrap.type != UVWASI_FILETYPE_DIRECTORY) { + uvwasi_fd_table_remove(&uvwasi->fds, wrap.id); + err = UVWASI_ENOTDIR; + goto close_file_and_error_exit; + } + + *fd = wrap.id; + return UVWASI_ESUCCESS; + +close_file_and_error_exit: + uv_fs_close(NULL, &req, r, NULL); + uv_fs_req_cleanup(&req); + return err; +} + + +uvwasi_errno_t uvwasi_path_readlink(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len, + char* buf, + size_t buf_len, + size_t* bufused) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + uv_fs_t req; + size_t len; + int r; + + if (uvwasi == NULL || path == NULL || buf == NULL || bufused == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_READLINK, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_readlink(NULL, &req, resolved_path, NULL); + if (r != 0) { + uv_fs_req_cleanup(&req); + return uvwasi__translate_uv_error(r); + } + + len = strnlen(req.ptr, buf_len); + if (len >= buf_len) { + uv_fs_req_cleanup(&req); + return UVWASI_ENOBUFS; + } + + memcpy(buf, req.ptr, len); + buf[len] = '\0'; + *bufused = len + 1; + uv_fs_req_cleanup(&req); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_remove_directory(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_REMOVE_DIRECTORY, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_rmdir(NULL, &req, resolved_path, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_rename(uvwasi_t* uvwasi, + uvwasi_fd_t old_fd, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t new_fd, + const char* new_path, + size_t new_path_len) { + char resolved_old_path[PATH_MAX_BYTES]; + char resolved_new_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* old_wrap; + struct uvwasi_fd_wrap_t* new_wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL || old_path == NULL || new_path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + old_fd, + &old_wrap, + UVWASI_RIGHT_PATH_RENAME_SOURCE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi_fd_table_get(&uvwasi->fds, + new_fd, + &new_wrap, + UVWASI_RIGHT_PATH_RENAME_TARGET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(old_wrap, + old_path, + old_path_len, + resolved_old_path, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(new_wrap, + new_path, + new_path_len, + resolved_new_path, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_rename(NULL, &req, resolved_old_path, resolved_new_path, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_symlink(uvwasi_t* uvwasi, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t fd, + const char* new_path, + size_t new_path_len) { + char resolved_new_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL || old_path == NULL || new_path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_SYMLINK, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, + new_path, + new_path_len, + resolved_new_path, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* Windows support may require setting the flags option. */ + r = uv_fs_symlink(NULL, &req, old_path, resolved_new_path, 0, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_unlink_file(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_UNLINK_FILE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_unlink(NULL, &req, resolved_path, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_poll_oneoff(uvwasi_t* uvwasi, + const uvwasi_subscription_t* in, + uvwasi_event_t* out, + size_t nsubscriptions, + size_t* nevents) { + /* TODO(cjihrig): Implement this. */ + return UVWASI_ENOTSUP; +} + + +uvwasi_errno_t uvwasi_proc_exit(uvwasi_t* uvwasi, uvwasi_exitcode_t rval) { + exit(rval); + return UVWASI_ESUCCESS; /* This doesn't happen. */ +} + + +uvwasi_errno_t uvwasi_proc_raise(uvwasi_t* uvwasi, uvwasi_signal_t sig) { + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + r = uvwasi__translate_to_uv_signal(sig); + if (r == -1) + return UVWASI_ENOSYS; + + r = uv_kill(uv_os_getpid(), r); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_random_get(uvwasi_t* uvwasi, void* buf, size_t buf_len) { + int r; + + if (uvwasi == NULL || buf == NULL) + return UVWASI_EINVAL; + + r = uv_random(NULL, NULL, buf, buf_len, 0, NULL); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_sched_yield(uvwasi_t* uvwasi) { + if (uvwasi == NULL) + return UVWASI_EINVAL; + +#ifndef _WIN32 + if (0 != sched_yield()) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); +#else + SwitchToThread(); +#endif /* _WIN32 */ + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_sock_recv(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + const uvwasi_iovec_t* ri_data, + size_t ri_data_len, + uvwasi_riflags_t ri_flags, + size_t* ro_datalen, + uvwasi_roflags_t* ro_flags) { + /* TODO(cjihrig): Waiting to implement, pending + https://github.com/WebAssembly/WASI/issues/4 */ + return UVWASI_ENOTSUP; +} + + +uvwasi_errno_t uvwasi_sock_send(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + const uvwasi_ciovec_t* si_data, + size_t si_data_len, + uvwasi_siflags_t si_flags, + size_t* so_datalen) { + /* TODO(cjihrig): Waiting to implement, pending + https://github.com/WebAssembly/WASI/issues/4 */ + return UVWASI_ENOTSUP; +} + + +uvwasi_errno_t uvwasi_sock_shutdown(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + uvwasi_sdflags_t how) { + /* TODO(cjihrig): Waiting to implement, pending + https://github.com/WebAssembly/WASI/issues/4 */ + return UVWASI_ENOTSUP; +} diff --git a/deps/uvwasi/uvwasi.gyp b/deps/uvwasi/uvwasi.gyp new file mode 100644 index 00000000000000..c07b07b608b724 --- /dev/null +++ b/deps/uvwasi/uvwasi.gyp @@ -0,0 +1,25 @@ +{ + 'targets': [ + { + 'target_name': 'uvwasi', + 'type': 'static_library', + 'cflags': ['-fvisibility=hidden'], + 'xcode_settings': { + 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden + }, + 'include_dirs': ['include'], + 'sources': [ + 'src/clocks.c', + 'src/fd_table.c', + 'src/uv_mapping.c', + 'src/uvwasi.c', + ], + 'dependencies': [ + '../uv/uv.gyp:libuv', + ], + 'direct_dependent_settings': { + 'include_dirs': ['include'] + }, + } + ] +} diff --git a/doc/api/cli.md b/doc/api/cli.md index ab9a600f25098e..1fa9036938af8f 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -221,6 +221,13 @@ added: v9.6.0 Enable experimental ES Module support in the `vm` module. +### `--experimental-wasi-unstable-preview0` + + +Enable experimental WebAssembly System Interface (WASI) support. + ### `--experimental-wasm-modules` + +> Stability: 1 - Experimental + +The WASI API provides an implementation of the [WebAssembly System Interface][] +specification. WASI gives sandboxed WebAssembly applications access to the +underlying operating system via a collection of POSIX-like functions. + +```js +'use strict'; +const fs = require('fs'); +const { WASI } = require('wasi'); +const wasi = new WASI({ + args: process.argv, + env: process.env, + preopens: { + '/sandbox': '/some/real/path/that/wasm/can/access' + } +}); +const importObject = { wasi_unstable: wasi.wasiImport }; + +(async () => { + const wasm = await WebAssembly.compile(fs.readFileSync('./binary.wasm')); + const instance = await WebAssembly.instantiate(wasm, importObject); + + wasi.start(instance); +})(); +``` + +The `--experimental-wasi-unstable-preview0` and `--experimental-wasm-bigint` +CLI arguments are needed for the previous example to run. + +## Class: WASI + + +The `WASI` class provides the WASI system call API and additional convenience +methods for working with WASI-based applications. Each `WASI` instance +represents a distinct sandbox environment. For security purposes, each `WASI` +instance must have its command line arguments, environment variables, and +sandbox directory structure configured explicitly. + +### new WASI(\[options\]) + + +* `options` {Object} + * `args` {Array} An array of strings that the WebAssembly application will + see as command line arguments. The first argument is the virtual path to the + WASI command itself. **Default:** `[]`. + * `env` {Object} An object similar to `process.env` that the WebAssembly + application will see as its environment. **Default:** `{}`. + * `preopens` {Object} This object represents the WebAssembly application's + sandbox directory structure. The string keys of `preopens` are treated as + directories within the sandbox. The corresponding values in `preopens` are + the real paths to those directories on the host machine. + +### wasi.start(instance) + + +* `instance` {WebAssembly.Instance} + +Attempt to begin execution of `instance` by invoking its `_start()` export. +If `instance` does not contain a `_start()` export, then `start()` attempts to +invoke the `__wasi_unstable_reactor_start()` export. If neither of those exports +is present on `instance`, then `start()` does nothing. + +`start()` requires that `instance` exports a [`WebAssembly.Memory`][] named +`memory`. If `instance` does not have a `memory` export an exception is thrown. + +### wasi.wasiImport + + +* {Object} + +`wasiImport` is an object that implements the WASI system call API. This object +should be passed as the `wasi_unstable` import during the instantiation of a +[`WebAssembly.Instance`][]. + +[`WebAssembly.Instance`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance +[`WebAssembly.Memory`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory +[WebAssembly System Interface]: https://wasi.dev/ diff --git a/doc/node.1 b/doc/node.1 index dc175e1381b3e6..8d99435b852804 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -141,6 +141,9 @@ feature. .It Fl -experimental-vm-modules Enable experimental ES module support in VM module. . +.It Fl -experimental-wasi-unstable-preview0 +Enable experimental WebAssembly System Interface support. +. .It Fl -experimental-wasm-modules Enable experimental WebAssembly module support. . diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js index 1a0db5f882fc6f..dbf1609dbc410a 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js @@ -160,6 +160,9 @@ function NativeModule(id) { this.loaded = false; this.loading = false; this.canBeRequiredByUsers = !id.startsWith('internal/'); + + if (id === 'wasi') + this.canBeRequiredByUsers = !!internalBinding('config').experimentalWasi; } // To be called during pre-execution when --expose-internals is on. diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 49148602a49606..cadd7aacf0acef 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1260,6 +1260,7 @@ E('ERR_VM_MODULE_LINKING_ERRORED', E('ERR_VM_MODULE_NOT_MODULE', 'Provided module is not an instance of Module', Error); E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); +E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error); E('ERR_WORKER_INVALID_EXEC_ARGV', (errors) => `Initiated Worker with invalid execArgv flags: ${errors.join(', ')}`, Error); diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js index 2934ee6148cc2e..df41f15ff58610 100644 --- a/lib/internal/modules/cjs/helpers.js +++ b/lib/internal/modules/cjs/helpers.js @@ -122,6 +122,11 @@ const builtinLibs = [ 'v8', 'vm', 'worker_threads', 'zlib' ]; +if (internalBinding('config').experimentalWasi) { + builtinLibs.push('wasi'); + builtinLibs.sort(); +} + if (typeof internalBinding('inspector').open === 'function') { builtinLibs.push('inspector'); builtinLibs.sort(); diff --git a/lib/wasi.js b/lib/wasi.js new file mode 100644 index 00000000000000..efe24e0ad7a2ee --- /dev/null +++ b/lib/wasi.js @@ -0,0 +1,106 @@ +'use strict'; +/* global WebAssembly */ +const { + ArrayIsArray, + ArrayPrototypeForEach, + ArrayPrototypeMap, + FunctionPrototypeBind, + ObjectKeys, + Symbol, +} = primordials; +const { + ERR_INVALID_ARG_TYPE, + ERR_WASI_ALREADY_STARTED +} = require('internal/errors').codes; +const { emitExperimentalWarning } = require('internal/util'); +const { WASI: _WASI } = internalBinding('wasi'); +const kSetMemory = Symbol('setMemory'); +const kStarted = Symbol('started'); + +emitExperimentalWarning('WASI'); + + +class WASI { + constructor(options = {}) { + if (options === null || typeof options !== 'object') + throw new ERR_INVALID_ARG_TYPE('options', 'object', options); + + // eslint-disable-next-line prefer-const + let { args, env, preopens } = options; + + if (ArrayIsArray(args)) + args = ArrayPrototypeMap(args, (arg) => { return String(arg); }); + else if (args === undefined) + args = []; + else + throw new ERR_INVALID_ARG_TYPE('options.args', 'Array', args); + + const envPairs = []; + + if (env !== null && typeof env === 'object') { + for (const key in env) { + const value = env[key]; + if (value !== undefined) + envPairs.push(`${key}=${value}`); + } + } else if (env !== undefined) { + throw new ERR_INVALID_ARG_TYPE('options.env', 'Object', env); + } + + const preopenArray = []; + + if (typeof preopens === 'object' && preopens !== null) { + ArrayPrototypeForEach(ObjectKeys(preopens), (key) => { + preopenArray.push(String(key)); + preopenArray.push(String(preopens[key])); + }); + } else if (preopens !== undefined) { + throw new ERR_INVALID_ARG_TYPE('options.preopens', 'Object', preopens); + } + + const wrap = new _WASI(args, envPairs, preopenArray); + + for (const prop in wrap) { + wrap[prop] = FunctionPrototypeBind(wrap[prop], wrap); + } + + this[kSetMemory] = wrap._setMemory; + delete wrap._setMemory; + this.wasiImport = wrap; + this[kStarted] = false; + } + + start(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new ERR_INVALID_ARG_TYPE( + 'instance', 'WebAssembly.Instance', instance); + } + + const exports = instance.exports; + + if (exports === null || typeof exports !== 'object') + throw new ERR_INVALID_ARG_TYPE('instance.exports', 'Object', exports); + + const { memory } = exports; + + if (!(memory instanceof WebAssembly.Memory)) { + throw new ERR_INVALID_ARG_TYPE( + 'instance.exports.memory', 'WebAssembly.Memory', memory); + } + + if (this[kStarted]) { + throw new ERR_WASI_ALREADY_STARTED(); + } + + this[kStarted] = true; + this[kSetMemory](memory); + + if (exports._start) + exports._start(); + else if (exports.__wasi_unstable_reactor_start) + exports.__wasi_unstable_reactor_start(); + } +} + + +module.exports = { WASI }; diff --git a/node.gyp b/node.gyp index 35ba2481e70167..0a1178b22619d3 100644 --- a/node.gyp +++ b/node.gyp @@ -83,6 +83,7 @@ 'lib/util.js', 'lib/v8.js', 'lib/vm.js', + 'lib/wasi.js', 'lib/worker_threads.js', 'lib/zlib.js', 'lib/internal/assert.js', @@ -321,7 +322,10 @@ 'src/node_main.cc' ], - 'dependencies': [ 'deps/histogram/histogram.gyp:histogram' ], + 'dependencies': [ + 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', + ], 'msvs_settings': { 'VCLinkerTool': { @@ -495,7 +499,10 @@ 'src', '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h ], - 'dependencies': [ 'deps/histogram/histogram.gyp:histogram' ], + 'dependencies': [ + 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', + ], 'sources': [ 'src/api/async_resource.cc', @@ -561,6 +568,7 @@ 'src/node_url.cc', 'src/node_util.cc', 'src/node_v8.cc', + 'src/node_wasi.cc', 'src/node_watchdog.cc', 'src/node_worker.cc', 'src/node_zlib.cc', @@ -644,6 +652,7 @@ 'src/node_url.h', 'src/node_version.h', 'src/node_v8_platform-inl.h', + 'src/node_wasi.h', 'src/node_watchdog.h', 'src/node_worker.h', 'src/pipe_wrap.h', @@ -1079,6 +1088,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', 'node_dtrace_header', 'node_dtrace_ustack', 'node_dtrace_provider', @@ -1094,6 +1104,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', 'test/cctest', ], @@ -1187,6 +1198,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', ], 'includes': [ @@ -1199,6 +1211,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', ], 'defines': [ @@ -1230,6 +1243,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', ], 'includes': [ @@ -1242,6 +1256,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', ], 'defines': [ 'NODE_WANT_INTERNALS=1' ], diff --git a/src/env-inl.h b/src/env-inl.h index 0ded61cd798dab..99b5273b9b25ce 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -1107,6 +1107,21 @@ inline void Environment::SetProtoMethodNoSideEffect( t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. } +inline void Environment::SetInstanceMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback) { + v8::Local signature = v8::Signature::New(isolate(), that); + v8::Local t = + NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->InstanceTemplate()->Set(name_string, t); + t->SetClassName(name_string); +} + void Environment::AddCleanupHook(void (*fn)(void*), void* arg) { auto insertion_info = cleanup_hooks_.emplace(CleanupHookCallback { fn, arg, cleanup_hook_counter_++ diff --git a/src/env.h b/src/env.h index 4f1018b0e0cb56..8d2404264cf270 100644 --- a/src/env.h +++ b/src/env.h @@ -547,7 +547,8 @@ struct ContextInfo { #define DEBUG_CATEGORY_NAMES(V) \ NODE_ASYNC_PROVIDER_TYPES(V) \ V(INSPECTOR_SERVER) \ - V(INSPECTOR_PROFILER) + V(INSPECTOR_PROFILER) \ + V(WASI) enum class DebugCategory { #define V(name) name, @@ -1120,6 +1121,11 @@ class Environment : public MemoryRetainer { const char* name, v8::FunctionCallback callback); + inline void SetInstanceMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); + + // Safe variants denote the function has no side effects. inline void SetMethodNoSideEffect(v8::Local that, const char* name, diff --git a/src/node_binding.cc b/src/node_binding.cc index 848d228b87dcd2..0facfc36cbcb78 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -86,6 +86,7 @@ V(util) \ V(uv) \ V(v8) \ + V(wasi) \ V(worker) \ V(zlib) diff --git a/src/node_config.cc b/src/node_config.cc index 92985dff2f8b0c..16050bdd5b967d 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -84,6 +84,9 @@ static void Initialize(Local target, READONLY_PROPERTY(target, "hasCachedBuiltins", v8::Boolean::New(isolate, native_module::has_code_cache)); + + if (env->options()->experimental_wasi) + READONLY_TRUE_PROPERTY(target, "experimentalWasi"); } // InitConfig } // namespace node diff --git a/src/node_options.cc b/src/node_options.cc index e4dee55f26ff8b..2d0c4df067de17 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -397,6 +397,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::experimental_report, kAllowedInEnvironment); #endif // NODE_REPORT + AddOption("--experimental-wasi-unstable-preview0", + "experimental WASI support", + &EnvironmentOptions::experimental_wasi, + kAllowedInEnvironment); AddOption("--expose-internals", "", &EnvironmentOptions::expose_internals); AddOption("--frozen-intrinsics", "experimental frozen intrinsics support", diff --git a/src/node_options.h b/src/node_options.h index 87a0e3b41e14e7..61d7ceed7bca85 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -155,6 +155,7 @@ class EnvironmentOptions : public Options { #ifdef NODE_REPORT bool experimental_report = false; #endif // NODE_REPORT + bool experimental_wasi = false; std::string eval_string; bool print_eval = false; bool force_repl = false; diff --git a/src/node_wasi.cc b/src/node_wasi.cc new file mode 100644 index 00000000000000..4d7f0e53f61b14 --- /dev/null +++ b/src/node_wasi.cc @@ -0,0 +1,1801 @@ +#include "env-inl.h" +#include "base_object-inl.h" +#include "debug_utils.h" +#include "util-inl.h" +#include "node.h" +#include "uv.h" +#include "uvwasi.h" +#include "node_wasi.h" + +namespace node { +namespace wasi { + +static inline bool is_access_oob(size_t mem_size, + uint32_t offset, + uint32_t buf_size) { + return offset + buf_size > mem_size; +} + +template +inline void Debug(WASI* wasi, Args&&... args) { + Debug(wasi->env(), DebugCategory::WASI, std::forward(args)...); +} + +#define RETURN_IF_BAD_ARG_COUNT(args, expected) \ + do { \ + if ((args).Length() != (expected)) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + } while (0) + +#define CHECK_TO_TYPE_OR_RETURN(args, input, type, result) \ + do { \ + if (!(input)->Is##type()) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + (result) = (input).As()->Value(); \ + } while (0) + +#define UNWRAP_BIGINT_OR_RETURN(args, input, type, result) \ + do { \ + if (!(input)->IsBigInt()) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + Local js_value = (input).As(); \ + bool lossless; \ + (result) = js_value->type ## Value(&lossless); \ + } while (0) + +#define GET_BACKING_STORE_OR_RETURN(wasi, args, mem_ptr, mem_size) \ + do { \ + uvwasi_errno_t err = (wasi)->backingStore((mem_ptr), (mem_size)); \ + if (err != UVWASI_ESUCCESS) { \ + (args).GetReturnValue().Set(err); \ + return; \ + } \ + } while (0) + +#define CHECK_BOUNDS_OR_RETURN(args, mem_size, offset, buf_size) \ + do { \ + if (is_access_oob((mem_size), (offset), (buf_size))) { \ + (args).GetReturnValue().Set(UVWASI_EOVERFLOW); \ + return; \ + } \ + } while (0) + + +using v8::Array; +using v8::ArrayBuffer; +using v8::BigInt; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Local; +using v8::Object; +using v8::String; +using v8::Uint32; +using v8::Value; + + +WASI::WASI(Environment* env, + Local object, + uvwasi_options_t* options) : BaseObject(env, object) { + MakeWeak(); + CHECK_EQ(uvwasi_init(&uvw_, options), UVWASI_ESUCCESS); +} + + +WASI::~WASI() { + uvwasi_destroy(&uvw_); +} + + +void WASI::New(const FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + CHECK_EQ(args.Length(), 3); + CHECK(args[0]->IsArray()); + CHECK(args[1]->IsArray()); + CHECK(args[2]->IsArray()); + + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + Local argv = args[0].As(); + const uint32_t argc = argv->Length(); + uvwasi_options_t options; + + options.fd_table_size = 3; + options.argc = argc; + options.argv = argc == 0 ? nullptr : new char*[argc]; + + for (uint32_t i = 0; i < argc; i++) { + auto arg = argv->Get(context, i).ToLocalChecked(); + CHECK(arg->IsString()); + node::Utf8Value str(env->isolate(), arg); + options.argv[i] = strdup(*str); + CHECK_NOT_NULL(options.argv[i]); + } + + Local env_pairs = args[1].As(); + const uint32_t envc = env_pairs->Length(); + options.envp = new char*[envc + 1]; + for (uint32_t i = 0; i < envc; i++) { + auto pair = env_pairs->Get(context, i).ToLocalChecked(); + CHECK(pair->IsString()); + node::Utf8Value str(env->isolate(), pair); + options.envp[i] = strdup(*str); + CHECK_NOT_NULL(options.envp[i]); + } + options.envp[envc] = nullptr; + + Local preopens = args[2].As(); + CHECK_EQ(preopens->Length() % 2, 0); + options.preopenc = preopens->Length() / 2; + options.preopens = UncheckedCalloc(options.preopenc); + int index = 0; + for (uint32_t i = 0; i < preopens->Length(); i += 2) { + auto mapped = preopens->Get(context, i).ToLocalChecked(); + auto real = preopens->Get(context, i + 1).ToLocalChecked(); + CHECK(mapped->IsString()); + CHECK(real->IsString()); + node::Utf8Value mapped_path(env->isolate(), mapped); + node::Utf8Value real_path(env->isolate(), real); + options.preopens[index].mapped_path = strdup(*mapped_path); + options.preopens[index].real_path = strdup(*real_path); + index++; + } + + new WASI(env, args.This(), &options); + + if (options.argv != nullptr) { + for (uint32_t i = 0; i < argc; i++) + free(options.argv[i]); + delete[] options.argv; + } + + if (options.envp != nullptr) { + for (uint32_t i = 0; options.envp[i]; i++) + free(options.envp[i]); + delete[] options.envp; + } +} + + +void WASI::ArgsGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t argv_offset; + uint32_t argv_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argv_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, + mem_size, + argv_buf_offset, + wasi->uvw_.argv_buf_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argv_offset, wasi->uvw_.argc * 4); + std::vector argv(wasi->uvw_.argc); + char* argv_buf = &memory[argv_buf_offset]; + uvwasi_errno_t err = uvwasi_args_get(&wasi->uvw_, argv.data(), argv_buf); + + if (err == UVWASI_ESUCCESS) { + for (size_t i = 0; i < wasi->uvw_.argc; i++) { + uint32_t offset = argv_buf_offset + (argv[i] - argv[0]); + wasi->writeUInt32(memory, offset, argv_offset + (i * 4)); + } + } + + args.GetReturnValue().Set(err); +} + + +void WASI::ArgsSizesGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t argc_offset; + uint32_t argv_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argc_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "args_sizes_get(%d, %d)\n", argc_offset, argv_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argc_offset, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argv_buf_offset, 4); + size_t argc; + size_t argv_buf_size; + uvwasi_errno_t err = uvwasi_args_sizes_get(&wasi->uvw_, + &argc, + &argv_buf_size); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, argc, argc_offset); + wasi->writeUInt32(memory, argv_buf_size, argv_buf_offset); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::ClockResGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t clock_id; + uint32_t resolution_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, clock_id); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, resolution_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "clock_res_get(%d, %d)\n", clock_id, resolution_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, resolution_ptr, 8); + uvwasi_timestamp_t resolution; + uvwasi_errno_t err = uvwasi_clock_res_get(&wasi->uvw_, + clock_id, + &resolution); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, resolution, resolution_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::ClockTimeGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t clock_id; + uint64_t precision; + uint32_t time_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, clock_id); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, precision); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, time_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "clock_time_get(%d, %d, %d)\n", clock_id, precision, time_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, time_ptr, 8); + uvwasi_timestamp_t time; + uvwasi_errno_t err = uvwasi_clock_time_get(&wasi->uvw_, + clock_id, + precision, + &time); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, time, time_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::EnvironGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t environ_offset; + uint32_t environ_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, environ_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, environ_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "environ_get(%d, %d)\n", environ_offset, environ_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, + mem_size, + environ_buf_offset, + wasi->uvw_.env_buf_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, environ_offset, wasi->uvw_.envc * 4); + std::vector environment(wasi->uvw_.envc); + char* environ_buf = &memory[environ_buf_offset]; + uvwasi_errno_t err = uvwasi_environ_get(&wasi->uvw_, + environment.data(), + environ_buf); + + if (err == UVWASI_ESUCCESS) { + for (size_t i = 0; i < wasi->uvw_.envc; i++) { + uint32_t offset = environ_buf_offset + (environment[i] - environment[0]); + wasi->writeUInt32(memory, offset, environ_offset + (i * 4)); + } + } + + args.GetReturnValue().Set(err); +} + + +void WASI::EnvironSizesGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t envc_offset; + uint32_t env_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, envc_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, env_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "environ_sizes_get(%d, %d)\n", envc_offset, env_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, envc_offset, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, env_buf_offset, 4); + size_t envc; + size_t env_buf_size; + uvwasi_errno_t err = uvwasi_environ_sizes_get(&wasi->uvw_, + &envc, + &env_buf_size); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, envc, envc_offset); + wasi->writeUInt32(memory, env_buf_size, env_buf_offset); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdAdvise(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t offset; + uint64_t len; + uint8_t advice; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, advice); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice); + uvwasi_errno_t err = uvwasi_fd_advise(&wasi->uvw_, fd, offset, len, advice); + args.GetReturnValue().Set(err); +} + + +void WASI::FdAllocate(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t offset; + uint64_t len; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_allocate(%d, %d, %d)\n", fd, offset, len); + uvwasi_errno_t err = uvwasi_fd_allocate(&wasi->uvw_, fd, offset, len); + args.GetReturnValue().Set(err); +} + + +void WASI::FdClose(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_close(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_close(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdDatasync(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_datasync(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_datasync(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_fdstat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 24); + uvwasi_fdstat_t stats; + uvwasi_errno_t err = uvwasi_fd_fdstat_get(&wasi->uvw_, fd, &stats); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt8(memory, stats.fs_filetype, buf); + wasi->writeUInt16(memory, stats.fs_flags, buf + 2); + wasi->writeUInt64(memory, stats.fs_rights_base, buf + 8); + wasi->writeUInt64(memory, stats.fs_rights_inheriting, buf + 16); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatSetFlags(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint16_t flags; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_fdstat_set_flags(%d, %d)\n", fd, flags); + uvwasi_errno_t err = uvwasi_fd_fdstat_set_flags(&wasi->uvw_, fd, flags); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatSetRights(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t fs_rights_base; + uint64_t fs_rights_inheriting; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, fs_rights_base); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, fs_rights_inheriting); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_fdstat_set_rights(%d, %d, %d)\n", + fd, + fs_rights_base, + fs_rights_inheriting); + uvwasi_errno_t err = uvwasi_fd_fdstat_set_rights(&wasi->uvw_, + fd, + fs_rights_base, + fs_rights_inheriting); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_filestat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 56); + uvwasi_filestat_t stats; + uvwasi_errno_t err = uvwasi_fd_filestat_get(&wasi->uvw_, fd, &stats); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt64(memory, stats.st_dev, buf); + wasi->writeUInt64(memory, stats.st_ino, buf + 8); + wasi->writeUInt8(memory, stats.st_filetype, buf + 16); + wasi->writeUInt32(memory, stats.st_nlink, buf + 20); + wasi->writeUInt64(memory, stats.st_size, buf + 24); + wasi->writeUInt64(memory, stats.st_atim, buf + 32); + wasi->writeUInt64(memory, stats.st_mtim, buf + 40); + wasi->writeUInt64(memory, stats.st_ctim, buf + 48); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatSetSize(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t st_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, st_size); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_filestat_set_size(%d, %d)\n", fd, st_size); + uvwasi_errno_t err = uvwasi_fd_filestat_set_size(&wasi->uvw_, fd, st_size); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatSetTimes(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t st_atim; + uint64_t st_mtim; + uint16_t fst_flags; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, st_atim); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, st_mtim); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, fst_flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_filestat_set_times(%d, %d, %d, %d)\n", + fd, + st_atim, + st_mtim, + fst_flags); + uvwasi_errno_t err = uvwasi_fd_filestat_set_times(&wasi->uvw_, + fd, + st_atim, + st_mtim, + fst_flags); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPread(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint64_t offset; + uint32_t nread_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, nread_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_pread(%d, %d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + offset, + nread_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nread_ptr, 4); + uvwasi_iovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nread; + uvwasi_errno_t err = uvwasi_fd_pread(&wasi->uvw_, + fd, + iovs, + iovs_len, + offset, + &nread); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nread, nread_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPrestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_prestat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 8); + uvwasi_prestat_t prestat; + uvwasi_errno_t err = uvwasi_fd_prestat_get(&wasi->uvw_, fd, &prestat); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, prestat.pr_type, buf); + wasi->writeUInt32(memory, prestat.u.dir.pr_name_len, buf + 4); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdPrestatDirName(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_prestat_dir_name(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_fd_prestat_dir_name(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPwrite(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint64_t offset; + uint32_t nwritten_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, nwritten_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_pwrite(%d, %d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + offset, + nwritten_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nwritten_ptr, 4); + uvwasi_ciovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nwritten; + uvwasi_errno_t err = uvwasi_fd_pwrite(&wasi->uvw_, + fd, + iovs, + iovs_len, + offset, + &nwritten); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nwritten, nwritten_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdRead(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint32_t nread_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nread_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_read(%d, %d, %d, %d)\n", fd, iovs_ptr, iovs_len, nread_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nread_ptr, 4); + uvwasi_iovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nread; + uvwasi_errno_t err = uvwasi_fd_read(&wasi->uvw_, + fd, + iovs, + iovs_len, + &nread); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nread, nread_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdReaddir(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf_ptr; + uint32_t buf_len; + uint64_t cookie; + uint32_t bufused_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, buf_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, cookie); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, bufused_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_readdir(%d, %d, %d, %d, %d)\n", + fd, + buf_ptr, + buf_len, + cookie, + bufused_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, bufused_ptr, 4); + size_t bufused; + uvwasi_errno_t err = uvwasi_fd_readdir(&wasi->uvw_, + fd, + &memory[buf_ptr], + buf_len, + cookie, + &bufused); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, bufused, bufused_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdRenumber(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t from; + uint32_t to; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, from); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, to); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_renumber(%d, %d)\n", from, to); + uvwasi_errno_t err = uvwasi_fd_renumber(&wasi->uvw_, from, to); + args.GetReturnValue().Set(err); +} + + +void WASI::FdSeek(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + int64_t offset; + uint8_t whence; + uint32_t newoffset_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Int64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, whence); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, newoffset_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, newoffset_ptr, 8); + uvwasi_filesize_t newoffset; + uvwasi_errno_t err = uvwasi_fd_seek(&wasi->uvw_, + fd, + offset, + whence, + &newoffset); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, newoffset, newoffset_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdSync(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_sync(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_sync(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdTell(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t offset_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, offset_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_tell(%d, %d)\n", fd, offset_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, offset_ptr, 8); + uvwasi_filesize_t offset; + uvwasi_errno_t err = uvwasi_fd_tell(&wasi->uvw_, fd, &offset); + + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, offset, offset_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdWrite(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint32_t nwritten_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nwritten_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_write(%d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + nwritten_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nwritten_ptr, 4); + uvwasi_ciovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nwritten; + uvwasi_errno_t err = uvwasi_fd_write(&wasi->uvw_, + fd, + iovs, + iovs_len, + &nwritten); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nwritten, nwritten_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::PathCreateDirectory(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_create_directory(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_create_directory(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathFilestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t flags; + uint32_t path_ptr; + uint32_t path_len; + uint32_t buf_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, buf_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_filestat_get(%d, %d, %d, %d, %d)\n", + fd, + path_ptr, + path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, 56); + uvwasi_filestat_t stats; + uvwasi_errno_t err = uvwasi_path_filestat_get(&wasi->uvw_, + fd, + flags, + &memory[path_ptr], + path_len, + &stats); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt64(memory, stats.st_dev, buf_ptr); + wasi->writeUInt64(memory, stats.st_ino, buf_ptr + 8); + wasi->writeUInt8(memory, stats.st_filetype, buf_ptr + 16); + wasi->writeUInt32(memory, stats.st_nlink, buf_ptr + 20); + wasi->writeUInt64(memory, stats.st_size, buf_ptr + 24); + wasi->writeUInt64(memory, stats.st_atim, buf_ptr + 32); + wasi->writeUInt64(memory, stats.st_mtim, buf_ptr + 40); + wasi->writeUInt64(memory, stats.st_ctim, buf_ptr + 48); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::PathFilestatSetTimes(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t flags; + uint32_t path_ptr; + uint32_t path_len; + uint64_t st_atim; + uint64_t st_mtim; + uint16_t fst_flags; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 7); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + UNWRAP_BIGINT_OR_RETURN(args, args[4], Uint64, st_atim); + UNWRAP_BIGINT_OR_RETURN(args, args[5], Uint64, st_mtim); + CHECK_TO_TYPE_OR_RETURN(args, args[6], Uint32, fst_flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_filestat_set_times(%d, %d, %d, %d, %d, %d, %d)\n", + fd, + flags, + path_ptr, + path_len, + st_atim, + st_mtim, + fst_flags); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_filestat_set_times(&wasi->uvw_, + fd, + flags, + &memory[path_ptr], + path_len, + st_atim, + st_mtim, + fst_flags); + args.GetReturnValue().Set(err); +} + + +void WASI::PathLink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_fd; + uint32_t old_flags; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t new_fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 7); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[6], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_link(%d, %d, %d, %d, %d, %d, %d)\n", + old_fd, + old_flags, + old_path_ptr, + old_path_len, + new_fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_link(&wasi->uvw_, + old_fd, + old_flags, + &memory[old_path_ptr], + old_path_len, + new_fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathOpen(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t dirfd; + uint32_t dirflags; + uint32_t path_ptr; + uint32_t path_len; + uint32_t o_flags; + uint64_t fs_rights_base; + uint64_t fs_rights_inheriting; + uint32_t fs_flags; + uint32_t fd_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 9); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, dirfd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, dirflags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, o_flags); + UNWRAP_BIGINT_OR_RETURN(args, args[5], Uint64, fs_rights_base); + UNWRAP_BIGINT_OR_RETURN(args, args[6], Uint64, fs_rights_inheriting); + CHECK_TO_TYPE_OR_RETURN(args, args[7], Uint32, fs_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[8], Uint32, fd_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_open(%d, %d, %d, %d, %d, %d, %d, %d, %d)\n", + dirfd, + dirflags, + path_ptr, + path_len, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, fd_ptr, 4); + uvwasi_fd_t fd; + uvwasi_errno_t err = uvwasi_path_open(&wasi->uvw_, + dirfd, + dirflags, + &memory[path_ptr], + path_len, + static_cast(o_flags), + fs_rights_base, + fs_rights_inheriting, + static_cast(fs_flags), + &fd); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, fd, fd_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::PathReadlink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + uint32_t buf_ptr; + uint32_t buf_len; + uint32_t bufused_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, buf_len); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, bufused_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_readlink(%d, %d, %d, %d, %d, %d)\n", + fd, + path_ptr, + path_len, + buf_ptr, + buf_len, + bufused_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, bufused_ptr, 4); + size_t bufused; + uvwasi_errno_t err = uvwasi_path_readlink(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len, + &memory[buf_ptr], + buf_len, + &bufused); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, bufused, bufused_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::PathRemoveDirectory(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_remove_directory(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_remove_directory(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathRename(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_fd; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t new_fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, new_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_rename(%d, %d, %d, %d, %d, %d)\n", + old_fd, + old_path_ptr, + old_path_len, + new_fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_rename(&wasi->uvw_, + old_fd, + &memory[old_path_ptr], + old_path_len, + new_fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathSymlink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_symlink(%d, %d, %d, %d, %d)\n", + old_path_ptr, + old_path_len, + fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_symlink(&wasi->uvw_, + &memory[old_path_ptr], + old_path_len, + fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathUnlinkFile(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_unlink_file(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_unlink_file(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PollOneoff(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t in_ptr; + uint32_t out_ptr; + uint32_t nsubscriptions; + uint32_t nevents_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, in_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, out_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, nsubscriptions); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nevents_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "poll_oneoff(%d, %d, %d, %d)\n", + in_ptr, + out_ptr, + nsubscriptions, + nevents_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, in_ptr, nsubscriptions * 56); + CHECK_BOUNDS_OR_RETURN(args, mem_size, out_ptr, nsubscriptions * 32); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nevents_ptr, 4); + uvwasi_subscription_t* in = + UncheckedCalloc(nsubscriptions); + + if (in == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + uvwasi_event_t* out = UncheckedCalloc(nsubscriptions); + + if (out == nullptr) { + free(in); + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < nsubscriptions; ++i) { + uvwasi_subscription_t sub = in[i]; + wasi->readUInt64(memory, &sub.userdata, in_ptr); + wasi->readUInt8(memory, &sub.type, in_ptr + 8); + + if (sub.type == UVWASI_EVENTTYPE_CLOCK) { + wasi->readUInt64(memory, &sub.u.clock.identifier, in_ptr + 16); + wasi->readUInt32(memory, &sub.u.clock.clock_id, in_ptr + 24); + wasi->readUInt64(memory, &sub.u.clock.timeout, in_ptr + 32); + wasi->readUInt64(memory, &sub.u.clock.precision, in_ptr + 40); + wasi->readUInt16(memory, &sub.u.clock.flags, in_ptr + 48); + } else if (sub.type == UVWASI_EVENTTYPE_FD_READ || + sub.type == UVWASI_EVENTTYPE_FD_WRITE) { + wasi->readUInt32(memory, &sub.u.fd_readwrite.fd, in_ptr + 16); + } + + in_ptr += 56; + } + + size_t nevents; + uvwasi_errno_t err = uvwasi_poll_oneoff(&wasi->uvw_, + in, + out, + nsubscriptions, + &nevents); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, nevents, nevents_ptr); + + for (uint32_t i = 0; i < nsubscriptions; ++i) { + uvwasi_event_t event = out[i]; + + wasi->writeUInt64(memory, event.userdata, out_ptr); + wasi->writeUInt16(memory, event.error, out_ptr + 8); + wasi->writeUInt8(memory, event.type, out_ptr + 10); + + if (event.type == UVWASI_EVENTTYPE_FD_READ || + event.type == UVWASI_EVENTTYPE_FD_WRITE) { + wasi->writeUInt64(memory, event.u.fd_readwrite.nbytes, out_ptr + 16); + wasi->writeUInt16(memory, event.u.fd_readwrite.flags, out_ptr + 24); + } + + out_ptr += 32; + } + } + + free(in); + free(out); + args.GetReturnValue().Set(err); +} + + +void WASI::ProcExit(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t code; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, code); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "proc_exit(%d)\n", code); + args.GetReturnValue().Set(uvwasi_proc_exit(&wasi->uvw_, code)); +} + + +void WASI::ProcRaise(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sig; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sig); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "proc_raise(%d)\n", sig); + uvwasi_errno_t err = uvwasi_proc_raise(&wasi->uvw_, sig); + args.GetReturnValue().Set(err); +} + + +void WASI::RandomGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t buf_ptr; + uint32_t buf_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "random_get(%d, %d)\n", buf_ptr, buf_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + uvwasi_errno_t err = uvwasi_random_get(&wasi->uvw_, + &memory[buf_ptr], + buf_len); + args.GetReturnValue().Set(err); +} + + +void WASI::SchedYield(const FunctionCallbackInfo& args) { + WASI* wasi; + RETURN_IF_BAD_ARG_COUNT(args, 0); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "sched_yield()\n"); + uvwasi_errno_t err = uvwasi_sched_yield(&wasi->uvw_); + args.GetReturnValue().Set(err); +} + + +void WASI::SockRecv(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint32_t ri_data_ptr; + uint32_t ri_data_len; + uint16_t ri_flags; + uint32_t ro_datalen_ptr; + uint16_t ro_flags_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, ri_data_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, ri_data_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, ri_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, ro_datalen_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, ro_flags_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "sock_recv(%d, %d, %d, %d, %d, %d)\n", + sock, + ri_data_ptr, + ri_data_len, + ri_flags, + ro_datalen_ptr, + ro_flags_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ri_data_ptr, ri_data_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ro_datalen_ptr, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ro_flags_ptr, 4); + uvwasi_iovec_t* ri_data = UncheckedCalloc(ri_data_len); + + if (ri_data == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < ri_data_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, ri_data_ptr); + wasi->readUInt32(memory, &buf_len, ri_data_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(ri_data); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + ri_data_ptr += 8; + ri_data[i].buf = static_cast(&memory[buf_ptr]); + ri_data[i].buf_len = buf_len; + } + + size_t ro_datalen; + uvwasi_roflags_t ro_flags; + uvwasi_errno_t err = uvwasi_sock_recv(&wasi->uvw_, + sock, + ri_data, + ri_data_len, + ri_flags, + &ro_datalen, + &ro_flags); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, ro_datalen, ro_datalen_ptr); + wasi->writeUInt32(memory, ro_flags, ro_flags_ptr); + } + + free(ri_data); + args.GetReturnValue().Set(err); +} + + +void WASI::SockSend(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint32_t si_data_ptr; + uint32_t si_data_len; + uint16_t si_flags; + uint32_t so_datalen_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, si_data_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, si_data_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, si_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, so_datalen_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "sock_send(%d, %d, %d, %d, %d)\n", + sock, + si_data_ptr, + si_data_len, + si_flags, + so_datalen_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, si_data_ptr, si_data_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, so_datalen_ptr, 4); + uvwasi_ciovec_t* si_data = UncheckedCalloc(si_data_len); + + if (si_data == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < si_data_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, si_data_ptr); + wasi->readUInt32(memory, &buf_len, si_data_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(si_data); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + si_data_ptr += 8; + si_data[i].buf = static_cast(&memory[buf_ptr]); + si_data[i].buf_len = buf_len; + } + + size_t so_datalen; + uvwasi_errno_t err = uvwasi_sock_send(&wasi->uvw_, + sock, + si_data, + si_data_len, + si_flags, + &so_datalen); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, so_datalen, so_datalen_ptr); + + free(si_data); + args.GetReturnValue().Set(err); +} + + +void WASI::SockShutdown(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint8_t how; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, how); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "sock_shutdown(%d, %d)\n", sock, how); + uvwasi_errno_t err = uvwasi_sock_shutdown(&wasi->uvw_, sock, how); + args.GetReturnValue().Set(err); +} + + +void WASI::_SetMemory(const FunctionCallbackInfo& args) { + WASI* wasi; + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsObject()); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + wasi->memory_.Reset(wasi->env()->isolate(), args[0].As()); +} + + +void WASI::readUInt8(char* memory, uint8_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = memory[offset] & 0xFF; +} + + +void WASI::readUInt16(char* memory, uint16_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8); +} + + +void WASI::readUInt32(char* memory, uint32_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8) | + ((memory[offset + 2] & 0xFF) << 16) | + ((memory[offset + 3] & 0xFF) << 24); +} + + +void WASI::readUInt64(char* memory, uint64_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + uint64_t low = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8) | + ((memory[offset + 2] & 0xFF) << 16) | + ((memory[offset + 3] & 0xFF) << 24); + uint64_t high = (memory[offset + 4] & 0xFF) | + ((memory[offset + 5] & 0xFF) << 8) | + ((memory[offset + 6] & 0xFF) << 16) | + ((memory[offset + 7] & 0xFF) << 24); + *value = (high << 32) + low; +} + + +void WASI::writeUInt8(char* memory, uint8_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset] = value & 0xFF; +} + + +void WASI::writeUInt16(char* memory, uint16_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset] = (value >> 8) & 0xFF; +} + + +void WASI::writeUInt32(char* memory, uint32_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset++] = (value >> 8) & 0xFF; + memory[offset++] = (value >> 16) & 0xFF; + memory[offset] = (value >> 24) & 0xFF; +} + + +void WASI::writeUInt64(char* memory, uint64_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset++] = (value >> 8) & 0xFF; + memory[offset++] = (value >> 16) & 0xFF; + memory[offset++] = (value >> 24) & 0xFF; + memory[offset++] = (value >> 32) & 0xFF; + memory[offset++] = (value >> 40) & 0xFF; + memory[offset++] = (value >> 48) & 0xFF; + memory[offset] = (value >> 56) & 0xFF; +} + + +uvwasi_errno_t WASI::backingStore(char** store, size_t* byte_length) { + Environment* env = this->env(); + Local memory = PersistentToLocal::Strong(this->memory_); + Local prop; + + if (!memory->Get(env->context(), env->buffer_string()).ToLocal(&prop)) + return UVWASI_EINVAL; + + if (!prop->IsArrayBuffer()) + return UVWASI_EINVAL; + + Local ab = prop.As(); + ArrayBuffer::Contents contents = ab->GetContents(); + *byte_length = ab->ByteLength(); + *store = static_cast(contents.Data()); + return UVWASI_ESUCCESS; +} + + +static void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + Local tmpl = env->NewFunctionTemplate(WASI::New); + auto wasi_wrap_string = FIXED_ONE_BYTE_STRING(env->isolate(), "WASI"); + tmpl->InstanceTemplate()->SetInternalFieldCount(1); + tmpl->SetClassName(wasi_wrap_string); + + env->SetProtoMethod(tmpl, "args_get", WASI::ArgsGet); + env->SetProtoMethod(tmpl, "args_sizes_get", WASI::ArgsSizesGet); + env->SetProtoMethod(tmpl, "clock_res_get", WASI::ClockResGet); + env->SetProtoMethod(tmpl, "clock_time_get", WASI::ClockTimeGet); + env->SetProtoMethod(tmpl, "environ_get", WASI::EnvironGet); + env->SetProtoMethod(tmpl, "environ_sizes_get", WASI::EnvironSizesGet); + env->SetProtoMethod(tmpl, "fd_advise", WASI::FdAdvise); + env->SetProtoMethod(tmpl, "fd_allocate", WASI::FdAllocate); + env->SetProtoMethod(tmpl, "fd_close", WASI::FdClose); + env->SetProtoMethod(tmpl, "fd_datasync", WASI::FdDatasync); + env->SetProtoMethod(tmpl, "fd_fdstat_get", WASI::FdFdstatGet); + env->SetProtoMethod(tmpl, "fd_fdstat_set_flags", WASI::FdFdstatSetFlags); + env->SetProtoMethod(tmpl, "fd_fdstat_set_rights", WASI::FdFdstatSetRights); + env->SetProtoMethod(tmpl, "fd_filestat_get", WASI::FdFilestatGet); + env->SetProtoMethod(tmpl, "fd_filestat_set_size", WASI::FdFilestatSetSize); + env->SetProtoMethod(tmpl, "fd_filestat_set_times", WASI::FdFilestatSetTimes); + env->SetProtoMethod(tmpl, "fd_pread", WASI::FdPread); + env->SetProtoMethod(tmpl, "fd_prestat_get", WASI::FdPrestatGet); + env->SetProtoMethod(tmpl, "fd_prestat_dir_name", WASI::FdPrestatDirName); + env->SetProtoMethod(tmpl, "fd_pwrite", WASI::FdPwrite); + env->SetProtoMethod(tmpl, "fd_read", WASI::FdRead); + env->SetProtoMethod(tmpl, "fd_readdir", WASI::FdReaddir); + env->SetProtoMethod(tmpl, "fd_renumber", WASI::FdRenumber); + env->SetProtoMethod(tmpl, "fd_seek", WASI::FdSeek); + env->SetProtoMethod(tmpl, "fd_sync", WASI::FdSync); + env->SetProtoMethod(tmpl, "fd_tell", WASI::FdTell); + env->SetProtoMethod(tmpl, "fd_write", WASI::FdWrite); + env->SetProtoMethod(tmpl, "path_create_directory", WASI::PathCreateDirectory); + env->SetProtoMethod(tmpl, "path_filestat_get", WASI::PathFilestatGet); + env->SetProtoMethod(tmpl, + "path_filestat_set_times", + WASI::PathFilestatSetTimes); + env->SetProtoMethod(tmpl, "path_link", WASI::PathLink); + env->SetProtoMethod(tmpl, "path_open", WASI::PathOpen); + env->SetProtoMethod(tmpl, "path_readlink", WASI::PathReadlink); + env->SetProtoMethod(tmpl, "path_remove_directory", WASI::PathRemoveDirectory); + env->SetProtoMethod(tmpl, "path_rename", WASI::PathRename); + env->SetProtoMethod(tmpl, "path_symlink", WASI::PathSymlink); + env->SetProtoMethod(tmpl, "path_unlink_file", WASI::PathUnlinkFile); + env->SetProtoMethod(tmpl, "poll_oneoff", WASI::PollOneoff); + env->SetProtoMethod(tmpl, "proc_exit", WASI::ProcExit); + env->SetProtoMethod(tmpl, "proc_raise", WASI::ProcRaise); + env->SetProtoMethod(tmpl, "random_get", WASI::RandomGet); + env->SetProtoMethod(tmpl, "sched_yield", WASI::SchedYield); + env->SetProtoMethod(tmpl, "sock_recv", WASI::SockRecv); + env->SetProtoMethod(tmpl, "sock_send", WASI::SockSend); + env->SetProtoMethod(tmpl, "sock_shutdown", WASI::SockShutdown); + + env->SetInstanceMethod(tmpl, "_setMemory", WASI::_SetMemory); + + target->Set(env->context(), + wasi_wrap_string, + tmpl->GetFunction(context).ToLocalChecked()).ToChecked(); +} + + +} // namespace wasi +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(wasi, node::wasi::Initialize) diff --git a/src/node_wasi.h b/src/node_wasi.h new file mode 100644 index 00000000000000..ca726e48a42a47 --- /dev/null +++ b/src/node_wasi.h @@ -0,0 +1,103 @@ +#ifndef SRC_NODE_WASI_H_ +#define SRC_NODE_WASI_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "base_object.h" +#include "memory_tracker-inl.h" +#include "uvwasi.h" + +namespace node { +namespace wasi { + + +class WASI : public BaseObject { + public: + WASI(Environment* env, + v8::Local object, + uvwasi_options_t* options); + static void New(const v8::FunctionCallbackInfo& args); + void MemoryInfo(MemoryTracker* tracker) const override { + /* TODO(cjihrig): Get memory consumption from uvwasi. */ + tracker->TrackField("memory", memory_); + } + + SET_MEMORY_INFO_NAME(WASI) + SET_SELF_SIZE(WASI) + + static void ArgsGet(const v8::FunctionCallbackInfo& args); + static void ArgsSizesGet(const v8::FunctionCallbackInfo& args); + static void ClockResGet(const v8::FunctionCallbackInfo& args); + static void ClockTimeGet(const v8::FunctionCallbackInfo& args); + static void EnvironGet(const v8::FunctionCallbackInfo& args); + static void EnvironSizesGet(const v8::FunctionCallbackInfo& args); + static void FdAdvise(const v8::FunctionCallbackInfo& args); + static void FdAllocate(const v8::FunctionCallbackInfo& args); + static void FdClose(const v8::FunctionCallbackInfo& args); + static void FdDatasync(const v8::FunctionCallbackInfo& args); + static void FdFdstatGet(const v8::FunctionCallbackInfo& args); + static void FdFdstatSetFlags(const v8::FunctionCallbackInfo& args); + static void FdFdstatSetRights( + const v8::FunctionCallbackInfo& args); + static void FdFilestatGet(const v8::FunctionCallbackInfo& args); + static void FdFilestatSetSize( + const v8::FunctionCallbackInfo& args); + static void FdFilestatSetTimes( + const v8::FunctionCallbackInfo& args); + static void FdPread(const v8::FunctionCallbackInfo& args); + static void FdPrestatGet(const v8::FunctionCallbackInfo& args); + static void FdPrestatDirName(const v8::FunctionCallbackInfo& args); + static void FdPwrite(const v8::FunctionCallbackInfo& args); + static void FdRead(const v8::FunctionCallbackInfo& args); + static void FdReaddir(const v8::FunctionCallbackInfo& args); + static void FdRenumber(const v8::FunctionCallbackInfo& args); + static void FdSeek(const v8::FunctionCallbackInfo& args); + static void FdSync(const v8::FunctionCallbackInfo& args); + static void FdTell(const v8::FunctionCallbackInfo& args); + static void FdWrite(const v8::FunctionCallbackInfo& args); + static void PathCreateDirectory( + const v8::FunctionCallbackInfo& args); + static void PathFilestatGet(const v8::FunctionCallbackInfo& args); + static void PathFilestatSetTimes( + const v8::FunctionCallbackInfo& args); + static void PathLink(const v8::FunctionCallbackInfo& args); + static void PathOpen(const v8::FunctionCallbackInfo& args); + static void PathReadlink(const v8::FunctionCallbackInfo& args); + static void PathRemoveDirectory( + const v8::FunctionCallbackInfo& args); + static void PathRename(const v8::FunctionCallbackInfo& args); + static void PathSymlink(const v8::FunctionCallbackInfo& args); + static void PathUnlinkFile(const v8::FunctionCallbackInfo& args); + static void PollOneoff(const v8::FunctionCallbackInfo& args); + static void ProcExit(const v8::FunctionCallbackInfo& args); + static void ProcRaise(const v8::FunctionCallbackInfo& args); + static void RandomGet(const v8::FunctionCallbackInfo& args); + static void SchedYield(const v8::FunctionCallbackInfo& args); + static void SockRecv(const v8::FunctionCallbackInfo& args); + static void SockSend(const v8::FunctionCallbackInfo& args); + static void SockShutdown(const v8::FunctionCallbackInfo& args); + + static void _SetMemory(const v8::FunctionCallbackInfo& args); + + private: + ~WASI() override; + inline void readUInt8(char* memory, uint8_t* value, uint32_t offset); + inline void readUInt16(char* memory, uint16_t* value, uint32_t offset); + inline void readUInt32(char* memory, uint32_t* value, uint32_t offset); + inline void readUInt64(char* memory, uint64_t* value, uint32_t offset); + inline void writeUInt8(char* memory, uint8_t value, uint32_t offset); + inline void writeUInt16(char* memory, uint16_t value, uint32_t offset); + inline void writeUInt32(char* memory, uint32_t value, uint32_t offset); + inline void writeUInt64(char* memory, uint64_t value, uint32_t offset); + uvwasi_errno_t backingStore(char** store, size_t* byte_length); + uvwasi_t uvw_; + v8::Global memory_; +}; + + +} // namespace wasi +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_WASI_H_ diff --git a/test/fixtures/outside.txt b/test/fixtures/outside.txt new file mode 100644 index 00000000000000..044c4b9614586d --- /dev/null +++ b/test/fixtures/outside.txt @@ -0,0 +1,2 @@ +this file is part of the WASI tests. it exists outside of the sandbox, and +should be inaccessible from the WASI tests. diff --git a/test/fixtures/wasi/input.txt b/test/fixtures/wasi/input.txt new file mode 100644 index 00000000000000..4c380537647f19 --- /dev/null +++ b/test/fixtures/wasi/input.txt @@ -0,0 +1 @@ +hello from input.txt diff --git a/test/fixtures/wasi/notadir b/test/fixtures/wasi/notadir new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/fixtures/wasi/simple-wasi.wasm b/test/fixtures/wasi/simple-wasi.wasm new file mode 100755 index 00000000000000..334d3a3dcac6c0 Binary files /dev/null and b/test/fixtures/wasi/simple-wasi.wasm differ diff --git a/test/fixtures/wasi/simple-wasi.wat b/test/fixtures/wasi/simple-wasi.wat new file mode 100644 index 00000000000000..318f390c76b1fa --- /dev/null +++ b/test/fixtures/wasi/simple-wasi.wat @@ -0,0 +1,6533 @@ +(module + (type (;0;) (func (param i32 i32 i32) (result i32))) + (type (;1;) (func (param i32 i64 i32) (result i64))) + (type (;2;) (func (param i32 i32) (result i32))) + (type (;3;) (func (param i32))) + (type (;4;) (func (param i32) (result i32))) + (type (;5;) (func (param i32 i32 i32 i32) (result i32))) + (type (;6;) (func (param i32 i64 i32 i32) (result i32))) + (type (;7;) (func)) + (type (;8;) (func (result i32))) + (import "wasi_unstable" "fd_prestat_get" (func (;0;) (type 2))) + (import "wasi_unstable" "fd_prestat_dir_name" (func (;1;) (type 0))) + (import "wasi_unstable" "environ_sizes_get" (func (;2;) (type 2))) + (import "wasi_unstable" "environ_get" (func (;3;) (type 2))) + (import "wasi_unstable" "args_sizes_get" (func (;4;) (type 2))) + (import "wasi_unstable" "args_get" (func (;5;) (type 2))) + (import "wasi_unstable" "proc_exit" (func (;6;) (type 3))) + (import "wasi_unstable" "fd_fdstat_get" (func (;7;) (type 2))) + (import "wasi_unstable" "fd_close" (func (;8;) (type 4))) + (import "wasi_unstable" "fd_write" (func (;9;) (type 5))) + (import "wasi_unstable" "fd_seek" (func (;10;) (type 6))) + (func (;11;) (type 7)) + (func (;12;) (type 7) + (local i32 i32 i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 0 + set_global 0 + call 23 + i32.const 3 + set_local 1 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + loop ;; label = @5 + get_local 1 + get_local 0 + call 0 + tee_local 2 + i32.const 8 + i32.eq + br_if 1 (;@4;) + get_local 2 + br_if 3 (;@2;) + block ;; label = @6 + get_local 0 + i32.load8_u + br_if 0 (;@6;) + get_local 0 + i32.load offset=4 + i32.const 1 + i32.add + call 14 + tee_local 2 + i32.eqz + br_if 4 (;@2;) + get_local 1 + get_local 2 + get_local 0 + i32.load offset=4 + call 1 + br_if 3 (;@3;) + get_local 2 + get_local 0 + i32.load offset=4 + i32.add + i32.const 0 + i32.store8 + get_local 1 + get_local 2 + call 24 + set_local 3 + get_local 2 + call 16 + get_local 3 + br_if 4 (;@2;) + end + get_local 1 + i32.const 1 + i32.add + tee_local 1 + br_if 0 (;@5;) + end + end + block ;; label = @4 + get_local 0 + get_local 0 + i32.const 12 + i32.add + call 2 + br_if 0 (;@4;) + i32.const 0 + get_local 0 + i32.load + i32.const 2 + i32.shl + i32.const 4 + i32.add + call 14 + i32.store offset=1544 + get_local 0 + i32.load offset=12 + call 14 + tee_local 1 + i32.eqz + br_if 0 (;@4;) + i32.const 0 + i32.load offset=1544 + tee_local 2 + i32.eqz + br_if 0 (;@4;) + get_local 2 + get_local 0 + i32.load + i32.const 2 + i32.shl + i32.add + i32.const 0 + i32.store + i32.const 0 + i32.load offset=1544 + get_local 1 + call 3 + br_if 0 (;@4;) + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + get_local 0 + i32.const 12 + i32.add + get_local 0 + call 4 + br_if 0 (;@7;) + get_local 0 + i32.load offset=12 + tee_local 1 + i32.eqz + br_if 1 (;@6;) + get_local 1 + i32.const 2 + i32.shl + i32.const 4 + i32.add + call 14 + set_local 1 + get_local 0 + i32.load + call 14 + set_local 2 + get_local 1 + i32.eqz + br_if 0 (;@7;) + get_local 2 + i32.eqz + br_if 0 (;@7;) + get_local 1 + i32.const 0 + i32.store + get_local 1 + get_local 2 + call 5 + i32.eqz + br_if 2 (;@5;) + end + i32.const 71 + call 19 + unreachable + end + end + call 11 + get_local 0 + i32.load offset=12 + get_local 1 + call 13 + set_local 1 + call 27 + get_local 1 + br_if 3 (;@1;) + get_local 0 + i32.const 16 + i32.add + set_global 0 + return + end + i32.const 71 + call 19 + unreachable + end + get_local 2 + call 16 + end + i32.const 71 + call 19 + unreachable + end + get_local 1 + call 19 + unreachable) + (func (;13;) (type 2) (param i32 i32) (result i32) + i32.const 1024 + call 35 + drop + i32.const 0) + (func (;14;) (type 4) (param i32) (result i32) + get_local 0 + call 15) + (func (;15;) (type 4) (param i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 1 + set_global 0 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + block ;; label = @11 + block ;; label = @12 + block ;; label = @13 + block ;; label = @14 + block ;; label = @15 + block ;; label = @16 + block ;; label = @17 + block ;; label = @18 + block ;; label = @19 + block ;; label = @20 + block ;; label = @21 + block ;; label = @22 + block ;; label = @23 + block ;; label = @24 + block ;; label = @25 + block ;; label = @26 + block ;; label = @27 + block ;; label = @28 + block ;; label = @29 + block ;; label = @30 + block ;; label = @31 + block ;; label = @32 + block ;; label = @33 + block ;; label = @34 + block ;; label = @35 + block ;; label = @36 + block ;; label = @37 + block ;; label = @38 + get_local 0 + i32.const 244 + i32.gt_u + br_if 0 (;@38;) + i32.const 0 + i32.load offset=1040 + tee_local 2 + i32.const 16 + get_local 0 + i32.const 11 + i32.add + i32.const -8 + i32.and + get_local 0 + i32.const 11 + i32.lt_u + select + tee_local 3 + i32.const 3 + i32.shr_u + tee_local 4 + i32.shr_u + tee_local 0 + i32.const 3 + i32.and + i32.eqz + br_if 1 (;@37;) + get_local 0 + i32.const -1 + i32.xor + i32.const 1 + i32.and + get_local 4 + i32.add + tee_local 5 + i32.const 3 + i32.shl + tee_local 6 + i32.const 1088 + i32.add + i32.load + tee_local 4 + i32.const 8 + i32.add + set_local 0 + get_local 4 + i32.load offset=8 + tee_local 3 + get_local 6 + i32.const 1080 + i32.add + tee_local 6 + i32.eq + br_if 2 (;@36;) + get_local 3 + get_local 6 + i32.store offset=12 + get_local 6 + i32.const 8 + i32.add + get_local 3 + i32.store + br 3 (;@35;) + end + i32.const -1 + set_local 3 + get_local 0 + i32.const -65 + i32.gt_u + br_if 14 (;@23;) + get_local 0 + i32.const 11 + i32.add + tee_local 0 + i32.const -8 + i32.and + set_local 3 + i32.const 0 + i32.load offset=1044 + tee_local 7 + i32.eqz + br_if 14 (;@23;) + i32.const 0 + set_local 8 + block ;; label = @38 + get_local 0 + i32.const 8 + i32.shr_u + tee_local 0 + i32.eqz + br_if 0 (;@38;) + i32.const 31 + set_local 8 + get_local 3 + i32.const 16777215 + i32.gt_u + br_if 0 (;@38;) + get_local 3 + i32.const 14 + get_local 0 + get_local 0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 4 + i32.shl + tee_local 0 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 5 + get_local 4 + i32.or + get_local 0 + get_local 5 + i32.shl + tee_local 0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + i32.sub + get_local 0 + get_local 4 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 0 + i32.const 1 + i32.shl + i32.or + set_local 8 + end + i32.const 0 + get_local 3 + i32.sub + set_local 5 + get_local 8 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + i32.load + tee_local 4 + i32.eqz + br_if 3 (;@34;) + get_local 3 + i32.const 0 + i32.const 25 + get_local 8 + i32.const 1 + i32.shr_u + i32.sub + get_local 8 + i32.const 31 + i32.eq + select + i32.shl + set_local 6 + i32.const 0 + set_local 0 + i32.const 0 + set_local 9 + loop ;; label = @38 + block ;; label = @39 + get_local 4 + i32.load offset=4 + i32.const -8 + i32.and + get_local 3 + i32.sub + tee_local 2 + get_local 5 + i32.ge_u + br_if 0 (;@39;) + get_local 2 + set_local 5 + get_local 4 + set_local 9 + get_local 2 + i32.eqz + br_if 8 (;@31;) + end + get_local 0 + get_local 4 + i32.const 20 + i32.add + i32.load + tee_local 2 + get_local 2 + get_local 4 + get_local 6 + i32.const 29 + i32.shr_u + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + i32.load + tee_local 4 + i32.eq + select + get_local 0 + get_local 2 + select + set_local 0 + get_local 6 + get_local 4 + i32.const 0 + i32.ne + i32.shl + set_local 6 + get_local 4 + br_if 0 (;@38;) + end + get_local 0 + get_local 9 + i32.or + i32.eqz + br_if 4 (;@33;) + br 11 (;@26;) + end + get_local 3 + i32.const 0 + i32.load offset=1048 + tee_local 7 + i32.le_u + br_if 13 (;@23;) + get_local 0 + i32.eqz + br_if 4 (;@32;) + get_local 0 + get_local 4 + i32.shl + i32.const 2 + get_local 4 + i32.shl + tee_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.or + i32.and + tee_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.and + i32.const -1 + i32.add + tee_local 0 + get_local 0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + tee_local 0 + i32.shr_u + tee_local 4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + tee_local 5 + get_local 0 + i32.or + get_local 4 + get_local 5 + i32.shr_u + tee_local 0 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + i32.add + tee_local 5 + i32.const 3 + i32.shl + tee_local 6 + i32.const 1088 + i32.add + i32.load + tee_local 4 + i32.load offset=8 + tee_local 0 + get_local 6 + i32.const 1080 + i32.add + tee_local 6 + i32.eq + br_if 6 (;@30;) + get_local 0 + get_local 6 + i32.store offset=12 + get_local 6 + i32.const 8 + i32.add + get_local 0 + i32.store + br 7 (;@29;) + end + i32.const 0 + get_local 2 + i32.const -2 + get_local 5 + i32.rotl + i32.and + i32.store offset=1040 + end + get_local 4 + get_local 5 + i32.const 3 + i32.shl + tee_local 5 + i32.const 3 + i32.or + i32.store offset=4 + get_local 4 + get_local 5 + i32.add + tee_local 4 + get_local 4 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + br 33 (;@1;) + end + i32.const 0 + set_local 0 + i32.const 0 + set_local 9 + i32.const 0 + i32.const 0 + i32.or + br_if 7 (;@26;) + end + i32.const 2 + get_local 8 + i32.shl + tee_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.or + get_local 7 + i32.and + tee_local 0 + i32.eqz + br_if 9 (;@23;) + get_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.and + i32.const -1 + i32.add + tee_local 0 + get_local 0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + tee_local 0 + i32.shr_u + tee_local 4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + tee_local 6 + get_local 0 + i32.or + get_local 4 + get_local 6 + i32.shr_u + tee_local 0 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 1344 + i32.add + i32.load + tee_local 0 + br_if 7 (;@25;) + br 8 (;@24;) + end + i32.const 0 + i32.load offset=1044 + tee_local 10 + i32.eqz + br_if 8 (;@23;) + get_local 10 + i32.const 0 + get_local 10 + i32.sub + i32.and + i32.const -1 + i32.add + tee_local 0 + get_local 0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + tee_local 0 + i32.shr_u + tee_local 4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + tee_local 5 + get_local 0 + i32.or + get_local 4 + get_local 5 + i32.shr_u + tee_local 0 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 1344 + i32.add + i32.load + tee_local 6 + i32.load offset=4 + i32.const -8 + i32.and + get_local 3 + i32.sub + set_local 5 + get_local 6 + tee_local 9 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 3 (;@28;) + i32.const 1 + set_local 4 + br 4 (;@27;) + end + i32.const 0 + set_local 5 + get_local 4 + set_local 9 + get_local 4 + set_local 0 + br 5 (;@25;) + end + i32.const 0 + get_local 2 + i32.const -2 + get_local 5 + i32.rotl + i32.and + tee_local 2 + i32.store offset=1040 + end + get_local 4 + i32.const 8 + i32.add + set_local 0 + get_local 4 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 4 + get_local 5 + i32.const 3 + i32.shl + tee_local 5 + i32.add + get_local 5 + get_local 3 + i32.sub + tee_local 5 + i32.store + get_local 4 + get_local 3 + i32.add + tee_local 6 + get_local 5 + i32.const 1 + i32.or + i32.store offset=4 + block ;; label = @29 + get_local 7 + i32.eqz + br_if 0 (;@29;) + get_local 7 + i32.const 3 + i32.shr_u + tee_local 9 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 3 + i32.const 0 + i32.load offset=1060 + set_local 4 + block ;; label = @30 + block ;; label = @31 + get_local 2 + i32.const 1 + get_local 9 + i32.shl + tee_local 9 + i32.and + i32.eqz + br_if 0 (;@31;) + get_local 3 + i32.load offset=8 + set_local 9 + br 1 (;@30;) + end + i32.const 0 + get_local 2 + get_local 9 + i32.or + i32.store offset=1040 + get_local 3 + set_local 9 + end + get_local 9 + get_local 4 + i32.store offset=12 + get_local 3 + get_local 4 + i32.store offset=8 + get_local 4 + get_local 3 + i32.store offset=12 + get_local 4 + get_local 9 + i32.store offset=8 + end + i32.const 0 + get_local 6 + i32.store offset=1060 + i32.const 0 + get_local 5 + i32.store offset=1048 + br 27 (;@1;) + end + i32.const 0 + set_local 4 + end + block ;; label = @27 + block ;; label = @28 + loop ;; label = @29 + block ;; label = @30 + block ;; label = @31 + block ;; label = @32 + block ;; label = @33 + get_local 4 + br_table 1 (;@32;) 0 (;@33;) 0 (;@33;) + end + get_local 0 + i32.load offset=4 + i32.const -8 + i32.and + get_local 3 + i32.sub + tee_local 4 + get_local 5 + get_local 4 + get_local 5 + i32.lt_u + tee_local 4 + select + set_local 5 + get_local 0 + get_local 6 + get_local 4 + select + set_local 6 + get_local 0 + tee_local 9 + i32.load offset=16 + tee_local 0 + br_if 1 (;@31;) + i32.const 0 + set_local 4 + br 3 (;@29;) + end + get_local 9 + i32.const 20 + i32.add + i32.load + tee_local 0 + br_if 1 (;@30;) + get_local 6 + get_local 3 + i32.add + tee_local 11 + get_local 6 + i32.le_u + br_if 8 (;@23;) + get_local 6 + i32.load offset=24 + set_local 12 + block ;; label = @32 + get_local 6 + i32.load offset=12 + tee_local 9 + get_local 6 + i32.eq + br_if 0 (;@32;) + get_local 6 + i32.load offset=8 + tee_local 0 + get_local 9 + i32.store offset=12 + get_local 9 + get_local 0 + i32.store offset=8 + get_local 12 + br_if 4 (;@28;) + br 5 (;@27;) + end + block ;; label = @32 + block ;; label = @33 + get_local 6 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 0 + br_if 0 (;@33;) + get_local 6 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 1 (;@32;) + get_local 6 + i32.const 16 + i32.add + set_local 4 + end + loop ;; label = @33 + get_local 4 + set_local 8 + get_local 0 + tee_local 9 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 0 + br_if 0 (;@33;) + get_local 9 + i32.const 16 + i32.add + set_local 4 + get_local 9 + i32.load offset=16 + tee_local 0 + br_if 0 (;@33;) + end + get_local 8 + i32.const 0 + i32.store + get_local 12 + i32.eqz + br_if 5 (;@27;) + br 4 (;@28;) + end + i32.const 0 + set_local 9 + get_local 12 + br_if 3 (;@28;) + br 4 (;@27;) + end + i32.const 1 + set_local 4 + br 1 (;@29;) + end + i32.const 1 + set_local 4 + br 0 (;@29;) + end + end + block ;; label = @28 + block ;; label = @29 + block ;; label = @30 + get_local 6 + get_local 6 + i32.load offset=28 + tee_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 0 + i32.load + i32.eq + br_if 0 (;@30;) + get_local 12 + i32.const 16 + i32.const 20 + get_local 12 + i32.load offset=16 + get_local 6 + i32.eq + select + i32.add + get_local 9 + i32.store + get_local 9 + br_if 1 (;@29;) + br 3 (;@27;) + end + get_local 0 + get_local 9 + i32.store + get_local 9 + i32.eqz + br_if 1 (;@28;) + end + get_local 9 + get_local 12 + i32.store offset=24 + block ;; label = @29 + get_local 6 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 0 (;@29;) + get_local 9 + get_local 0 + i32.store offset=16 + get_local 0 + get_local 9 + i32.store offset=24 + end + get_local 6 + i32.const 20 + i32.add + i32.load + tee_local 0 + i32.eqz + br_if 1 (;@27;) + get_local 9 + i32.const 20 + i32.add + get_local 0 + i32.store + get_local 0 + get_local 9 + i32.store offset=24 + br 1 (;@27;) + end + i32.const 0 + get_local 10 + i32.const -2 + get_local 4 + i32.rotl + i32.and + i32.store offset=1044 + end + block ;; label = @27 + block ;; label = @28 + get_local 5 + i32.const 15 + i32.gt_u + br_if 0 (;@28;) + get_local 6 + get_local 5 + get_local 3 + i32.add + tee_local 0 + i32.const 3 + i32.or + i32.store offset=4 + get_local 6 + get_local 0 + i32.add + tee_local 0 + get_local 0 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + br 1 (;@27;) + end + get_local 11 + get_local 5 + i32.const 1 + i32.or + i32.store offset=4 + get_local 6 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 11 + get_local 5 + i32.add + get_local 5 + i32.store + block ;; label = @28 + get_local 7 + i32.eqz + br_if 0 (;@28;) + get_local 7 + i32.const 3 + i32.shr_u + tee_local 3 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 4 + i32.const 0 + i32.load offset=1060 + set_local 0 + block ;; label = @29 + block ;; label = @30 + i32.const 1 + get_local 3 + i32.shl + tee_local 3 + get_local 2 + i32.and + i32.eqz + br_if 0 (;@30;) + get_local 4 + i32.load offset=8 + set_local 3 + br 1 (;@29;) + end + i32.const 0 + get_local 3 + get_local 2 + i32.or + i32.store offset=1040 + get_local 4 + set_local 3 + end + get_local 3 + get_local 0 + i32.store offset=12 + get_local 4 + get_local 0 + i32.store offset=8 + get_local 0 + get_local 4 + i32.store offset=12 + get_local 0 + get_local 3 + i32.store offset=8 + end + i32.const 0 + get_local 11 + i32.store offset=1060 + i32.const 0 + get_local 5 + i32.store offset=1048 + end + get_local 6 + i32.const 8 + i32.add + set_local 0 + br 25 (;@1;) + end + get_local 0 + i32.eqz + br_if 1 (;@24;) + end + loop ;; label = @25 + get_local 0 + i32.load offset=4 + i32.const -8 + i32.and + get_local 3 + i32.sub + tee_local 2 + get_local 5 + i32.lt_u + set_local 6 + block ;; label = @26 + get_local 0 + i32.load offset=16 + tee_local 4 + br_if 0 (;@26;) + get_local 0 + i32.const 20 + i32.add + i32.load + set_local 4 + end + get_local 2 + get_local 5 + get_local 6 + select + set_local 5 + get_local 0 + get_local 9 + get_local 6 + select + set_local 9 + get_local 4 + set_local 0 + get_local 4 + br_if 0 (;@25;) + end + end + get_local 9 + i32.eqz + br_if 0 (;@23;) + get_local 5 + i32.const 0 + i32.load offset=1048 + get_local 3 + i32.sub + i32.ge_u + br_if 0 (;@23;) + get_local 9 + get_local 3 + i32.add + tee_local 8 + get_local 9 + i32.le_u + br_if 0 (;@23;) + get_local 9 + i32.load offset=24 + set_local 10 + get_local 9 + i32.load offset=12 + tee_local 6 + get_local 9 + i32.eq + br_if 1 (;@22;) + get_local 9 + i32.load offset=8 + tee_local 0 + get_local 6 + i32.store offset=12 + get_local 6 + get_local 0 + i32.store offset=8 + get_local 10 + br_if 20 (;@3;) + br 21 (;@2;) + end + block ;; label = @23 + block ;; label = @24 + block ;; label = @25 + block ;; label = @26 + block ;; label = @27 + block ;; label = @28 + i32.const 0 + i32.load offset=1048 + tee_local 0 + get_local 3 + i32.ge_u + br_if 0 (;@28;) + i32.const 0 + i32.load offset=1052 + tee_local 6 + get_local 3 + i32.le_u + br_if 1 (;@27;) + i32.const 0 + i32.load offset=1064 + tee_local 0 + get_local 3 + i32.add + tee_local 4 + get_local 6 + get_local 3 + i32.sub + tee_local 5 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + get_local 5 + i32.store offset=1052 + i32.const 0 + get_local 4 + i32.store offset=1064 + get_local 0 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 0 + i32.const 8 + i32.add + set_local 0 + br 27 (;@1;) + end + i32.const 0 + i32.load offset=1060 + set_local 4 + get_local 0 + get_local 3 + i32.sub + tee_local 5 + i32.const 16 + i32.lt_u + br_if 1 (;@26;) + get_local 4 + get_local 3 + i32.add + tee_local 6 + get_local 5 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + get_local 5 + i32.store offset=1048 + i32.const 0 + get_local 6 + i32.store offset=1060 + get_local 4 + get_local 0 + i32.add + get_local 5 + i32.store + get_local 4 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + br 2 (;@25;) + end + i32.const 0 + i32.load offset=1512 + i32.eqz + br_if 2 (;@24;) + i32.const 0 + i32.load offset=1520 + set_local 4 + br 3 (;@23;) + end + get_local 4 + get_local 0 + i32.const 3 + i32.or + i32.store offset=4 + get_local 4 + get_local 0 + i32.add + tee_local 0 + get_local 0 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + i32.const 0 + i32.store offset=1060 + i32.const 0 + i32.const 0 + i32.store offset=1048 + end + get_local 4 + i32.const 8 + i32.add + set_local 0 + br 23 (;@1;) + end + i32.const 0 + i64.const -1 + i64.store offset=1524 align=4 + i32.const 0 + i64.const 281474976776192 + i64.store offset=1516 align=4 + i32.const 0 + get_local 1 + i32.const 12 + i32.add + i32.const -16 + i32.and + i32.const 1431655768 + i32.xor + i32.store offset=1512 + i32.const 0 + i32.const 0 + i32.store offset=1532 + i32.const 0 + i32.const 0 + i32.store offset=1484 + i32.const 65536 + set_local 4 + end + i32.const 0 + set_local 0 + block ;; label = @23 + block ;; label = @24 + get_local 4 + get_local 3 + i32.const 47 + i32.add + tee_local 7 + i32.add + tee_local 2 + i32.const 0 + get_local 4 + i32.sub + tee_local 8 + i32.and + tee_local 9 + get_local 3 + i32.le_u + br_if 0 (;@24;) + block ;; label = @25 + i32.const 0 + i32.load offset=1480 + tee_local 0 + i32.eqz + br_if 0 (;@25;) + i32.const 0 + i32.load offset=1472 + tee_local 4 + get_local 9 + i32.add + tee_local 5 + get_local 4 + i32.le_u + br_if 2 (;@23;) + get_local 5 + get_local 0 + i32.gt_u + br_if 2 (;@23;) + end + i32.const 0 + i32.load8_u offset=1484 + i32.const 4 + i32.and + br_if 10 (;@14;) + block ;; label = @25 + i32.const 0 + i32.load offset=1064 + tee_local 4 + i32.eqz + br_if 0 (;@25;) + i32.const 1488 + set_local 0 + loop ;; label = @26 + block ;; label = @27 + get_local 0 + i32.load + tee_local 5 + get_local 4 + i32.gt_u + br_if 0 (;@27;) + get_local 5 + get_local 0 + i32.load offset=4 + i32.add + get_local 4 + i32.gt_u + br_if 6 (;@21;) + end + get_local 0 + i32.load offset=8 + tee_local 0 + br_if 0 (;@26;) + end + end + i32.const 0 + call 25 + tee_local 6 + i32.const -1 + i32.eq + br_if 9 (;@15;) + get_local 9 + set_local 2 + block ;; label = @25 + i32.const 0 + i32.load offset=1516 + tee_local 0 + i32.const -1 + i32.add + tee_local 4 + get_local 6 + i32.and + i32.eqz + br_if 0 (;@25;) + get_local 9 + get_local 6 + i32.sub + get_local 4 + get_local 6 + i32.add + i32.const 0 + get_local 0 + i32.sub + i32.and + i32.add + set_local 2 + end + get_local 2 + get_local 3 + i32.le_u + br_if 9 (;@15;) + get_local 2 + i32.const 2147483646 + i32.gt_u + br_if 9 (;@15;) + block ;; label = @25 + i32.const 0 + i32.load offset=1480 + tee_local 0 + i32.eqz + br_if 0 (;@25;) + i32.const 0 + i32.load offset=1472 + tee_local 4 + get_local 2 + i32.add + tee_local 5 + get_local 4 + i32.le_u + br_if 10 (;@15;) + get_local 5 + get_local 0 + i32.gt_u + br_if 10 (;@15;) + end + get_local 2 + call 25 + tee_local 0 + get_local 6 + i32.ne + br_if 4 (;@20;) + br 11 (;@13;) + end + i32.const 0 + i32.const 48 + i32.store offset=1536 + br 22 (;@1;) + end + i32.const 0 + set_local 0 + i32.const 0 + i32.const 48 + i32.store offset=1536 + br 21 (;@1;) + end + block ;; label = @22 + get_local 9 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 0 + br_if 0 (;@22;) + get_local 9 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 3 (;@19;) + get_local 9 + i32.const 16 + i32.add + set_local 4 + end + loop ;; label = @22 + get_local 4 + set_local 2 + get_local 0 + tee_local 6 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 0 + br_if 0 (;@22;) + get_local 6 + i32.const 16 + i32.add + set_local 4 + get_local 6 + i32.load offset=16 + tee_local 0 + br_if 0 (;@22;) + end + get_local 2 + i32.const 0 + i32.store + get_local 10 + i32.eqz + br_if 19 (;@2;) + br 18 (;@3;) + end + get_local 2 + get_local 6 + i32.sub + get_local 8 + i32.and + tee_local 2 + i32.const 2147483646 + i32.gt_u + br_if 5 (;@15;) + get_local 2 + call 25 + tee_local 6 + get_local 0 + i32.load + get_local 0 + i32.load offset=4 + i32.add + i32.eq + br_if 3 (;@17;) + get_local 6 + set_local 0 + end + get_local 0 + set_local 6 + get_local 3 + i32.const 48 + i32.add + get_local 2 + i32.le_u + br_if 1 (;@18;) + get_local 2 + i32.const 2147483646 + i32.gt_u + br_if 1 (;@18;) + get_local 6 + i32.const -1 + i32.eq + br_if 1 (;@18;) + get_local 7 + get_local 2 + i32.sub + i32.const 0 + i32.load offset=1520 + tee_local 0 + i32.add + i32.const 0 + get_local 0 + i32.sub + i32.and + tee_local 0 + i32.const 2147483646 + i32.gt_u + br_if 6 (;@13;) + get_local 0 + call 25 + i32.const -1 + i32.eq + br_if 3 (;@16;) + get_local 0 + get_local 2 + i32.add + set_local 2 + br 6 (;@13;) + end + i32.const 0 + set_local 6 + get_local 10 + br_if 15 (;@3;) + br 16 (;@2;) + end + get_local 6 + i32.const -1 + i32.ne + br_if 4 (;@13;) + br 2 (;@15;) + end + get_local 6 + i32.const -1 + i32.ne + br_if 3 (;@13;) + br 1 (;@15;) + end + i32.const 0 + get_local 2 + i32.sub + call 25 + drop + end + i32.const 0 + i32.const 0 + i32.load offset=1484 + i32.const 4 + i32.or + i32.store offset=1484 + end + get_local 9 + i32.const 2147483646 + i32.gt_u + br_if 1 (;@12;) + get_local 9 + call 25 + tee_local 6 + i32.const 0 + call 25 + tee_local 0 + i32.ge_u + br_if 1 (;@12;) + get_local 6 + i32.const -1 + i32.eq + br_if 1 (;@12;) + get_local 0 + i32.const -1 + i32.eq + br_if 1 (;@12;) + get_local 0 + get_local 6 + i32.sub + tee_local 2 + get_local 3 + i32.const 40 + i32.add + i32.le_u + br_if 1 (;@12;) + end + i32.const 0 + i32.const 0 + i32.load offset=1472 + get_local 2 + i32.add + tee_local 0 + i32.store offset=1472 + block ;; label = @13 + get_local 0 + i32.const 0 + i32.load offset=1476 + i32.le_u + br_if 0 (;@13;) + i32.const 0 + get_local 0 + i32.store offset=1476 + end + block ;; label = @13 + block ;; label = @14 + block ;; label = @15 + block ;; label = @16 + i32.const 0 + i32.load offset=1064 + tee_local 4 + i32.eqz + br_if 0 (;@16;) + i32.const 1488 + set_local 0 + loop ;; label = @17 + get_local 6 + get_local 0 + i32.load + tee_local 5 + get_local 0 + i32.load offset=4 + tee_local 9 + i32.add + i32.eq + br_if 2 (;@15;) + get_local 0 + i32.load offset=8 + tee_local 0 + br_if 0 (;@17;) + br 3 (;@14;) + end + end + block ;; label = @16 + block ;; label = @17 + i32.const 0 + i32.load offset=1056 + tee_local 0 + i32.eqz + br_if 0 (;@17;) + get_local 6 + get_local 0 + i32.ge_u + br_if 1 (;@16;) + end + i32.const 0 + get_local 6 + i32.store offset=1056 + end + i32.const 0 + set_local 0 + i32.const 0 + get_local 2 + i32.store offset=1492 + i32.const 0 + get_local 6 + i32.store offset=1488 + i32.const 0 + i32.const -1 + i32.store offset=1072 + i32.const 0 + i32.const 0 + i32.load offset=1512 + i32.store offset=1076 + i32.const 0 + i32.const 0 + i32.store offset=1500 + loop ;; label = @16 + get_local 0 + i32.const 1088 + i32.add + get_local 0 + i32.const 1080 + i32.add + tee_local 4 + i32.store + get_local 0 + i32.const 1092 + i32.add + get_local 4 + i32.store + get_local 0 + i32.const 8 + i32.add + tee_local 0 + i32.const 256 + i32.ne + br_if 0 (;@16;) + end + get_local 6 + i32.const -8 + get_local 6 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 6 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + tee_local 0 + i32.add + tee_local 4 + get_local 2 + i32.const -40 + i32.add + tee_local 5 + get_local 0 + i32.sub + tee_local 0 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + i32.const 0 + i32.load offset=1528 + i32.store offset=1068 + i32.const 0 + get_local 0 + i32.store offset=1052 + i32.const 0 + get_local 4 + i32.store offset=1064 + get_local 6 + get_local 5 + i32.add + i32.const 40 + i32.store offset=4 + br 2 (;@13;) + end + get_local 0 + i32.load8_u offset=12 + i32.const 8 + i32.and + br_if 0 (;@14;) + get_local 6 + get_local 4 + i32.le_u + br_if 0 (;@14;) + get_local 5 + get_local 4 + i32.gt_u + br_if 0 (;@14;) + get_local 4 + i32.const -8 + get_local 4 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 4 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + tee_local 5 + i32.add + tee_local 6 + i32.const 0 + i32.load offset=1052 + get_local 2 + i32.add + tee_local 8 + get_local 5 + i32.sub + tee_local 5 + i32.const 1 + i32.or + i32.store offset=4 + get_local 0 + i32.const 4 + i32.add + get_local 9 + get_local 2 + i32.add + i32.store + i32.const 0 + i32.const 0 + i32.load offset=1528 + i32.store offset=1068 + i32.const 0 + get_local 5 + i32.store offset=1052 + i32.const 0 + get_local 6 + i32.store offset=1064 + get_local 4 + get_local 8 + i32.add + i32.const 40 + i32.store offset=4 + br 1 (;@13;) + end + block ;; label = @14 + get_local 6 + i32.const 0 + i32.load offset=1056 + i32.ge_u + br_if 0 (;@14;) + i32.const 0 + get_local 6 + i32.store offset=1056 + end + get_local 6 + get_local 2 + i32.add + set_local 5 + i32.const 1488 + set_local 0 + block ;; label = @14 + block ;; label = @15 + block ;; label = @16 + block ;; label = @17 + block ;; label = @18 + block ;; label = @19 + block ;; label = @20 + block ;; label = @21 + loop ;; label = @22 + get_local 0 + i32.load + get_local 5 + i32.eq + br_if 1 (;@21;) + get_local 0 + i32.load offset=8 + tee_local 0 + br_if 0 (;@22;) + br 2 (;@20;) + end + end + get_local 0 + i32.load8_u offset=12 + i32.const 8 + i32.and + br_if 0 (;@20;) + get_local 0 + get_local 6 + i32.store + get_local 0 + get_local 0 + i32.load offset=4 + get_local 2 + i32.add + i32.store offset=4 + get_local 6 + i32.const -8 + get_local 6 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 6 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + i32.add + tee_local 2 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 5 + i32.const -8 + get_local 5 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 5 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + i32.add + tee_local 6 + get_local 2 + i32.sub + get_local 3 + i32.sub + set_local 0 + get_local 2 + get_local 3 + i32.add + set_local 5 + get_local 4 + get_local 6 + i32.eq + br_if 1 (;@19;) + i32.const 0 + i32.load offset=1060 + get_local 6 + i32.eq + br_if 9 (;@11;) + get_local 6 + i32.load offset=4 + tee_local 4 + i32.const 3 + i32.and + i32.const 1 + i32.ne + br_if 15 (;@5;) + get_local 4 + i32.const -8 + i32.and + set_local 7 + get_local 4 + i32.const 255 + i32.gt_u + br_if 10 (;@10;) + get_local 6 + i32.load offset=12 + tee_local 3 + get_local 6 + i32.load offset=8 + tee_local 9 + i32.eq + br_if 11 (;@9;) + get_local 3 + get_local 9 + i32.store offset=8 + get_local 9 + get_local 3 + i32.store offset=12 + br 14 (;@6;) + end + i32.const 1488 + set_local 0 + block ;; label = @20 + loop ;; label = @21 + block ;; label = @22 + get_local 0 + i32.load + tee_local 5 + get_local 4 + i32.gt_u + br_if 0 (;@22;) + get_local 5 + get_local 0 + i32.load offset=4 + i32.add + tee_local 5 + get_local 4 + i32.gt_u + br_if 2 (;@20;) + end + get_local 0 + i32.load offset=8 + set_local 0 + br 0 (;@21;) + end + end + get_local 6 + i32.const -8 + get_local 6 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 6 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + tee_local 0 + i32.add + tee_local 8 + get_local 2 + i32.const -40 + i32.add + tee_local 9 + get_local 0 + i32.sub + tee_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 6 + get_local 9 + i32.add + i32.const 40 + i32.store offset=4 + get_local 4 + get_local 5 + i32.const 39 + get_local 5 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 5 + i32.const -39 + i32.add + i32.const 7 + i32.and + select + i32.add + i32.const -47 + i32.add + tee_local 9 + get_local 9 + get_local 4 + i32.const 16 + i32.add + i32.lt_u + select + tee_local 9 + i32.const 27 + i32.store offset=4 + i32.const 0 + i32.const 0 + i32.load offset=1528 + i32.store offset=1068 + i32.const 0 + get_local 0 + i32.store offset=1052 + i32.const 0 + get_local 8 + i32.store offset=1064 + get_local 9 + i32.const 16 + i32.add + i32.const 0 + i64.load offset=1496 align=4 + i64.store align=4 + get_local 9 + i32.const 0 + i64.load offset=1488 align=4 + i64.store offset=8 align=4 + i32.const 0 + get_local 2 + i32.store offset=1492 + i32.const 0 + get_local 6 + i32.store offset=1488 + i32.const 0 + get_local 9 + i32.const 8 + i32.add + i32.store offset=1496 + i32.const 0 + i32.const 0 + i32.store offset=1500 + get_local 9 + i32.const 28 + i32.add + set_local 0 + loop ;; label = @20 + get_local 0 + i32.const 7 + i32.store + get_local 0 + i32.const 4 + i32.add + tee_local 0 + get_local 5 + i32.lt_u + br_if 0 (;@20;) + end + get_local 9 + get_local 4 + i32.eq + br_if 6 (;@13;) + get_local 9 + i32.const 4 + i32.add + tee_local 0 + get_local 0 + i32.load + i32.const -2 + i32.and + i32.store + get_local 9 + get_local 9 + get_local 4 + i32.sub + tee_local 2 + i32.store + get_local 4 + get_local 2 + i32.const 1 + i32.or + i32.store offset=4 + block ;; label = @20 + get_local 2 + i32.const 255 + i32.gt_u + br_if 0 (;@20;) + get_local 2 + i32.const 3 + i32.shr_u + tee_local 5 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 0 + i32.const 0 + i32.load offset=1040 + tee_local 6 + i32.const 1 + get_local 5 + i32.shl + tee_local 5 + i32.and + i32.eqz + br_if 2 (;@18;) + get_local 0 + i32.load offset=8 + set_local 5 + br 3 (;@17;) + end + i32.const 0 + set_local 0 + block ;; label = @20 + get_local 2 + i32.const 8 + i32.shr_u + tee_local 5 + i32.eqz + br_if 0 (;@20;) + i32.const 31 + set_local 0 + get_local 2 + i32.const 16777215 + i32.gt_u + br_if 0 (;@20;) + get_local 2 + i32.const 14 + get_local 5 + get_local 5 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 0 + i32.shl + tee_local 5 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 6 + get_local 0 + i32.or + get_local 5 + get_local 6 + i32.shl + tee_local 0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 5 + i32.or + i32.sub + get_local 0 + get_local 5 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 0 + i32.const 1 + i32.shl + i32.or + set_local 0 + end + get_local 4 + i64.const 0 + i64.store offset=16 align=4 + get_local 4 + i32.const 28 + i32.add + get_local 0 + i32.store + get_local 0 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + set_local 5 + i32.const 0 + i32.load offset=1044 + tee_local 6 + i32.const 1 + get_local 0 + i32.shl + tee_local 9 + i32.and + i32.eqz + br_if 3 (;@16;) + get_local 2 + i32.const 0 + i32.const 25 + get_local 0 + i32.const 1 + i32.shr_u + i32.sub + get_local 0 + i32.const 31 + i32.eq + select + i32.shl + set_local 0 + get_local 5 + i32.load + set_local 6 + loop ;; label = @20 + get_local 6 + tee_local 5 + i32.load offset=4 + i32.const -8 + i32.and + get_local 2 + i32.eq + br_if 6 (;@14;) + get_local 0 + i32.const 29 + i32.shr_u + set_local 6 + get_local 0 + i32.const 1 + i32.shl + set_local 0 + get_local 5 + get_local 6 + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + tee_local 9 + i32.load + tee_local 6 + br_if 0 (;@20;) + end + get_local 9 + get_local 4 + i32.store + get_local 4 + i32.const 24 + i32.add + get_local 5 + i32.store + br 4 (;@15;) + end + i32.const 0 + get_local 5 + i32.store offset=1064 + i32.const 0 + i32.const 0 + i32.load offset=1052 + get_local 0 + i32.add + tee_local 0 + i32.store offset=1052 + get_local 5 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + br 14 (;@4;) + end + i32.const 0 + get_local 6 + get_local 5 + i32.or + i32.store offset=1040 + get_local 0 + set_local 5 + end + get_local 5 + get_local 4 + i32.store offset=12 + get_local 0 + get_local 4 + i32.store offset=8 + get_local 4 + get_local 0 + i32.store offset=12 + get_local 4 + get_local 5 + i32.store offset=8 + br 3 (;@13;) + end + get_local 5 + get_local 4 + i32.store + i32.const 0 + get_local 6 + get_local 9 + i32.or + i32.store offset=1044 + get_local 4 + i32.const 24 + i32.add + get_local 5 + i32.store + end + get_local 4 + get_local 4 + i32.store offset=12 + get_local 4 + get_local 4 + i32.store offset=8 + br 1 (;@13;) + end + get_local 5 + i32.load offset=8 + tee_local 0 + get_local 4 + i32.store offset=12 + get_local 5 + get_local 4 + i32.store offset=8 + get_local 4 + i32.const 24 + i32.add + i32.const 0 + i32.store + get_local 4 + get_local 5 + i32.store offset=12 + get_local 4 + get_local 0 + i32.store offset=8 + end + i32.const 0 + i32.load offset=1052 + tee_local 0 + get_local 3 + i32.le_u + br_if 0 (;@12;) + i32.const 0 + i32.load offset=1064 + tee_local 4 + get_local 3 + i32.add + tee_local 5 + get_local 0 + get_local 3 + i32.sub + tee_local 0 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + get_local 0 + i32.store offset=1052 + i32.const 0 + get_local 5 + i32.store offset=1064 + get_local 4 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 4 + i32.const 8 + i32.add + set_local 0 + br 11 (;@1;) + end + i32.const 0 + set_local 0 + i32.const 0 + i32.const 48 + i32.store offset=1536 + br 10 (;@1;) + end + i32.const 0 + get_local 5 + i32.store offset=1060 + i32.const 0 + i32.const 0 + i32.load offset=1048 + get_local 0 + i32.add + tee_local 0 + i32.store offset=1048 + get_local 5 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 5 + get_local 0 + i32.add + get_local 0 + i32.store + br 6 (;@4;) + end + get_local 6 + i32.load offset=24 + set_local 10 + get_local 6 + i32.load offset=12 + tee_local 9 + get_local 6 + i32.eq + br_if 1 (;@8;) + get_local 6 + i32.load offset=8 + tee_local 4 + get_local 9 + i32.store offset=12 + get_local 9 + get_local 4 + i32.store offset=8 + get_local 10 + br_if 2 (;@7;) + br 3 (;@6;) + end + i32.const 0 + i32.const 0 + i32.load offset=1040 + i32.const -2 + get_local 4 + i32.const 3 + i32.shr_u + i32.rotl + i32.and + i32.store offset=1040 + br 2 (;@6;) + end + block ;; label = @8 + block ;; label = @9 + get_local 6 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 3 + br_if 0 (;@9;) + get_local 6 + i32.const 16 + i32.add + tee_local 4 + i32.load + tee_local 3 + i32.eqz + br_if 1 (;@8;) + end + loop ;; label = @9 + get_local 4 + set_local 8 + get_local 3 + tee_local 9 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 3 + br_if 0 (;@9;) + get_local 9 + i32.const 16 + i32.add + set_local 4 + get_local 9 + i32.load offset=16 + tee_local 3 + br_if 0 (;@9;) + end + get_local 8 + i32.const 0 + i32.store + get_local 10 + i32.eqz + br_if 2 (;@6;) + br 1 (;@7;) + end + i32.const 0 + set_local 9 + get_local 10 + i32.eqz + br_if 1 (;@6;) + end + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + get_local 6 + i32.load offset=28 + tee_local 3 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 4 + i32.load + get_local 6 + i32.eq + br_if 0 (;@9;) + get_local 10 + i32.const 16 + i32.const 20 + get_local 10 + i32.load offset=16 + get_local 6 + i32.eq + select + i32.add + get_local 9 + i32.store + get_local 9 + br_if 1 (;@8;) + br 3 (;@6;) + end + get_local 4 + get_local 9 + i32.store + get_local 9 + i32.eqz + br_if 1 (;@7;) + end + get_local 9 + get_local 10 + i32.store offset=24 + block ;; label = @8 + get_local 6 + i32.load offset=16 + tee_local 4 + i32.eqz + br_if 0 (;@8;) + get_local 9 + get_local 4 + i32.store offset=16 + get_local 4 + get_local 9 + i32.store offset=24 + end + get_local 6 + i32.const 20 + i32.add + i32.load + tee_local 4 + i32.eqz + br_if 1 (;@6;) + get_local 9 + i32.const 20 + i32.add + get_local 4 + i32.store + get_local 4 + get_local 9 + i32.store offset=24 + br 1 (;@6;) + end + i32.const 0 + i32.const 0 + i32.load offset=1044 + i32.const -2 + get_local 3 + i32.rotl + i32.and + i32.store offset=1044 + end + get_local 7 + get_local 0 + i32.add + set_local 0 + get_local 6 + get_local 7 + i32.add + set_local 6 + end + get_local 6 + get_local 6 + i32.load offset=4 + i32.const -2 + i32.and + i32.store offset=4 + get_local 5 + get_local 0 + i32.add + get_local 0 + i32.store + get_local 5 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + get_local 0 + i32.const 255 + i32.gt_u + br_if 0 (;@10;) + get_local 0 + i32.const 3 + i32.shr_u + tee_local 4 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 0 + i32.const 0 + i32.load offset=1040 + tee_local 3 + i32.const 1 + get_local 4 + i32.shl + tee_local 4 + i32.and + i32.eqz + br_if 1 (;@9;) + get_local 0 + i32.load offset=8 + set_local 4 + br 2 (;@8;) + end + i32.const 0 + set_local 4 + block ;; label = @10 + get_local 0 + i32.const 8 + i32.shr_u + tee_local 3 + i32.eqz + br_if 0 (;@10;) + i32.const 31 + set_local 4 + get_local 0 + i32.const 16777215 + i32.gt_u + br_if 0 (;@10;) + get_local 0 + i32.const 14 + get_local 3 + get_local 3 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 4 + i32.shl + tee_local 3 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 6 + get_local 4 + i32.or + get_local 3 + get_local 6 + i32.shl + tee_local 4 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 3 + i32.or + i32.sub + get_local 4 + get_local 3 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 4 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 4 + i32.const 1 + i32.shl + i32.or + set_local 4 + end + get_local 5 + get_local 4 + i32.store offset=28 + get_local 5 + i64.const 0 + i64.store offset=16 align=4 + get_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + set_local 3 + i32.const 0 + i32.load offset=1044 + tee_local 6 + i32.const 1 + get_local 4 + i32.shl + tee_local 9 + i32.and + i32.eqz + br_if 2 (;@7;) + get_local 0 + i32.const 0 + i32.const 25 + get_local 4 + i32.const 1 + i32.shr_u + i32.sub + get_local 4 + i32.const 31 + i32.eq + select + i32.shl + set_local 4 + get_local 3 + i32.load + set_local 6 + loop ;; label = @10 + get_local 6 + tee_local 3 + i32.load offset=4 + i32.const -8 + i32.and + get_local 0 + i32.eq + br_if 5 (;@5;) + get_local 4 + i32.const 29 + i32.shr_u + set_local 6 + get_local 4 + i32.const 1 + i32.shl + set_local 4 + get_local 3 + get_local 6 + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + tee_local 9 + i32.load + tee_local 6 + br_if 0 (;@10;) + end + get_local 9 + get_local 5 + i32.store + get_local 5 + get_local 3 + i32.store offset=24 + br 3 (;@6;) + end + i32.const 0 + get_local 3 + get_local 4 + i32.or + i32.store offset=1040 + get_local 0 + set_local 4 + end + get_local 4 + get_local 5 + i32.store offset=12 + get_local 0 + get_local 5 + i32.store offset=8 + get_local 5 + get_local 0 + i32.store offset=12 + get_local 5 + get_local 4 + i32.store offset=8 + br 3 (;@4;) + end + get_local 3 + get_local 5 + i32.store + i32.const 0 + get_local 6 + get_local 9 + i32.or + i32.store offset=1044 + get_local 5 + get_local 3 + i32.store offset=24 + end + get_local 5 + get_local 5 + i32.store offset=12 + get_local 5 + get_local 5 + i32.store offset=8 + br 1 (;@4;) + end + get_local 3 + i32.load offset=8 + tee_local 0 + get_local 5 + i32.store offset=12 + get_local 3 + get_local 5 + i32.store offset=8 + get_local 5 + i32.const 0 + i32.store offset=24 + get_local 5 + get_local 3 + i32.store offset=12 + get_local 5 + get_local 0 + i32.store offset=8 + end + get_local 2 + i32.const 8 + i32.add + set_local 0 + br 2 (;@1;) + end + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + get_local 9 + get_local 9 + i32.load offset=28 + tee_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 0 + i32.load + i32.eq + br_if 0 (;@5;) + get_local 10 + i32.const 16 + i32.const 20 + get_local 10 + i32.load offset=16 + get_local 9 + i32.eq + select + i32.add + get_local 6 + i32.store + get_local 6 + br_if 1 (;@4;) + br 3 (;@2;) + end + get_local 0 + get_local 6 + i32.store + get_local 6 + i32.eqz + br_if 1 (;@3;) + end + get_local 6 + get_local 10 + i32.store offset=24 + block ;; label = @4 + get_local 9 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 0 (;@4;) + get_local 6 + get_local 0 + i32.store offset=16 + get_local 0 + get_local 6 + i32.store offset=24 + end + get_local 9 + i32.const 20 + i32.add + i32.load + tee_local 0 + i32.eqz + br_if 1 (;@2;) + get_local 6 + i32.const 20 + i32.add + get_local 0 + i32.store + get_local 0 + get_local 6 + i32.store offset=24 + br 1 (;@2;) + end + i32.const 0 + get_local 7 + i32.const -2 + get_local 4 + i32.rotl + i32.and + tee_local 7 + i32.store offset=1044 + end + block ;; label = @2 + block ;; label = @3 + get_local 5 + i32.const 15 + i32.gt_u + br_if 0 (;@3;) + get_local 9 + get_local 5 + get_local 3 + i32.add + tee_local 0 + i32.const 3 + i32.or + i32.store offset=4 + get_local 9 + get_local 0 + i32.add + tee_local 0 + get_local 0 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + br 1 (;@2;) + end + get_local 8 + get_local 5 + i32.const 1 + i32.or + i32.store offset=4 + get_local 9 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 8 + get_local 5 + i32.add + get_local 5 + i32.store + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + get_local 5 + i32.const 255 + i32.gt_u + br_if 0 (;@7;) + get_local 5 + i32.const 3 + i32.shr_u + tee_local 4 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 0 + i32.const 0 + i32.load offset=1040 + tee_local 5 + i32.const 1 + get_local 4 + i32.shl + tee_local 4 + i32.and + i32.eqz + br_if 1 (;@6;) + get_local 0 + i32.load offset=8 + set_local 4 + br 2 (;@5;) + end + get_local 5 + i32.const 8 + i32.shr_u + tee_local 4 + i32.eqz + br_if 2 (;@4;) + i32.const 31 + set_local 0 + get_local 5 + i32.const 16777215 + i32.gt_u + br_if 3 (;@3;) + get_local 5 + i32.const 14 + get_local 4 + get_local 4 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 0 + i32.shl + tee_local 4 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 3 + get_local 0 + i32.or + get_local 4 + get_local 3 + i32.shl + tee_local 0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + i32.sub + get_local 0 + get_local 4 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 0 + i32.const 1 + i32.shl + i32.or + set_local 0 + br 3 (;@3;) + end + i32.const 0 + get_local 5 + get_local 4 + i32.or + i32.store offset=1040 + get_local 0 + set_local 4 + end + get_local 4 + get_local 8 + i32.store offset=12 + get_local 0 + get_local 8 + i32.store offset=8 + get_local 8 + get_local 0 + i32.store offset=12 + get_local 8 + get_local 4 + i32.store offset=8 + br 2 (;@2;) + end + i32.const 0 + set_local 0 + end + get_local 8 + get_local 0 + i32.store offset=28 + get_local 8 + i64.const 0 + i64.store offset=16 align=4 + get_local 0 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + set_local 4 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + get_local 7 + i32.const 1 + get_local 0 + i32.shl + tee_local 3 + i32.and + i32.eqz + br_if 0 (;@5;) + get_local 5 + i32.const 0 + i32.const 25 + get_local 0 + i32.const 1 + i32.shr_u + i32.sub + get_local 0 + i32.const 31 + i32.eq + select + i32.shl + set_local 0 + get_local 4 + i32.load + set_local 3 + loop ;; label = @6 + get_local 3 + tee_local 4 + i32.load offset=4 + i32.const -8 + i32.and + get_local 5 + i32.eq + br_if 3 (;@3;) + get_local 0 + i32.const 29 + i32.shr_u + set_local 3 + get_local 0 + i32.const 1 + i32.shl + set_local 0 + get_local 4 + get_local 3 + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + tee_local 6 + i32.load + tee_local 3 + br_if 0 (;@6;) + end + get_local 6 + get_local 8 + i32.store + get_local 8 + get_local 4 + i32.store offset=24 + br 1 (;@4;) + end + get_local 4 + get_local 8 + i32.store + i32.const 0 + get_local 7 + get_local 3 + i32.or + i32.store offset=1044 + get_local 8 + get_local 4 + i32.store offset=24 + end + get_local 8 + get_local 8 + i32.store offset=12 + get_local 8 + get_local 8 + i32.store offset=8 + br 1 (;@2;) + end + get_local 4 + i32.load offset=8 + tee_local 0 + get_local 8 + i32.store offset=12 + get_local 4 + get_local 8 + i32.store offset=8 + get_local 8 + i32.const 0 + i32.store offset=24 + get_local 8 + get_local 4 + i32.store offset=12 + get_local 8 + get_local 0 + i32.store offset=8 + end + get_local 9 + i32.const 8 + i32.add + set_local 0 + end + get_local 1 + i32.const 16 + i32.add + set_global 0 + get_local 0) + (func (;16;) (type 3) (param i32) + get_local 0 + call 17) + (func (;17;) (type 3) (param i32) + (local i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + block ;; label = @2 + get_local 0 + i32.eqz + br_if 0 (;@2;) + get_local 0 + i32.const -8 + i32.add + tee_local 1 + get_local 0 + i32.const -4 + i32.add + i32.load + tee_local 2 + i32.const -8 + i32.and + tee_local 0 + i32.add + set_local 3 + block ;; label = @3 + block ;; label = @4 + get_local 2 + i32.const 1 + i32.and + br_if 0 (;@4;) + get_local 2 + i32.const 3 + i32.and + i32.eqz + br_if 2 (;@2;) + get_local 1 + get_local 1 + i32.load + tee_local 2 + i32.sub + tee_local 1 + i32.const 0 + i32.load offset=1056 + i32.lt_u + br_if 2 (;@2;) + get_local 2 + get_local 0 + i32.add + set_local 0 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + i32.const 0 + i32.load offset=1060 + get_local 1 + i32.eq + br_if 0 (;@9;) + get_local 2 + i32.const 255 + i32.gt_u + br_if 1 (;@8;) + get_local 1 + i32.load offset=12 + tee_local 4 + get_local 1 + i32.load offset=8 + tee_local 5 + i32.eq + br_if 2 (;@7;) + get_local 4 + get_local 5 + i32.store offset=8 + get_local 5 + get_local 4 + i32.store offset=12 + get_local 1 + get_local 3 + i32.lt_u + br_if 6 (;@3;) + br 7 (;@2;) + end + get_local 3 + i32.load offset=4 + tee_local 2 + i32.const 3 + i32.and + i32.const 3 + i32.ne + br_if 4 (;@4;) + get_local 3 + i32.const 4 + i32.add + get_local 2 + i32.const -2 + i32.and + i32.store + i32.const 0 + get_local 0 + i32.store offset=1048 + get_local 1 + get_local 0 + i32.add + get_local 0 + i32.store + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + return + end + get_local 1 + i32.load offset=24 + set_local 6 + get_local 1 + i32.load offset=12 + tee_local 5 + get_local 1 + i32.eq + br_if 1 (;@6;) + get_local 1 + i32.load offset=8 + tee_local 2 + get_local 5 + i32.store offset=12 + get_local 5 + get_local 2 + i32.store offset=8 + get_local 6 + br_if 2 (;@5;) + br 3 (;@4;) + end + i32.const 0 + i32.const 0 + i32.load offset=1040 + i32.const -2 + get_local 2 + i32.const 3 + i32.shr_u + i32.rotl + i32.and + i32.store offset=1040 + get_local 1 + get_local 3 + i32.lt_u + br_if 3 (;@3;) + br 4 (;@2;) + end + block ;; label = @6 + block ;; label = @7 + get_local 1 + i32.const 20 + i32.add + tee_local 2 + i32.load + tee_local 4 + br_if 0 (;@7;) + get_local 1 + i32.const 16 + i32.add + tee_local 2 + i32.load + tee_local 4 + i32.eqz + br_if 1 (;@6;) + end + loop ;; label = @7 + get_local 2 + set_local 7 + get_local 4 + tee_local 5 + i32.const 20 + i32.add + tee_local 2 + i32.load + tee_local 4 + br_if 0 (;@7;) + get_local 5 + i32.const 16 + i32.add + set_local 2 + get_local 5 + i32.load offset=16 + tee_local 4 + br_if 0 (;@7;) + end + get_local 7 + i32.const 0 + i32.store + get_local 6 + i32.eqz + br_if 2 (;@4;) + br 1 (;@5;) + end + i32.const 0 + set_local 5 + get_local 6 + i32.eqz + br_if 1 (;@4;) + end + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + get_local 1 + i32.load offset=28 + tee_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 2 + i32.load + get_local 1 + i32.eq + br_if 0 (;@7;) + get_local 6 + i32.const 16 + i32.const 20 + get_local 6 + i32.load offset=16 + get_local 1 + i32.eq + select + i32.add + get_local 5 + i32.store + get_local 5 + br_if 1 (;@6;) + br 3 (;@4;) + end + get_local 2 + get_local 5 + i32.store + get_local 5 + i32.eqz + br_if 1 (;@5;) + end + get_local 5 + get_local 6 + i32.store offset=24 + block ;; label = @6 + get_local 1 + i32.load offset=16 + tee_local 2 + i32.eqz + br_if 0 (;@6;) + get_local 5 + get_local 2 + i32.store offset=16 + get_local 2 + get_local 5 + i32.store offset=24 + end + get_local 1 + i32.const 20 + i32.add + i32.load + tee_local 2 + i32.eqz + br_if 1 (;@4;) + get_local 5 + i32.const 20 + i32.add + get_local 2 + i32.store + get_local 2 + get_local 5 + i32.store offset=24 + get_local 1 + get_local 3 + i32.lt_u + br_if 2 (;@3;) + br 3 (;@2;) + end + i32.const 0 + i32.const 0 + i32.load offset=1044 + i32.const -2 + get_local 4 + i32.rotl + i32.and + i32.store offset=1044 + end + get_local 1 + get_local 3 + i32.ge_u + br_if 1 (;@2;) + end + get_local 3 + i32.load offset=4 + tee_local 2 + i32.const 1 + i32.and + i32.eqz + br_if 0 (;@2;) + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + block ;; label = @11 + get_local 2 + i32.const 2 + i32.and + br_if 0 (;@11;) + i32.const 0 + i32.load offset=1064 + get_local 3 + i32.eq + br_if 1 (;@10;) + i32.const 0 + i32.load offset=1060 + get_local 3 + i32.eq + br_if 2 (;@9;) + get_local 2 + i32.const -8 + i32.and + get_local 0 + i32.add + set_local 0 + get_local 2 + i32.const 255 + i32.gt_u + br_if 3 (;@8;) + get_local 3 + i32.load offset=12 + tee_local 4 + get_local 3 + i32.load offset=8 + tee_local 5 + i32.eq + br_if 4 (;@7;) + get_local 4 + get_local 5 + i32.store offset=8 + get_local 5 + get_local 4 + i32.store offset=12 + br 7 (;@4;) + end + get_local 3 + i32.const 4 + i32.add + get_local 2 + i32.const -2 + i32.and + i32.store + get_local 1 + get_local 0 + i32.add + get_local 0 + i32.store + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + br 7 (;@3;) + end + i32.const 0 + get_local 1 + i32.store offset=1064 + i32.const 0 + i32.const 0 + i32.load offset=1052 + get_local 0 + i32.add + tee_local 0 + i32.store offset=1052 + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 1 + i32.const 0 + i32.load offset=1060 + i32.ne + br_if 7 (;@2;) + i32.const 0 + i32.const 0 + i32.store offset=1048 + i32.const 0 + i32.const 0 + i32.store offset=1060 + return + end + i32.const 0 + get_local 1 + i32.store offset=1060 + i32.const 0 + i32.const 0 + i32.load offset=1048 + get_local 0 + i32.add + tee_local 0 + i32.store offset=1048 + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 1 + get_local 0 + i32.add + get_local 0 + i32.store + return + end + get_local 3 + i32.load offset=24 + set_local 6 + get_local 3 + i32.load offset=12 + tee_local 5 + get_local 3 + i32.eq + br_if 1 (;@6;) + get_local 3 + i32.load offset=8 + tee_local 2 + get_local 5 + i32.store offset=12 + get_local 5 + get_local 2 + i32.store offset=8 + get_local 6 + br_if 2 (;@5;) + br 3 (;@4;) + end + i32.const 0 + i32.const 0 + i32.load offset=1040 + i32.const -2 + get_local 2 + i32.const 3 + i32.shr_u + i32.rotl + i32.and + i32.store offset=1040 + br 2 (;@4;) + end + block ;; label = @6 + block ;; label = @7 + get_local 3 + i32.const 20 + i32.add + tee_local 2 + i32.load + tee_local 4 + br_if 0 (;@7;) + get_local 3 + i32.const 16 + i32.add + tee_local 2 + i32.load + tee_local 4 + i32.eqz + br_if 1 (;@6;) + end + loop ;; label = @7 + get_local 2 + set_local 7 + get_local 4 + tee_local 5 + i32.const 20 + i32.add + tee_local 2 + i32.load + tee_local 4 + br_if 0 (;@7;) + get_local 5 + i32.const 16 + i32.add + set_local 2 + get_local 5 + i32.load offset=16 + tee_local 4 + br_if 0 (;@7;) + end + get_local 7 + i32.const 0 + i32.store + get_local 6 + i32.eqz + br_if 2 (;@4;) + br 1 (;@5;) + end + i32.const 0 + set_local 5 + get_local 6 + i32.eqz + br_if 1 (;@4;) + end + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + get_local 3 + i32.load offset=28 + tee_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 2 + i32.load + get_local 3 + i32.eq + br_if 0 (;@7;) + get_local 6 + i32.const 16 + i32.const 20 + get_local 6 + i32.load offset=16 + get_local 3 + i32.eq + select + i32.add + get_local 5 + i32.store + get_local 5 + br_if 1 (;@6;) + br 3 (;@4;) + end + get_local 2 + get_local 5 + i32.store + get_local 5 + i32.eqz + br_if 1 (;@5;) + end + get_local 5 + get_local 6 + i32.store offset=24 + block ;; label = @6 + get_local 3 + i32.load offset=16 + tee_local 2 + i32.eqz + br_if 0 (;@6;) + get_local 5 + get_local 2 + i32.store offset=16 + get_local 2 + get_local 5 + i32.store offset=24 + end + get_local 3 + i32.const 20 + i32.add + i32.load + tee_local 2 + i32.eqz + br_if 1 (;@4;) + get_local 5 + i32.const 20 + i32.add + get_local 2 + i32.store + get_local 2 + get_local 5 + i32.store offset=24 + br 1 (;@4;) + end + i32.const 0 + i32.const 0 + i32.load offset=1044 + i32.const -2 + get_local 4 + i32.rotl + i32.and + i32.store offset=1044 + end + get_local 1 + get_local 0 + i32.add + get_local 0 + i32.store + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 1 + i32.const 0 + i32.load offset=1060 + i32.ne + br_if 0 (;@3;) + i32.const 0 + get_local 0 + i32.store offset=1048 + return + end + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + get_local 0 + i32.const 255 + i32.gt_u + br_if 0 (;@9;) + get_local 0 + i32.const 3 + i32.shr_u + tee_local 2 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 0 + i32.const 0 + i32.load offset=1040 + tee_local 4 + i32.const 1 + get_local 2 + i32.shl + tee_local 2 + i32.and + i32.eqz + br_if 1 (;@8;) + get_local 0 + i32.load offset=8 + set_local 2 + br 2 (;@7;) + end + i32.const 0 + set_local 2 + block ;; label = @9 + get_local 0 + i32.const 8 + i32.shr_u + tee_local 4 + i32.eqz + br_if 0 (;@9;) + i32.const 31 + set_local 2 + get_local 0 + i32.const 16777215 + i32.gt_u + br_if 0 (;@9;) + get_local 0 + i32.const 14 + get_local 4 + get_local 4 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 2 + i32.shl + tee_local 4 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 5 + get_local 2 + i32.or + get_local 4 + get_local 5 + i32.shl + tee_local 2 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + i32.sub + get_local 2 + get_local 4 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 2 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 2 + i32.const 1 + i32.shl + i32.or + set_local 2 + end + get_local 1 + i64.const 0 + i64.store offset=16 align=4 + get_local 1 + i32.const 28 + i32.add + get_local 2 + i32.store + get_local 2 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + set_local 4 + i32.const 0 + i32.load offset=1044 + tee_local 5 + i32.const 1 + get_local 2 + i32.shl + tee_local 3 + i32.and + i32.eqz + br_if 2 (;@6;) + get_local 0 + i32.const 0 + i32.const 25 + get_local 2 + i32.const 1 + i32.shr_u + i32.sub + get_local 2 + i32.const 31 + i32.eq + select + i32.shl + set_local 2 + get_local 4 + i32.load + set_local 5 + loop ;; label = @9 + get_local 5 + tee_local 4 + i32.load offset=4 + i32.const -8 + i32.and + get_local 0 + i32.eq + br_if 5 (;@4;) + get_local 2 + i32.const 29 + i32.shr_u + set_local 5 + get_local 2 + i32.const 1 + i32.shl + set_local 2 + get_local 4 + get_local 5 + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + tee_local 3 + i32.load + tee_local 5 + br_if 0 (;@9;) + end + get_local 3 + get_local 1 + i32.store + get_local 1 + i32.const 24 + i32.add + get_local 4 + i32.store + br 3 (;@5;) + end + i32.const 0 + get_local 4 + get_local 2 + i32.or + i32.store offset=1040 + get_local 0 + set_local 2 + end + get_local 2 + get_local 1 + i32.store offset=12 + get_local 0 + get_local 1 + i32.store offset=8 + get_local 1 + get_local 0 + i32.store offset=12 + get_local 1 + get_local 2 + i32.store offset=8 + return + end + get_local 4 + get_local 1 + i32.store + i32.const 0 + get_local 5 + get_local 3 + i32.or + i32.store offset=1044 + get_local 1 + i32.const 24 + i32.add + get_local 4 + i32.store + end + get_local 1 + get_local 1 + i32.store offset=12 + get_local 1 + get_local 1 + i32.store offset=8 + br 1 (;@3;) + end + get_local 4 + i32.load offset=8 + tee_local 0 + get_local 1 + i32.store offset=12 + get_local 4 + get_local 1 + i32.store offset=8 + get_local 1 + i32.const 24 + i32.add + i32.const 0 + i32.store + get_local 1 + get_local 4 + i32.store offset=12 + get_local 1 + get_local 0 + i32.store offset=8 + end + i32.const 0 + i32.const 0 + i32.load offset=1072 + i32.const -1 + i32.add + tee_local 1 + i32.store offset=1072 + get_local 1 + i32.eqz + br_if 1 (;@1;) + end + return + end + i32.const 1496 + set_local 1 + loop ;; label = @1 + get_local 1 + i32.load + tee_local 0 + i32.const 8 + i32.add + set_local 1 + get_local 0 + br_if 0 (;@1;) + end + i32.const 0 + i32.const -1 + i32.store offset=1072) + (func (;18;) (type 2) (param i32 i32) (result i32) + (local i32) + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 0 + i32.eqz + br_if 0 (;@3;) + get_local 1 + get_local 0 + i32.mul + set_local 2 + block ;; label = @4 + get_local 1 + get_local 0 + i32.or + i32.const 65536 + i32.lt_u + br_if 0 (;@4;) + get_local 2 + i32.const -1 + get_local 2 + get_local 0 + i32.div_u + get_local 1 + i32.eq + select + set_local 2 + end + get_local 2 + call 15 + tee_local 0 + br_if 1 (;@2;) + br 2 (;@1;) + end + i32.const 0 + set_local 2 + i32.const 0 + call 15 + tee_local 0 + i32.eqz + br_if 1 (;@1;) + end + get_local 0 + i32.const -4 + i32.add + i32.load8_u + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@1;) + get_local 0 + i32.const 0 + get_local 2 + call 44 + drop + end + get_local 0) + (func (;19;) (type 3) (param i32) + get_local 0 + call 6 + unreachable) + (func (;20;) (type 7) + unreachable + unreachable) + (func (;21;) (type 4) (param i32) (result i32) + block ;; label = @1 + get_local 0 + call 8 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + i32.const 0 + get_local 0 + i32.store offset=1536 + i32.const -1 + return + end + i32.const 0) + (func (;22;) (type 3) (param i32) + (local i32 i32) + block ;; label = @1 + get_local 0 + i32.load + i32.const 0 + i32.le_s + br_if 0 (;@1;) + get_local 0 + i32.load offset=12 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.gt_u + br_if 0 (;@1;) + get_local 0 + i32.load offset=4 + set_local 0 + block ;; label = @2 + get_local 2 + i32.eqz + br_if 0 (;@2;) + get_local 0 + i32.eqz + br_if 1 (;@1;) + end + block ;; label = @2 + get_local 1 + i32.eqz + br_if 0 (;@2;) + i32.const 0 + set_local 2 + loop ;; label = @3 + get_local 0 + i32.load + i32.eqz + br_if 2 (;@1;) + get_local 0 + i32.const 4 + i32.add + i32.load + i32.const -1 + i32.le_s + br_if 2 (;@1;) + get_local 0 + i32.const 24 + i32.add + set_local 0 + get_local 2 + i32.const 1 + i32.add + tee_local 2 + get_local 1 + i32.lt_u + br_if 0 (;@3;) + end + end + return + end + call 20 + unreachable) + (func (;23;) (type 7) + (local i32 i32) + block ;; label = @1 + i32.const 16 + call 14 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + get_local 0 + i32.const 24 + i32.const 4 + call 18 + tee_local 1 + i32.store offset=4 + block ;; label = @2 + get_local 1 + i32.eqz + br_if 0 (;@2;) + get_local 0 + i64.const 4 + i64.store offset=8 align=4 + get_local 0 + i32.const 1 + i32.store + get_local 0 + call 22 + i32.const 0 + get_local 0 + i32.store offset=1540 + get_local 0 + call 22 + return + end + get_local 0 + call 16 + end + i32.const 0 + i32.const 0 + i32.store offset=1540 + unreachable + unreachable) + (func (;24;) (type 2) (param i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32) + get_global 0 + i32.const 32 + i32.sub + tee_local 2 + set_global 0 + i32.const 0 + i32.load offset=1540 + call 22 + i32.const -1 + set_local 3 + block ;; label = @1 + get_local 1 + i32.eqz + br_if 0 (;@1;) + i32.const 0 + i32.load offset=1540 + tee_local 4 + call 22 + get_local 0 + i32.const 0 + i32.lt_s + br_if 0 (;@1;) + block ;; label = @2 + block ;; label = @3 + get_local 4 + i32.load offset=12 + tee_local 5 + get_local 4 + i32.load offset=8 + i32.ne + br_if 0 (;@3;) + i32.const 24 + get_local 5 + i32.const 1 + i32.shl + call 18 + tee_local 6 + i32.eqz + br_if 2 (;@1;) + get_local 6 + get_local 4 + i32.load offset=4 + get_local 4 + i32.const 12 + i32.add + tee_local 5 + i32.load + i32.const 24 + i32.mul + call 43 + drop + get_local 4 + i32.load offset=4 + call 16 + get_local 4 + get_local 6 + i32.store offset=4 + get_local 4 + i32.const 8 + i32.add + tee_local 7 + get_local 7 + i32.load + i32.const 1 + i32.shl + i32.store + get_local 5 + i32.load + set_local 5 + br 1 (;@2;) + end + get_local 4 + i32.load offset=4 + set_local 6 + end + get_local 4 + i32.const 12 + i32.add + get_local 5 + i32.const 1 + i32.add + i32.store + get_local 1 + call 45 + set_local 7 + get_local 6 + get_local 5 + i32.const 24 + i32.mul + i32.add + tee_local 1 + get_local 0 + i32.store offset=4 + get_local 1 + get_local 7 + i32.store + block ;; label = @2 + get_local 0 + get_local 2 + i32.const 8 + i32.add + call 7 + tee_local 0 + i32.eqz + br_if 0 (;@2;) + i32.const 0 + get_local 0 + i32.store offset=1536 + br 1 (;@1;) + end + get_local 1 + get_local 2 + i64.load offset=16 + i64.store offset=8 + get_local 1 + get_local 2 + i64.load offset=24 + i64.store offset=16 + get_local 4 + call 22 + get_local 4 + call 22 + i32.const 0 + set_local 3 + i32.const 0 + get_local 4 + i32.store offset=1540 + end + get_local 2 + i32.const 32 + i32.add + set_global 0 + get_local 3) + (func (;25;) (type 4) (param i32) (result i32) + block ;; label = @1 + get_local 0 + i32.const 65535 + i32.and + br_if 0 (;@1;) + get_local 0 + i32.const -1 + i32.le_s + br_if 0 (;@1;) + block ;; label = @2 + get_local 0 + i32.const 16 + i32.shr_u + grow_memory + tee_local 0 + i32.const -1 + i32.eq + br_if 0 (;@2;) + get_local 0 + i32.const 16 + i32.shl + return + end + i32.const 0 + i32.const 48 + i32.store offset=1536 + i32.const -1 + return + end + call 20 + unreachable) + (func (;26;) (type 7)) + (func (;27;) (type 7) + call 26 + call 29) + (func (;28;) (type 8) (result i32) + i32.const 1548) + (func (;29;) (type 7) + (local i32 i32 i32) + block ;; label = @1 + call 28 + i32.load + tee_local 0 + i32.eqz + br_if 0 (;@1;) + loop ;; label = @2 + block ;; label = @3 + get_local 0 + i32.load offset=20 + get_local 0 + i32.load offset=24 + i32.eq + br_if 0 (;@3;) + get_local 0 + i32.const 0 + i32.const 0 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + drop + end + block ;; label = @3 + get_local 0 + i32.load offset=4 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.eq + br_if 0 (;@3;) + get_local 0 + get_local 1 + get_local 2 + i32.sub + i64.extend_s/i32 + i32.const 0 + get_local 0 + i32.load offset=36 + call_indirect (type 1) + drop + end + get_local 0 + i32.load offset=52 + tee_local 0 + br_if 0 (;@2;) + end + end + block ;; label = @1 + i32.const 0 + i32.load offset=1552 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + block ;; label = @2 + get_local 0 + i32.load offset=20 + get_local 0 + i32.load offset=24 + i32.eq + br_if 0 (;@2;) + get_local 0 + i32.const 0 + i32.const 0 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + drop + end + get_local 0 + i32.load offset=4 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.eq + br_if 0 (;@1;) + get_local 0 + get_local 1 + get_local 2 + i32.sub + i64.extend_s/i32 + i32.const 0 + get_local 0 + i32.load offset=36 + call_indirect (type 1) + drop + end + block ;; label = @1 + i32.const 0 + i32.load offset=2712 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + block ;; label = @2 + get_local 0 + i32.load offset=20 + get_local 0 + i32.load offset=24 + i32.eq + br_if 0 (;@2;) + get_local 0 + i32.const 0 + i32.const 0 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + drop + end + get_local 0 + i32.load offset=4 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.eq + br_if 0 (;@1;) + get_local 0 + get_local 1 + get_local 2 + i32.sub + i64.extend_s/i32 + i32.const 0 + get_local 0 + i32.load offset=36 + call_indirect (type 1) + drop + end + block ;; label = @1 + i32.const 0 + i32.load offset=1552 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + block ;; label = @2 + get_local 0 + i32.load offset=20 + get_local 0 + i32.load offset=24 + i32.eq + br_if 0 (;@2;) + get_local 0 + i32.const 0 + i32.const 0 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + drop + end + get_local 0 + i32.load offset=4 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.eq + br_if 0 (;@1;) + get_local 0 + get_local 1 + get_local 2 + i32.sub + i64.extend_s/i32 + i32.const 0 + get_local 0 + i32.load offset=36 + call_indirect (type 1) + drop + end) + (func (;30;) (type 4) (param i32) (result i32) + (local i32) + get_local 0 + get_local 0 + i32.load offset=60 + tee_local 1 + i32.const -1 + i32.add + get_local 1 + i32.or + i32.store offset=60 + block ;; label = @1 + get_local 0 + i32.load + tee_local 1 + i32.const 8 + i32.and + br_if 0 (;@1;) + get_local 0 + i64.const 0 + i64.store offset=4 align=4 + get_local 0 + get_local 0 + i32.load offset=40 + tee_local 1 + i32.store offset=24 + get_local 0 + get_local 1 + i32.store offset=20 + get_local 0 + get_local 1 + get_local 0 + i32.load offset=44 + i32.add + i32.store offset=16 + i32.const 0 + return + end + get_local 0 + get_local 1 + i32.const 32 + i32.or + i32.store + i32.const -1) + (func (;31;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32) + block ;; label = @1 + block ;; label = @2 + get_local 2 + i32.load offset=16 + tee_local 3 + br_if 0 (;@2;) + i32.const 0 + set_local 6 + get_local 2 + call 30 + br_if 1 (;@1;) + get_local 2 + i32.const 16 + i32.add + i32.load + set_local 3 + end + block ;; label = @2 + get_local 3 + get_local 2 + i32.load offset=20 + tee_local 4 + i32.sub + get_local 1 + i32.ge_u + br_if 0 (;@2;) + get_local 2 + get_local 0 + get_local 1 + get_local 2 + i32.load offset=32 + call_indirect (type 0) + return + end + i32.const 0 + set_local 5 + block ;; label = @2 + get_local 2 + i32.load offset=64 + i32.const 0 + i32.lt_s + br_if 0 (;@2;) + i32.const 0 + set_local 5 + get_local 0 + set_local 6 + i32.const 0 + set_local 3 + loop ;; label = @3 + get_local 1 + get_local 3 + i32.eq + br_if 1 (;@2;) + get_local 3 + i32.const 1 + i32.add + set_local 3 + get_local 6 + get_local 1 + i32.add + set_local 7 + get_local 6 + i32.const -1 + i32.add + tee_local 8 + set_local 6 + get_local 7 + i32.const -1 + i32.add + i32.load8_u + i32.const 10 + i32.ne + br_if 0 (;@3;) + end + get_local 2 + get_local 0 + get_local 1 + get_local 3 + i32.sub + i32.const 1 + i32.add + tee_local 5 + get_local 2 + i32.load offset=32 + call_indirect (type 0) + tee_local 6 + get_local 5 + i32.lt_u + br_if 1 (;@1;) + get_local 8 + get_local 1 + i32.add + i32.const 1 + i32.add + set_local 0 + get_local 2 + i32.const 20 + i32.add + i32.load + set_local 4 + get_local 3 + i32.const -1 + i32.add + set_local 1 + end + get_local 4 + get_local 0 + get_local 1 + call 43 + drop + get_local 2 + i32.const 20 + i32.add + tee_local 3 + get_local 3 + i32.load + get_local 1 + i32.add + i32.store + get_local 5 + get_local 1 + i32.add + return + end + get_local 6) + (func (;32;) (type 5) (param i32 i32 i32 i32) (result i32) + (local i32) + block ;; label = @1 + get_local 0 + get_local 2 + get_local 1 + i32.mul + tee_local 4 + get_local 3 + call 31 + tee_local 0 + get_local 4 + i32.ne + br_if 0 (;@1;) + get_local 2 + i32.const 0 + get_local 1 + select + return + end + get_local 0 + get_local 1 + i32.div_u) + (func (;33;) (type 2) (param i32 i32) (result i32) + (local i32) + i32.const -1 + i32.const 0 + get_local 0 + call 46 + tee_local 2 + get_local 0 + i32.const 1 + get_local 2 + get_local 1 + call 32 + i32.ne + select) + (func (;34;) (type 2) (param i32 i32) (result i32) + (local i32 i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 2 + set_global 0 + get_local 2 + get_local 1 + i32.store8 offset=15 + block ;; label = @1 + block ;; label = @2 + get_local 0 + i32.load offset=16 + tee_local 3 + br_if 0 (;@2;) + i32.const -1 + set_local 3 + get_local 0 + call 30 + br_if 1 (;@1;) + get_local 0 + i32.const 16 + i32.add + i32.load + set_local 3 + end + block ;; label = @2 + block ;; label = @3 + get_local 0 + i32.load offset=20 + tee_local 4 + get_local 3 + i32.eq + br_if 0 (;@3;) + get_local 0 + i32.load offset=64 + get_local 1 + i32.const 255 + i32.and + tee_local 3 + i32.ne + br_if 1 (;@2;) + end + i32.const -1 + set_local 3 + get_local 0 + get_local 2 + i32.const 15 + i32.add + i32.const 1 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + i32.const 1 + i32.ne + br_if 1 (;@1;) + get_local 2 + i32.load8_u offset=15 + set_local 3 + br 1 (;@1;) + end + get_local 0 + i32.const 20 + i32.add + get_local 4 + i32.const 1 + i32.add + i32.store + get_local 4 + get_local 1 + i32.store8 + end + get_local 2 + i32.const 16 + i32.add + set_global 0 + get_local 3) + (func (;35;) (type 4) (param i32) (result i32) + block ;; label = @1 + get_local 0 + i32.const 2600 + call 33 + i32.const 0 + i32.lt_s + br_if 0 (;@1;) + block ;; label = @2 + i32.const 0 + i32.load offset=2664 + i32.const 10 + i32.eq + br_if 0 (;@2;) + i32.const 0 + i32.load offset=2620 + tee_local 0 + i32.const 0 + i32.load offset=2616 + i32.eq + br_if 0 (;@2;) + i32.const 0 + get_local 0 + i32.const 1 + i32.add + i32.store offset=2620 + get_local 0 + i32.const 10 + i32.store8 + i32.const 0 + return + end + i32.const 2600 + i32.const 10 + call 34 + i32.const 31 + i32.shr_s + return + end + i32.const -1) + (func (;36;) (type 4) (param i32) (result i32) + get_local 0 + i32.load offset=56 + call 21) + (func (;37;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 3 + set_global 0 + i32.const -1 + set_local 4 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 2 + i32.const -1 + i32.le_s + br_if 0 (;@3;) + get_local 0 + get_local 1 + get_local 2 + get_local 3 + i32.const 12 + i32.add + call 9 + tee_local 2 + i32.eqz + br_if 1 (;@2;) + i32.const 0 + get_local 2 + i32.store offset=1536 + i32.const -1 + set_local 4 + br 2 (;@1;) + end + i32.const 0 + i32.const 28 + i32.store offset=1536 + br 1 (;@1;) + end + get_local 3 + i32.load offset=12 + set_local 4 + end + get_local 3 + i32.const 16 + i32.add + set_global 0 + get_local 4) + (func (;38;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 3 + set_global 0 + get_local 3 + get_local 2 + i32.store offset=12 + get_local 3 + get_local 1 + i32.store offset=8 + get_local 3 + get_local 0 + i32.load offset=24 + tee_local 1 + i32.store + get_local 3 + get_local 0 + i32.load offset=20 + get_local 1 + i32.sub + tee_local 1 + i32.store offset=4 + i32.const 2 + set_local 4 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 1 + get_local 2 + i32.add + tee_local 5 + get_local 0 + i32.load offset=56 + get_local 3 + i32.const 2 + call 37 + tee_local 6 + i32.eq + br_if 0 (;@3;) + get_local 3 + set_local 1 + get_local 0 + i32.const 56 + i32.add + set_local 7 + loop ;; label = @4 + get_local 6 + i32.const -1 + i32.le_s + br_if 2 (;@2;) + get_local 1 + i32.const 8 + i32.add + get_local 1 + get_local 6 + get_local 1 + i32.load offset=4 + tee_local 8 + i32.gt_u + tee_local 9 + select + tee_local 1 + get_local 1 + i32.load + get_local 6 + get_local 8 + i32.const 0 + get_local 9 + select + i32.sub + tee_local 8 + i32.add + i32.store + get_local 1 + get_local 1 + i32.load offset=4 + get_local 8 + i32.sub + i32.store offset=4 + get_local 5 + get_local 6 + i32.sub + set_local 5 + get_local 7 + i32.load + get_local 1 + get_local 4 + get_local 9 + i32.sub + tee_local 4 + call 37 + tee_local 9 + set_local 6 + get_local 5 + get_local 9 + i32.ne + br_if 0 (;@4;) + end + end + get_local 0 + i32.const 24 + i32.add + get_local 0 + i32.load offset=40 + tee_local 1 + i32.store + get_local 0 + i32.const 20 + i32.add + get_local 1 + i32.store + get_local 0 + get_local 1 + get_local 0 + i32.load offset=44 + i32.add + i32.store offset=16 + get_local 2 + set_local 6 + br 1 (;@1;) + end + get_local 0 + i64.const 0 + i64.store offset=16 + i32.const 0 + set_local 6 + get_local 0 + i32.const 24 + i32.add + i32.const 0 + i32.store + get_local 0 + get_local 0 + i32.load + i32.const 32 + i32.or + i32.store + get_local 4 + i32.const 2 + i32.eq + br_if 0 (;@1;) + get_local 2 + get_local 1 + i32.load offset=4 + i32.sub + set_local 6 + end + get_local 3 + i32.const 16 + i32.add + set_global 0 + get_local 6) + (func (;39;) (type 4) (param i32) (result i32) + (local i32 i32) + get_global 0 + i32.const 32 + i32.sub + tee_local 1 + set_global 0 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 0 + get_local 1 + i32.const 8 + i32.add + call 7 + tee_local 0 + br_if 0 (;@3;) + i32.const 59 + set_local 0 + get_local 1 + i32.load8_u offset=8 + i32.const 2 + i32.ne + br_if 0 (;@3;) + get_local 1 + i32.load8_u offset=16 + i32.const 36 + i32.and + i32.eqz + br_if 1 (;@2;) + end + i32.const 0 + set_local 2 + i32.const 0 + get_local 0 + i32.store offset=1536 + br 1 (;@1;) + end + i32.const 1 + set_local 2 + end + get_local 1 + i32.const 32 + i32.add + set_global 0 + get_local 2) + (func (;40;) (type 0) (param i32 i32 i32) (result i32) + get_local 0 + i32.const 1 + i32.store offset=32 + block ;; label = @1 + block ;; label = @2 + get_local 0 + i32.load8_u + i32.const 64 + i32.and + br_if 0 (;@2;) + get_local 0 + i32.load offset=56 + call 39 + i32.eqz + br_if 1 (;@1;) + end + get_local 0 + get_local 1 + get_local 2 + call 38 + return + end + get_local 0 + i32.const -1 + i32.store offset=64 + get_local 0 + get_local 1 + get_local 2 + call 38) + (func (;41;) (type 1) (param i32 i64 i32) (result i64) + (local i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 3 + set_global 0 + block ;; label = @1 + block ;; label = @2 + get_local 0 + get_local 1 + get_local 2 + i32.const 255 + i32.and + get_local 3 + i32.const 8 + i32.add + call 10 + tee_local 0 + i32.eqz + br_if 0 (;@2;) + i32.const 0 + i32.const 70 + get_local 0 + get_local 0 + i32.const 76 + i32.eq + select + i32.store offset=1536 + i64.const -1 + set_local 1 + br 1 (;@1;) + end + get_local 3 + i64.load offset=8 + set_local 1 + end + get_local 3 + i32.const 16 + i32.add + set_global 0 + get_local 1) + (func (;42;) (type 1) (param i32 i64 i32) (result i64) + get_local 0 + i32.load offset=56 + get_local 1 + get_local 2 + call 41) + (func (;43;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + get_local 2 + i32.eqz + br_if 0 (;@4;) + get_local 1 + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@4;) + get_local 0 + set_local 3 + block ;; label = @5 + loop ;; label = @6 + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 2 + i32.const -1 + i32.add + set_local 4 + get_local 3 + i32.const 1 + i32.add + set_local 3 + get_local 1 + i32.const 1 + i32.add + set_local 1 + get_local 2 + i32.const 1 + i32.eq + br_if 1 (;@5;) + get_local 4 + set_local 2 + get_local 1 + i32.const 3 + i32.and + br_if 0 (;@6;) + end + end + get_local 3 + i32.const 3 + i32.and + tee_local 2 + i32.eqz + br_if 1 (;@3;) + br 2 (;@2;) + end + get_local 2 + set_local 4 + get_local 0 + tee_local 3 + i32.const 3 + i32.and + tee_local 2 + br_if 1 (;@2;) + end + block ;; label = @3 + block ;; label = @4 + get_local 4 + i32.const 16 + i32.lt_u + br_if 0 (;@4;) + get_local 4 + i32.const -16 + i32.add + set_local 2 + loop ;; label = @5 + get_local 3 + get_local 1 + i32.load + i32.store + get_local 3 + i32.const 4 + i32.add + get_local 1 + i32.const 4 + i32.add + i32.load + i32.store + get_local 3 + i32.const 8 + i32.add + get_local 1 + i32.const 8 + i32.add + i32.load + i32.store + get_local 3 + i32.const 12 + i32.add + get_local 1 + i32.const 12 + i32.add + i32.load + i32.store + get_local 3 + i32.const 16 + i32.add + set_local 3 + get_local 1 + i32.const 16 + i32.add + set_local 1 + get_local 4 + i32.const -16 + i32.add + tee_local 4 + i32.const 15 + i32.gt_u + br_if 0 (;@5;) + br 2 (;@3;) + end + end + get_local 4 + set_local 2 + end + block ;; label = @3 + get_local 2 + i32.const 8 + i32.and + i32.eqz + br_if 0 (;@3;) + get_local 3 + get_local 1 + i64.load align=4 + i64.store align=4 + get_local 1 + i32.const 8 + i32.add + set_local 1 + get_local 3 + i32.const 8 + i32.add + set_local 3 + end + block ;; label = @3 + get_local 2 + i32.const 4 + i32.and + i32.eqz + br_if 0 (;@3;) + get_local 3 + get_local 1 + i32.load + i32.store + get_local 1 + i32.const 4 + i32.add + set_local 1 + get_local 3 + i32.const 4 + i32.add + set_local 3 + end + block ;; label = @3 + get_local 2 + i32.const 2 + i32.and + i32.eqz + br_if 0 (;@3;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + i32.const 2 + i32.add + set_local 3 + get_local 1 + i32.const 2 + i32.add + set_local 1 + end + get_local 2 + i32.const 1 + i32.and + i32.eqz + br_if 1 (;@1;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 0 + return + end + block ;; label = @2 + get_local 4 + i32.const 32 + i32.lt_u + br_if 0 (;@2;) + block ;; label = @3 + block ;; label = @4 + get_local 2 + i32.const 3 + i32.eq + br_if 0 (;@4;) + get_local 2 + i32.const 2 + i32.eq + br_if 1 (;@3;) + get_local 2 + i32.const 1 + i32.ne + br_if 2 (;@2;) + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + get_local 1 + i32.load + tee_local 5 + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=2 + i32.store8 offset=2 + get_local 4 + i32.const -3 + i32.add + set_local 6 + get_local 3 + i32.const 3 + i32.add + set_local 7 + get_local 4 + i32.const -20 + i32.add + i32.const -16 + i32.and + set_local 8 + i32.const 0 + set_local 2 + loop ;; label = @5 + get_local 7 + get_local 2 + i32.add + tee_local 3 + get_local 1 + get_local 2 + i32.add + tee_local 9 + i32.const 4 + i32.add + i32.load + tee_local 10 + i32.const 8 + i32.shl + get_local 5 + i32.const 24 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 4 + i32.add + get_local 9 + i32.const 8 + i32.add + i32.load + tee_local 5 + i32.const 8 + i32.shl + get_local 10 + i32.const 24 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 8 + i32.add + get_local 9 + i32.const 12 + i32.add + i32.load + tee_local 10 + i32.const 8 + i32.shl + get_local 5 + i32.const 24 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 12 + i32.add + get_local 9 + i32.const 16 + i32.add + i32.load + tee_local 5 + i32.const 8 + i32.shl + get_local 10 + i32.const 24 + i32.shr_u + i32.or + i32.store + get_local 2 + i32.const 16 + i32.add + set_local 2 + get_local 6 + i32.const -16 + i32.add + tee_local 6 + i32.const 16 + i32.gt_u + br_if 0 (;@5;) + end + get_local 7 + get_local 2 + i32.add + set_local 3 + get_local 1 + get_local 2 + i32.add + i32.const 3 + i32.add + set_local 1 + get_local 4 + i32.const -19 + i32.add + get_local 8 + i32.sub + set_local 4 + br 2 (;@2;) + end + get_local 3 + get_local 1 + i32.load + tee_local 5 + i32.store8 + get_local 4 + i32.const -1 + i32.add + set_local 6 + get_local 3 + i32.const 1 + i32.add + set_local 7 + get_local 4 + i32.const -20 + i32.add + i32.const -16 + i32.and + set_local 8 + i32.const 0 + set_local 2 + loop ;; label = @4 + get_local 7 + get_local 2 + i32.add + tee_local 3 + get_local 1 + get_local 2 + i32.add + tee_local 9 + i32.const 4 + i32.add + i32.load + tee_local 10 + i32.const 24 + i32.shl + get_local 5 + i32.const 8 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 4 + i32.add + get_local 9 + i32.const 8 + i32.add + i32.load + tee_local 5 + i32.const 24 + i32.shl + get_local 10 + i32.const 8 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 8 + i32.add + get_local 9 + i32.const 12 + i32.add + i32.load + tee_local 10 + i32.const 24 + i32.shl + get_local 5 + i32.const 8 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 12 + i32.add + get_local 9 + i32.const 16 + i32.add + i32.load + tee_local 5 + i32.const 24 + i32.shl + get_local 10 + i32.const 8 + i32.shr_u + i32.or + i32.store + get_local 2 + i32.const 16 + i32.add + set_local 2 + get_local 6 + i32.const -16 + i32.add + tee_local 6 + i32.const 18 + i32.gt_u + br_if 0 (;@4;) + end + get_local 7 + get_local 2 + i32.add + set_local 3 + get_local 1 + get_local 2 + i32.add + i32.const 1 + i32.add + set_local 1 + get_local 4 + i32.const -17 + i32.add + get_local 8 + i32.sub + set_local 4 + br 1 (;@2;) + end + get_local 3 + get_local 1 + i32.load + tee_local 5 + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 4 + i32.const -2 + i32.add + set_local 6 + get_local 3 + i32.const 2 + i32.add + set_local 7 + get_local 4 + i32.const -20 + i32.add + i32.const -16 + i32.and + set_local 8 + i32.const 0 + set_local 2 + loop ;; label = @3 + get_local 7 + get_local 2 + i32.add + tee_local 3 + get_local 1 + get_local 2 + i32.add + tee_local 9 + i32.const 4 + i32.add + i32.load + tee_local 10 + i32.const 16 + i32.shl + get_local 5 + i32.const 16 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 4 + i32.add + get_local 9 + i32.const 8 + i32.add + i32.load + tee_local 5 + i32.const 16 + i32.shl + get_local 10 + i32.const 16 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 8 + i32.add + get_local 9 + i32.const 12 + i32.add + i32.load + tee_local 10 + i32.const 16 + i32.shl + get_local 5 + i32.const 16 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 12 + i32.add + get_local 9 + i32.const 16 + i32.add + i32.load + tee_local 5 + i32.const 16 + i32.shl + get_local 10 + i32.const 16 + i32.shr_u + i32.or + i32.store + get_local 2 + i32.const 16 + i32.add + set_local 2 + get_local 6 + i32.const -16 + i32.add + tee_local 6 + i32.const 17 + i32.gt_u + br_if 0 (;@3;) + end + get_local 7 + get_local 2 + i32.add + set_local 3 + get_local 1 + get_local 2 + i32.add + i32.const 2 + i32.add + set_local 1 + get_local 4 + i32.const -18 + i32.add + get_local 8 + i32.sub + set_local 4 + end + block ;; label = @2 + get_local 4 + i32.const 16 + i32.and + i32.eqz + br_if 0 (;@2;) + get_local 3 + get_local 1 + i32.load16_u align=1 + i32.store16 align=1 + get_local 3 + get_local 1 + i32.load8_u offset=2 + i32.store8 offset=2 + get_local 3 + get_local 1 + i32.load8_u offset=3 + i32.store8 offset=3 + get_local 3 + get_local 1 + i32.load8_u offset=4 + i32.store8 offset=4 + get_local 3 + get_local 1 + i32.load8_u offset=5 + i32.store8 offset=5 + get_local 3 + get_local 1 + i32.load8_u offset=6 + i32.store8 offset=6 + get_local 3 + get_local 1 + i32.load8_u offset=7 + i32.store8 offset=7 + get_local 3 + get_local 1 + i32.load8_u offset=8 + i32.store8 offset=8 + get_local 3 + get_local 1 + i32.load8_u offset=9 + i32.store8 offset=9 + get_local 3 + get_local 1 + i32.load8_u offset=10 + i32.store8 offset=10 + get_local 3 + get_local 1 + i32.load8_u offset=11 + i32.store8 offset=11 + get_local 3 + get_local 1 + i32.load8_u offset=12 + i32.store8 offset=12 + get_local 3 + get_local 1 + i32.load8_u offset=13 + i32.store8 offset=13 + get_local 3 + get_local 1 + i32.load8_u offset=14 + i32.store8 offset=14 + get_local 3 + get_local 1 + i32.load8_u offset=15 + i32.store8 offset=15 + get_local 3 + i32.const 16 + i32.add + set_local 3 + get_local 1 + i32.const 16 + i32.add + set_local 1 + end + block ;; label = @2 + get_local 4 + i32.const 8 + i32.and + i32.eqz + br_if 0 (;@2;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + get_local 1 + i32.load8_u offset=2 + i32.store8 offset=2 + get_local 3 + get_local 1 + i32.load8_u offset=3 + i32.store8 offset=3 + get_local 3 + get_local 1 + i32.load8_u offset=4 + i32.store8 offset=4 + get_local 3 + get_local 1 + i32.load8_u offset=5 + i32.store8 offset=5 + get_local 3 + get_local 1 + i32.load8_u offset=6 + i32.store8 offset=6 + get_local 3 + get_local 1 + i32.load8_u offset=7 + i32.store8 offset=7 + get_local 3 + i32.const 8 + i32.add + set_local 3 + get_local 1 + i32.const 8 + i32.add + set_local 1 + end + block ;; label = @2 + get_local 4 + i32.const 4 + i32.and + i32.eqz + br_if 0 (;@2;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + get_local 1 + i32.load8_u offset=2 + i32.store8 offset=2 + get_local 3 + get_local 1 + i32.load8_u offset=3 + i32.store8 offset=3 + get_local 3 + i32.const 4 + i32.add + set_local 3 + get_local 1 + i32.const 4 + i32.add + set_local 1 + end + block ;; label = @2 + get_local 4 + i32.const 2 + i32.and + i32.eqz + br_if 0 (;@2;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + i32.const 2 + i32.add + set_local 3 + get_local 1 + i32.const 2 + i32.add + set_local 1 + end + get_local 4 + i32.const 1 + i32.and + i32.eqz + br_if 0 (;@1;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + end + get_local 0) + (func (;44;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i64) + block ;; label = @1 + get_local 2 + i32.eqz + br_if 0 (;@1;) + get_local 0 + get_local 1 + i32.store8 + get_local 0 + get_local 2 + i32.add + tee_local 3 + i32.const -1 + i32.add + get_local 1 + i32.store8 + get_local 2 + i32.const 3 + i32.lt_u + br_if 0 (;@1;) + get_local 0 + get_local 1 + i32.store8 offset=2 + get_local 0 + get_local 1 + i32.store8 offset=1 + get_local 3 + i32.const -3 + i32.add + get_local 1 + i32.store8 + get_local 3 + i32.const -2 + i32.add + get_local 1 + i32.store8 + get_local 2 + i32.const 7 + i32.lt_u + br_if 0 (;@1;) + get_local 0 + get_local 1 + i32.store8 offset=3 + get_local 3 + i32.const -4 + i32.add + get_local 1 + i32.store8 + get_local 2 + i32.const 9 + i32.lt_u + br_if 0 (;@1;) + get_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.const 3 + i32.and + tee_local 4 + i32.add + tee_local 3 + get_local 1 + i32.const 255 + i32.and + i32.const 16843009 + i32.mul + tee_local 1 + i32.store + get_local 3 + get_local 2 + get_local 4 + i32.sub + i32.const -4 + i32.and + tee_local 4 + i32.add + tee_local 2 + i32.const -4 + i32.add + get_local 1 + i32.store + get_local 4 + i32.const 9 + i32.lt_u + br_if 0 (;@1;) + get_local 3 + get_local 1 + i32.store offset=8 + get_local 3 + get_local 1 + i32.store offset=4 + get_local 2 + i32.const -8 + i32.add + get_local 1 + i32.store + get_local 2 + i32.const -12 + i32.add + get_local 1 + i32.store + get_local 4 + i32.const 25 + i32.lt_u + br_if 0 (;@1;) + get_local 3 + get_local 1 + i32.store offset=24 + get_local 3 + get_local 1 + i32.store offset=20 + get_local 3 + get_local 1 + i32.store offset=16 + get_local 3 + get_local 1 + i32.store offset=12 + get_local 2 + i32.const -16 + i32.add + get_local 1 + i32.store + get_local 2 + i32.const -20 + i32.add + get_local 1 + i32.store + get_local 2 + i32.const -24 + i32.add + get_local 1 + i32.store + get_local 2 + i32.const -28 + i32.add + get_local 1 + i32.store + get_local 4 + get_local 3 + i32.const 4 + i32.and + i32.const 24 + i32.or + tee_local 5 + i32.sub + tee_local 2 + i32.const 32 + i32.lt_u + br_if 0 (;@1;) + get_local 1 + i64.extend_u/i32 + tee_local 6 + i64.const 32 + i64.shl + get_local 6 + i64.or + set_local 6 + get_local 3 + get_local 5 + i32.add + set_local 1 + loop ;; label = @2 + get_local 1 + get_local 6 + i64.store + get_local 1 + i32.const 24 + i32.add + get_local 6 + i64.store + get_local 1 + i32.const 16 + i32.add + get_local 6 + i64.store + get_local 1 + i32.const 8 + i32.add + get_local 6 + i64.store + get_local 1 + i32.const 32 + i32.add + set_local 1 + get_local 2 + i32.const -32 + i32.add + tee_local 2 + i32.const 31 + i32.gt_u + br_if 0 (;@2;) + end + end + get_local 0) + (func (;45;) (type 4) (param i32) (result i32) + (local i32 i32) + block ;; label = @1 + get_local 0 + call 46 + i32.const 1 + i32.add + tee_local 1 + call 14 + tee_local 2 + i32.eqz + br_if 0 (;@1;) + get_local 2 + get_local 0 + get_local 1 + call 43 + return + end + i32.const 0) + (func (;46;) (type 4) (param i32) (result i32) + (local i32 i32 i32) + get_local 0 + set_local 1 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 0 + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@3;) + get_local 0 + i32.load8_u + i32.eqz + br_if 1 (;@2;) + get_local 0 + i32.const 1 + i32.add + set_local 1 + loop ;; label = @4 + get_local 1 + i32.const 3 + i32.and + i32.eqz + br_if 1 (;@3;) + get_local 1 + i32.load8_u + set_local 2 + get_local 1 + i32.const 1 + i32.add + tee_local 3 + set_local 1 + get_local 2 + br_if 0 (;@4;) + end + get_local 3 + i32.const -1 + i32.add + get_local 0 + i32.sub + return + end + get_local 1 + i32.const -4 + i32.add + set_local 1 + loop ;; label = @3 + get_local 1 + i32.const 4 + i32.add + tee_local 1 + i32.load + tee_local 2 + i32.const -1 + i32.xor + get_local 2 + i32.const -16843009 + i32.add + i32.and + i32.const -2139062144 + i32.and + i32.eqz + br_if 0 (;@3;) + end + get_local 2 + i32.const 255 + i32.and + i32.eqz + br_if 1 (;@1;) + loop ;; label = @3 + get_local 1 + i32.load8_u offset=1 + set_local 2 + get_local 1 + i32.const 1 + i32.add + tee_local 3 + set_local 1 + get_local 2 + br_if 0 (;@3;) + end + get_local 3 + get_local 0 + i32.sub + return + end + get_local 0 + get_local 0 + i32.sub + return + end + get_local 1 + get_local 0 + i32.sub) + (table (;0;) 5 5 anyfunc) + (memory (;0;) 2) + (global (;0;) (mut i32) (i32.const 68256)) + (global (;1;) i32 (i32.const 68256)) + (global (;2;) i32 (i32.const 2716)) + (export "memory" (memory 0)) + (export "__heap_base" (global 1)) + (export "__data_end" (global 2)) + (export "_start" (func 12)) + (elem (i32.const 1) 38 36 40 42) + (data (i32.const 1024) "simple-wasi...\00") + (data (i32.const 1040) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 2600) "\05\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\03\00\00\00\04\00\00\00(\06\00\00\00\04\00\00\00\00\00\00\00\00\00\00\01\00\00\00\00\00\00\00\0a\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00(\0a\00\00")) diff --git a/test/fixtures/wasi/subdir/input_link.txt b/test/fixtures/wasi/subdir/input_link.txt new file mode 120000 index 00000000000000..4a6d09bf82379f --- /dev/null +++ b/test/fixtures/wasi/subdir/input_link.txt @@ -0,0 +1 @@ +../input.txt \ No newline at end of file diff --git a/test/fixtures/wasi/subdir/loop1 b/test/fixtures/wasi/subdir/loop1 new file mode 120000 index 00000000000000..433d7fdbce1536 --- /dev/null +++ b/test/fixtures/wasi/subdir/loop1 @@ -0,0 +1 @@ +./loop2 \ No newline at end of file diff --git a/test/fixtures/wasi/subdir/loop2 b/test/fixtures/wasi/subdir/loop2 new file mode 120000 index 00000000000000..2907735afc0680 --- /dev/null +++ b/test/fixtures/wasi/subdir/loop2 @@ -0,0 +1 @@ +./loop1 \ No newline at end of file diff --git a/test/fixtures/wasi/subdir/outside.txt b/test/fixtures/wasi/subdir/outside.txt new file mode 120000 index 00000000000000..2e0694706746f7 --- /dev/null +++ b/test/fixtures/wasi/subdir/outside.txt @@ -0,0 +1 @@ +../../outside.txt \ No newline at end of file diff --git a/test/wasi/Makefile b/test/wasi/Makefile new file mode 100644 index 00000000000000..42d3b4e3fa7a80 --- /dev/null +++ b/test/wasi/Makefile @@ -0,0 +1,12 @@ +CC = /opt/wasi-sdk/bin/clang +TARGET = wasm32-unknown-wasi +SYSROOT = + +OBJ = $(patsubst c/%.c, wasm/%.wasm, $(wildcard c/*.c)) +all: $(OBJ) + +wasm/%.wasm : c/%.c + $(CC) $< --target=$(TARGET) --sysroot=$(SYSROOT) -s -o $@ + +.PHONY clean: + rm -f $(OBJ) diff --git a/test/wasi/README.md b/test/wasi/README.md new file mode 100644 index 00000000000000..ccb02d095ef939 --- /dev/null +++ b/test/wasi/README.md @@ -0,0 +1,8 @@ +# WASI Tests + +Compile with clang and `wasm32-wasi` target. The clang version used must be +built with wasi-libc. You can specify the location for clang and the sysroot +if needed when running make: +```console +$ make CC=/usr/local/opt/llvm/bin/clang SYSROOT=/path/to/wasi-libc/sysroot +``` diff --git a/test/wasi/c/cant_dotdot.c b/test/wasi/c/cant_dotdot.c new file mode 100644 index 00000000000000..e2722062cd0156 --- /dev/null +++ b/test/wasi/c/cant_dotdot.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int main() { + FILE* file = fopen("/sandbox/../outside.txt", "r"); + assert(file == NULL); + assert(errno == ENOTCAPABLE); + + return 0; +} diff --git a/test/wasi/c/clock_getres.c b/test/wasi/c/clock_getres.c new file mode 100644 index 00000000000000..eaac02c665b9f2 --- /dev/null +++ b/test/wasi/c/clock_getres.c @@ -0,0 +1,17 @@ +#include +#include + +int main() { + struct timespec ts; + int r; + + // supported clocks + r = clock_getres(CLOCK_REALTIME, &ts); + assert(r == 0); + r = clock_getres(CLOCK_MONOTONIC, &ts); + assert(r == 0); + r = clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts); + assert(r == 0); + r = clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts); + assert(r == 0); +} diff --git a/test/wasi/c/exitcode.c b/test/wasi/c/exitcode.c new file mode 100644 index 00000000000000..9c44b0de741a69 --- /dev/null +++ b/test/wasi/c/exitcode.c @@ -0,0 +1,3 @@ +int main() { + return 120; +} diff --git a/test/wasi/c/fd_prestat_get_refresh.c b/test/wasi/c/fd_prestat_get_refresh.c new file mode 100644 index 00000000000000..fb644ab49b208a --- /dev/null +++ b/test/wasi/c/fd_prestat_get_refresh.c @@ -0,0 +1,8 @@ +#include + +int main(void) { + isatty(1); + __builtin_wasm_memory_grow(0, 1); + isatty(1); + return 0; +} diff --git a/test/wasi/c/follow_symlink.c b/test/wasi/c/follow_symlink.c new file mode 100644 index 00000000000000..badb0ee2676bbe --- /dev/null +++ b/test/wasi/c/follow_symlink.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/input_link.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } +} diff --git a/test/wasi/c/getentropy.c b/test/wasi/c/getentropy.c new file mode 100644 index 00000000000000..75547e1c474bba --- /dev/null +++ b/test/wasi/c/getentropy.c @@ -0,0 +1,18 @@ +#include +#include + +int main() { + char buf[256] = {0}; + int r = getentropy(buf, 256); + assert(r == 0); + + for (int i = 0; i < 256; i++) { + if (buf[i] != 0) { + return 0; + } + } + + // if this ever is reached, we either have a bug or should buy a lottery + // ticket + return 1; +} diff --git a/test/wasi/c/getrusage.c b/test/wasi/c/getrusage.c new file mode 100644 index 00000000000000..ad1e430b853395 --- /dev/null +++ b/test/wasi/c/getrusage.c @@ -0,0 +1,34 @@ +#include +#include + +int main() { + struct rusage ru1; + struct rusage ru2; + long long s1; + long long us1; + long long s2; + long long us2; + int r; + int success = 0; + + r = getrusage(RUSAGE_SELF, &ru1); + assert(r == 0); + s1 = ru1.ru_utime.tv_sec; + us1 = ru1.ru_utime.tv_usec; + + for (int i = 0; i < 10000; i++) { + r = getrusage(RUSAGE_SELF, &ru2); + assert(r == 0); + s2 = ru2.ru_utime.tv_sec; + us2 = ru2.ru_utime.tv_usec; + assert(s1 <= s2); + + // Verify that some time has passed. + if (s2 > s1 || (s2 == s1 && us2 > us1)) { + success = 1; + break; + } + } + + assert(success == 1); +} diff --git a/test/wasi/c/gettimeofday.c b/test/wasi/c/gettimeofday.c new file mode 100644 index 00000000000000..209a54e4983bfa --- /dev/null +++ b/test/wasi/c/gettimeofday.c @@ -0,0 +1,35 @@ +#include +#include +#include + +int main() { + struct timeval tv1; + struct timeval tv2; + long long s1; + long long us1; + long long s2; + long long us2; + int r; + int success = 0; + + r = gettimeofday(&tv1, NULL); + assert(r == 0); + s1 = tv1.tv_sec; + us1 = tv1.tv_usec; + + for (int i = 0; i < 10000; i++) { + r = gettimeofday(&tv2, NULL); + assert(r == 0); + s2 = tv2.tv_sec; + us2 = tv2.tv_usec; + assert(s1 <= s2); + + // Verify that some time has passed. + if (s2 > s1 || (s2 == s1 && us2 > us1)) { + success = 1; + break; + } + } + + assert(success == 1); +} diff --git a/test/wasi/c/notdir.c b/test/wasi/c/notdir.c new file mode 100644 index 00000000000000..03f369ffd3a862 --- /dev/null +++ b/test/wasi/c/notdir.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int main() { + DIR* dir = opendir("/sandbox/notadir"); + assert(dir == NULL); + assert(errno == ENOTDIR); + + return 0; +} diff --git a/test/wasi/c/poll.c b/test/wasi/c/poll.c new file mode 100644 index 00000000000000..6b6ef71fd68c3b --- /dev/null +++ b/test/wasi/c/poll.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +int main(void) { + struct pollfd fds[2]; + time_t before, now; + int ret; + + fds[0] = (struct pollfd){.fd = 1, .events = POLLOUT, .revents = 0}; + fds[1] = (struct pollfd){.fd = 2, .events = POLLOUT, .revents = 0}; + + ret = poll(fds, 2, -1); + assert(ret == 2); + assert(fds[0].revents == POLLOUT); + assert(fds[1].revents == POLLOUT); + + fds[0] = (struct pollfd){.fd = 0, .events = POLLIN, .revents = 0}; + time(&before); + ret = poll(fds, 1, 2000); + time(&now); + assert(ret == 0); + assert(now - before >= 2); + + sleep(1); + time(&now); + assert(now - before >= 3); + + return 0; +} diff --git a/test/wasi/c/preopen_populates.c b/test/wasi/c/preopen_populates.c new file mode 100644 index 00000000000000..03b2213bb9a36c --- /dev/null +++ b/test/wasi/c/preopen_populates.c @@ -0,0 +1,3 @@ +int main(void) { + return 0; +} diff --git a/test/wasi/c/read_file.c b/test/wasi/c/read_file.c new file mode 100644 index 00000000000000..40023e29e2727f --- /dev/null +++ b/test/wasi/c/read_file.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + FILE* file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } +} diff --git a/test/wasi/c/read_file_twice.c b/test/wasi/c/read_file_twice.c new file mode 100644 index 00000000000000..e295e38a3b3e13 --- /dev/null +++ b/test/wasi/c/read_file_twice.c @@ -0,0 +1,16 @@ +#include +#include + +int main() { + for (int i = 0; i < 2; i++) { + FILE* file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } + } +} diff --git a/test/wasi/c/stat.c b/test/wasi/c/stat.c new file mode 100644 index 00000000000000..fd3854937b9e6e --- /dev/null +++ b/test/wasi/c/stat.c @@ -0,0 +1,53 @@ +#include + +#include +#include +#include +#include + +#define BASE_DIR "/tmp" +#define OUTPUT_DIR BASE_DIR "/testdir" +#define PATH OUTPUT_DIR "/output.txt" +#define SIZE 500 + +int main(void) { + struct stat st; + int fd; + int ret; + off_t pos; + + (void)st; + ret = mkdir(OUTPUT_DIR, 0755); + assert(ret == 0); + + fd = open(PATH, O_CREAT | O_WRONLY, 0666); + assert(fd != -1); + + pos = lseek(fd, SIZE - 1, SEEK_SET); + assert(pos == SIZE - 1); + + ret = (int)write(fd, "", 1); + assert(ret == 1); + + ret = fstat(fd, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = close(fd); + assert(ret == 0); + + ret = access(PATH, R_OK); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = unlink(PATH); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == -1); + + return 0; +} diff --git a/test/wasi/c/stdin.c b/test/wasi/c/stdin.c new file mode 100644 index 00000000000000..5a81ea1265fb11 --- /dev/null +++ b/test/wasi/c/stdin.c @@ -0,0 +1,13 @@ +#include + +int main(void) { + char x[32]; + + if (fgets(x, sizeof x, stdin) == NULL) { + return ferror(stdin); + } + if (fputs(x, stdout) == EOF) { + return ferror(stdout); + } + return 0; +} diff --git a/test/wasi/c/symlink_escape.c b/test/wasi/c/symlink_escape.c new file mode 100644 index 00000000000000..32dcc64eebdc2b --- /dev/null +++ b/test/wasi/c/symlink_escape.c @@ -0,0 +1,9 @@ +#include +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/outside.txt", "r"); + assert(file == NULL); + assert(errno == ENOTCAPABLE); +} diff --git a/test/wasi/c/symlink_loop.c b/test/wasi/c/symlink_loop.c new file mode 100644 index 00000000000000..23bd70ba601176 --- /dev/null +++ b/test/wasi/c/symlink_loop.c @@ -0,0 +1,9 @@ +#include +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/loop1", "r"); + assert(file == NULL); + assert(errno == ELOOP); +} diff --git a/test/wasi/c/write_file.c b/test/wasi/c/write_file.c new file mode 100644 index 00000000000000..c4cf30cf2954cb --- /dev/null +++ b/test/wasi/c/write_file.c @@ -0,0 +1,15 @@ +#include +#include +#include + +static char* message = "hello, file!"; + +int main() { + FILE* file = fopen("/tmp/output.txt", "w"); + assert(file != NULL); + + int nwritten = fprintf(file, "%s", message); + assert(nwritten == strlen(message)); + int r = fclose(file); + assert(r == 0); +} diff --git a/test/wasi/test-wasi-binding.js b/test/wasi/test-wasi-binding.js new file mode 100644 index 00000000000000..876c8a15a72c13 --- /dev/null +++ b/test/wasi/test-wasi-binding.js @@ -0,0 +1,19 @@ +// Flags: --experimental-wasi-unstable-preview0 +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const buffer = fixtures.readSync(['wasi', 'simple-wasi.wasm']); +const { WASI } = require('wasi'); +const wasi = new WASI({ args: [], env: process.env }); +const importObject = { + wasi_unstable: wasi.wasiImport +}; + +WebAssembly.instantiate(buffer, importObject) +.then(common.mustCall((results) => { + assert(results.instance.exports._start); + wasi.start(results.instance); +})); diff --git a/test/wasi/test-wasi-symlinks.js b/test/wasi/test-wasi-symlinks.js new file mode 100644 index 00000000000000..3829464198273b --- /dev/null +++ b/test/wasi/test-wasi-symlinks.js @@ -0,0 +1,78 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const path = require('path'); + +if (process.argv[2] === 'wasi-child') { + common.expectWarning('ExperimentalWarning', + 'WASI is an experimental feature. This feature could ' + + 'change at any time'); + + const { WASI } = require('wasi'); + const wasmDir = path.join(__dirname, 'wasm'); + const wasi = new WASI({ + args: [], + env: process.env, + preopens: { + '/sandbox': process.argv[4] + } + }); + const importObject = { wasi_unstable: wasi.wasiImport }; + const modulePath = path.join(wasmDir, `${process.argv[3]}.wasm`); + const buffer = fs.readFileSync(modulePath); + + (async () => { + const { instance } = await WebAssembly.instantiate(buffer, importObject); + + wasi.start(instance); + })(); +} else { + if (!common.canCreateSymLink()) { + common.skip('insufficient privileges'); + } + + const assert = require('assert'); + const cp = require('child_process'); + const tmpdir = require('../../test/common/tmpdir'); + + // Setup the sandbox environment. + tmpdir.refresh(); + const sandbox = path.join(tmpdir.path, 'sandbox'); + const sandboxedFile = path.join(sandbox, 'input.txt'); + const externalFile = path.join(tmpdir.path, 'outside.txt'); + const sandboxedDir = path.join(sandbox, 'subdir'); + const sandboxedSymlink = path.join(sandboxedDir, 'input_link.txt'); + const escapingSymlink = path.join(sandboxedDir, 'outside.txt'); + const loopSymlink1 = path.join(sandboxedDir, 'loop1'); + const loopSymlink2 = path.join(sandboxedDir, 'loop2'); + + fs.mkdirSync(sandbox); + fs.mkdirSync(sandboxedDir); + fs.writeFileSync(sandboxedFile, 'hello from input.txt', 'utf8'); + fs.writeFileSync(externalFile, 'this should be inaccessible', 'utf8'); + fs.symlinkSync(sandboxedFile, sandboxedSymlink, 'file'); + fs.symlinkSync(externalFile, escapingSymlink, 'file'); + fs.symlinkSync(loopSymlink2, loopSymlink1, 'file'); + fs.symlinkSync(loopSymlink1, loopSymlink2, 'file'); + + function runWASI(options) { + console.log('executing', options.test); + const opts = { env: { ...process.env, NODE_DEBUG_NATIVE: 'wasi' } }; + const child = cp.spawnSync(process.execPath, [ + '--experimental-wasi-unstable-preview0', + '--experimental-wasm-bigint', + __filename, + 'wasi-child', + options.test, + sandbox + ], opts); + console.log(child.stderr.toString()); + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), options.stdout || ''); + } + + runWASI({ test: 'follow_symlink', stdout: 'hello from input.txt' }); + runWASI({ test: 'symlink_escape' }); + runWASI({ test: 'symlink_loop' }); +} diff --git a/test/wasi/test-wasi.js b/test/wasi/test-wasi.js new file mode 100644 index 00000000000000..fa2e0894c906ce --- /dev/null +++ b/test/wasi/test-wasi.js @@ -0,0 +1,81 @@ +'use strict'; +const common = require('../common'); + +if (process.argv[2] === 'wasi-child') { + const fixtures = require('../common/fixtures'); + const tmpdir = require('../../test/common/tmpdir'); + const fs = require('fs'); + const path = require('path'); + + common.expectWarning('ExperimentalWarning', + 'WASI is an experimental feature. This feature could ' + + 'change at any time'); + + const { WASI } = require('wasi'); + tmpdir.refresh(); + const wasmDir = path.join(__dirname, 'wasm'); + const wasi = new WASI({ + args: [], + env: process.env, + preopens: { + '/sandbox': fixtures.path('wasi'), + '/tmp': tmpdir.path + } + }); + const importObject = { wasi_unstable: wasi.wasiImport }; + const modulePath = path.join(wasmDir, `${process.argv[3]}.wasm`); + const buffer = fs.readFileSync(modulePath); + + (async () => { + const { instance } = await WebAssembly.instantiate(buffer, importObject); + + wasi.start(instance); + })(); +} else { + const assert = require('assert'); + const cp = require('child_process'); + const { EOL } = require('os'); + + function runWASI(options) { + console.log('executing', options.test); + const opts = { env: { ...process.env, NODE_DEBUG_NATIVE: 'wasi' } }; + + if (options.stdin !== undefined) + opts.input = options.stdin; + + const child = cp.spawnSync(process.execPath, [ + '--experimental-wasi-unstable-preview0', + '--experimental-wasm-bigint', + __filename, + 'wasi-child', + options.test + ], opts); + console.log(child.stderr.toString()); + assert.strictEqual(child.status, options.exitCode || 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), options.stdout || ''); + } + + runWASI({ test: 'cant_dotdot' }); + runWASI({ test: 'clock_getres' }); + runWASI({ test: 'exitcode', exitCode: 120 }); + runWASI({ test: 'fd_prestat_get_refresh' }); + runWASI({ test: 'getentropy' }); + runWASI({ test: 'getrusage' }); + runWASI({ test: 'gettimeofday' }); + runWASI({ test: 'notdir' }); + // runWASI({ test: 'poll' }); + runWASI({ test: 'preopen_populates' }); + runWASI({ test: 'read_file', stdout: `hello from input.txt${EOL}` }); + runWASI({ + test: 'read_file_twice', + stdout: `hello from input.txt${EOL}hello from input.txt${EOL}` + }); + runWASI({ test: 'stat' }); + runWASI({ test: 'write_file' }); + + // Tests that are currently unsupported on Windows. + if (!common.isWindows) { + runWASI({ test: 'stdin', stdin: 'hello world', stdout: 'hello world' }); + } +} diff --git a/test/wasi/testcfg.py b/test/wasi/testcfg.py new file mode 100644 index 00000000000000..ec6cbc5fe3dc3a --- /dev/null +++ b/test/wasi/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.ParallelTestConfiguration(context, root, 'wasi') diff --git a/test/wasi/wasi.status b/test/wasi/wasi.status new file mode 100644 index 00000000000000..12212e8f72822b --- /dev/null +++ b/test/wasi/wasi.status @@ -0,0 +1,7 @@ +prefix wasi + +# To mark a test as flaky, list the test name in the appropriate section +# below, without ".js", followed by ": PASS,FLAKY". Example: +# sample-test : PASS,FLAKY + +[true] # This section applies to all platforms diff --git a/test/wasi/wasm/cant_dotdot.wasm b/test/wasi/wasm/cant_dotdot.wasm new file mode 100755 index 00000000000000..61e202d6913a3e Binary files /dev/null and b/test/wasi/wasm/cant_dotdot.wasm differ diff --git a/test/wasi/wasm/clock_getres.wasm b/test/wasi/wasm/clock_getres.wasm new file mode 100755 index 00000000000000..fac14edb04d794 Binary files /dev/null and b/test/wasi/wasm/clock_getres.wasm differ diff --git a/test/wasi/wasm/exitcode.wasm b/test/wasi/wasm/exitcode.wasm new file mode 100755 index 00000000000000..b2d9ef5e114218 Binary files /dev/null and b/test/wasi/wasm/exitcode.wasm differ diff --git a/test/wasi/wasm/fd_prestat_get_refresh.wasm b/test/wasi/wasm/fd_prestat_get_refresh.wasm new file mode 100755 index 00000000000000..cf4bb97c3b2b20 Binary files /dev/null and b/test/wasi/wasm/fd_prestat_get_refresh.wasm differ diff --git a/test/wasi/wasm/follow_symlink.wasm b/test/wasi/wasm/follow_symlink.wasm new file mode 100755 index 00000000000000..48cf8da1eb2860 Binary files /dev/null and b/test/wasi/wasm/follow_symlink.wasm differ diff --git a/test/wasi/wasm/getentropy.wasm b/test/wasi/wasm/getentropy.wasm new file mode 100755 index 00000000000000..6e3e8c8a8edbab Binary files /dev/null and b/test/wasi/wasm/getentropy.wasm differ diff --git a/test/wasi/wasm/getrusage.wasm b/test/wasi/wasm/getrusage.wasm new file mode 100755 index 00000000000000..524e809175b5aa Binary files /dev/null and b/test/wasi/wasm/getrusage.wasm differ diff --git a/test/wasi/wasm/gettimeofday.wasm b/test/wasi/wasm/gettimeofday.wasm new file mode 100755 index 00000000000000..94627f00866903 Binary files /dev/null and b/test/wasi/wasm/gettimeofday.wasm differ diff --git a/test/wasi/wasm/notdir.wasm b/test/wasi/wasm/notdir.wasm new file mode 100755 index 00000000000000..f83a790ddc4700 Binary files /dev/null and b/test/wasi/wasm/notdir.wasm differ diff --git a/test/wasi/wasm/poll.wasm b/test/wasi/wasm/poll.wasm new file mode 100755 index 00000000000000..98d0736762d7fb Binary files /dev/null and b/test/wasi/wasm/poll.wasm differ diff --git a/test/wasi/wasm/preopen_populates.wasm b/test/wasi/wasm/preopen_populates.wasm new file mode 100755 index 00000000000000..e7c34bc964602f Binary files /dev/null and b/test/wasi/wasm/preopen_populates.wasm differ diff --git a/test/wasi/wasm/read_file.wasm b/test/wasi/wasm/read_file.wasm new file mode 100755 index 00000000000000..2b9db77d272618 Binary files /dev/null and b/test/wasi/wasm/read_file.wasm differ diff --git a/test/wasi/wasm/read_file_twice.wasm b/test/wasi/wasm/read_file_twice.wasm new file mode 100755 index 00000000000000..cd075a4de75a51 Binary files /dev/null and b/test/wasi/wasm/read_file_twice.wasm differ diff --git a/test/wasi/wasm/stat.wasm b/test/wasi/wasm/stat.wasm new file mode 100755 index 00000000000000..9007334d37d1a1 Binary files /dev/null and b/test/wasi/wasm/stat.wasm differ diff --git a/test/wasi/wasm/stdin.wasm b/test/wasi/wasm/stdin.wasm new file mode 100755 index 00000000000000..7264608753229f Binary files /dev/null and b/test/wasi/wasm/stdin.wasm differ diff --git a/test/wasi/wasm/symlink_escape.wasm b/test/wasi/wasm/symlink_escape.wasm new file mode 100755 index 00000000000000..0cdb8327a1a23b Binary files /dev/null and b/test/wasi/wasm/symlink_escape.wasm differ diff --git a/test/wasi/wasm/symlink_loop.wasm b/test/wasi/wasm/symlink_loop.wasm new file mode 100755 index 00000000000000..3883d5278c7008 Binary files /dev/null and b/test/wasi/wasm/symlink_loop.wasm differ diff --git a/test/wasi/wasm/write_file.wasm b/test/wasi/wasm/write_file.wasm new file mode 100755 index 00000000000000..500405e0fb3120 Binary files /dev/null and b/test/wasi/wasm/write_file.wasm differ diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 0b9458351e6618..ef4499e50ff35a 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -18,7 +18,7 @@ const jsGlobalTypes = [ 'Array', 'ArrayBuffer', 'ArrayBufferView', 'DataView', 'Date', 'Error', 'EvalError', 'Function', 'Map', 'Object', 'Promise', 'RangeError', 'ReferenceError', 'RegExp', 'Set', 'SharedArrayBuffer', 'SyntaxError', - 'TypeError', 'TypedArray', 'URIError', 'Uint8Array', + 'TypeError', 'TypedArray', 'URIError', 'Uint8Array', 'WebAssembly.Instance', ]; const customTypesMap = { diff --git a/tools/license-builder.sh b/tools/license-builder.sh index c46b18845f18d1..14eb4514a6c3ec 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -109,4 +109,6 @@ addlicense "node-heapdump" "src/heap_utils.cc" \ addlicense "rimraf" "lib/internal/fs/rimraf.js" \ "$(curl -sL https://raw.githubusercontent.com/isaacs/rimraf/0e365ac4e4d64a25aa2a3cc026348f13410210e1/LICENSE)" +addlicense "uvwasi" "deps/uvwasi" "$(cat ${rootdir}/deps/uvwasi/LICENSE)" + mv $tmplicense $licensefile