import { type Fd, wasi } from "@bjorn3/browser_wasi_shim"; import { WASIFarmPark } from "../park.ts"; import { AllocatorUseArrayBuffer } from "./allocator.ts"; import { FdCloseSenderUseArrayBuffer } from "./fd_close_sender.ts"; import type { WASIFarmRefUseArrayBufferObject } from "./ref.ts"; import { wrap_async } from "./util.ts"; import { decodeStrictJsonValue, encodeStrictJsonValue, } from "../codec/index.ts"; import { BaseCallOpcode, BaseCallParkUseArrayBuffer } from "./base_call.ts"; export const fd_func_sig_u32_size: number = 18; export const fd_func_sig_bytes: number = fd_func_sig_u32_size * 4; /** * WASIFarmParkUseArrayBuffer implements the WASIFarmPark using SharedArrayBuffer * for high-performance system call communication between guest Wasm and host. */ export class WASIFarmParkUseArrayBuffer extends WASIFarmPark { private allocator: AllocatorUseArrayBuffer; // args and env do not change, so copying them is fine. // Functions that do not depend on fds are skipped. // Since it is wasm32, the pointer is u32. // errno is u8. // https://github.com/WebAssembly/WASI/blob/4feaf733e946c375b610cc5d39ea2e1a68046e62/legacy/preview1/docs.md // The first item is the function signature, and the second (if it exists) represents data that must be communicated in Park. If they are the same, it is not written. // From here, direct access to fd begins. // fd_advise: (fd: u32, offset: u64, len: u64, advice: u8) => errno; // (fd: u32) => errno; // fd_allocate: (fd: u32, offset: u64, len: u64) => errno; // fd_close: (fd: u32) => errno; // fd_datasync: (fd: u32) => errno; // fd_fdstat_get: (fd: u32, fdstat_ptr: pointer) => errno; // (fd: u32) => [wasi.Fdstat(u32 * 6)], errno]; // fd_fdstat_set_flags: (fd: u32, flags: u16) => errno; // fd_fdstat_set_rights: (fd: u32, fs_rights_base: u64, fs_rights_inheriting: u64) => errno; // fd_filestat_get: (fd: u32, filestat_ptr: pointer) => errno; // (fd: u32) => [wasi.Filestat(u32 * 16)], errno]; // fd_filestat_set_size: (fd: u32, size: u64) => errno; // fd_filestat_set_times: (fd: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; // fd_pread: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, errno]; // use share_arrays_memory; // (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, data_ptr, errno]; // fd_prestat_get: (fd: u32, prestat_ptr: pointer) => errno; // (fd: u32) => [wasi.Prestat(u32 * 2)], errno]; // fd_prestat_dir_name: (fd: u32, path_ptr: pointer, path_len: u32) => errno; // (fd: u32, path_len: u32) => [path_ptr: pointer, path_len: u32, errno]; // fd_pwrite: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, errno]; // use share_arrays_memory; // (fd: u32, write_data: pointer, write_data_len: u32, offset: u64) => [u32, errno]; // fd_read: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, errno]; // use share_arrays_memory; // (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, data_ptr, errno]; // fd_readdir: (fd: u32, buf_ptr: pointer, buf_len: u32, cookie: u64) => [u32, errno]; // use share_arrays_memory; // (fd: u32, buf_len: u32, cookie: u64) => [buf_ptr: pointer, buf_len: u32, buf_used: u32, errno]; // fd_renumber: (fd: u32, to: u32) => errno; // fd_seek: (fd: u32, offset: i64, whence: u8) => [u64, errno]; // fd_sync: (fd: u32) => errno; // fd_tell: (fd: u32) => [u64, errno]; // fd_write: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, errno]; // use share_arrays_memory; // (fd: u32, write_data: pointer, write_data_len: u32) => [u32, errno]; // path_create_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; // path_filestat_get: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32) => [wasi.Filestat(u32 * 16), errno]; // path_filestat_set_times: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; // path_link: (old_fd: u32, old_flags: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; // path_open: (fd: u32, dirflags: u32, path_ptr: pointer, path_len: u32, oflags: u32, fs_rights_base: u64, fs_rights_inheriting: u64, fdflags: u16) => [u32, errno]; // note: fdsにpushするが、既存のfdに影響しないので、競合しない。 // path_readlink: (fd: u32, path_ptr: pointer, path_len: u32, buf_ptr: pointer, buf_len: u32) => [u32, errno]; // use share_arrays_memory; // (fd: u32, path_ptr: pointer, path_len: u32, buf_len: u32) => [buf_len: u32, data_ptr: pointer, data_len: u32, errno]; // path_remove_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; // path_rename: (old_fd: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; // path_symlink: (old_path_ptr: pointer, old_path_len: u32, fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; // path_unlink_file: (fd: u32, path_ptr: pointer, path_len: u32) => errno; // Lock when you want to use fd // Array<[lock, call_func]> private lock_fds: SharedArrayBuffer; // 1 bytes: fds.length // 1 bytes: wasi_farm_ref num(id) // Actually, as long as it is working properly, fds.length is not used private fds_len_and_num: SharedArrayBuffer; // listen promise keep private listen_fds: Array | undefined | null> = []; // listen_fds terminator private listen_fds_terminator: Array<(() => void) | undefined | null> = []; // The largest size is u32 * 18 + 1 // Alignment is troublesome, so make it u32 * 18 + 4 // In other words, one size is 76 bytes private fd_func_sig: SharedArrayBuffer; private readonly baseCall: BaseCallParkUseArrayBuffer; private destroyed = false; /** tell other processes that the file descriptor has been closed */ private fd_close_receiver: FdCloseSenderUseArrayBuffer; /** this is not send by postMessage, * so it is not necessary to keep shared_array_buffer * this class is not used by user, * to avoid mistakes, all constructors are now required to be passed in. */ /** * Initializes a new WASIFarmParkUseArrayBuffer. * * @param fds The initial array of file descriptors. * @param stdin The standard input FD index. * @param stdout The standard output FD index. * @param stderr The standard error FD index. * @param default_allow_fds The list of FDs initially allowed for access. * @param allocator_size Optional size for the shared memory allocator. * @param max_fds_len Optional limit on the number of file descriptors. * @param unknown_fn Optional host callback invoked by user base calls. * @param max_base_calls_limit Maximum concurrent user calls; one additional * reserved slot remains available for fd mapping and destruction. * @param base_call_allocator_size Optional byte size of the multiplexed base-call payload allocator. */ constructor( fds: Array, // stdin fd number stdin: number | undefined, // stdout fd number stdout: number | undefined, // stderr fd number stderr: number | undefined, // wasi_farm_ref default allow fds default_allow_fds: Array, allocator_size?: number, max_fds_len?: number, unknown_fn?: ((arg: unknown) => Promise | unknown) | null, max_base_calls_limit?: number, base_call_allocator_size?: number, ) { super(fds, stdin, stdout, stderr, default_allow_fds, unknown_fn); this.baseCall = new BaseCallParkUseArrayBuffer( this.handleBaseCall.bind(this), { maxBaseCalls: max_base_calls_limit, allocatorBytes: base_call_allocator_size, onBeginDestroy: () => this.stopFdListeners(), }, ); if (allocator_size === undefined) { this.allocator = new AllocatorUseArrayBuffer(); } else { this.allocator = new AllocatorUseArrayBuffer( new SharedArrayBuffer(allocator_size), ); } // size of int * max_fds_len * 4 (= lock, call, wait) const _max_fds_len = max_fds_len ?? 128; this.lock_fds = new SharedArrayBuffer(4 * _max_fds_len * 3); this.fd_func_sig = new SharedArrayBuffer( fd_func_sig_u32_size * 4 * _max_fds_len, ); this.fds_len_and_num = new SharedArrayBuffer(8); const view = new Int32Array(this.fds_len_and_num); Atomics.store(view, 0, fds.length); Atomics.store(view, 1, 0); this.fd_close_receiver = new FdCloseSenderUseArrayBuffer(); } /** * Destroys this Park's base transport and fd listeners. * * The operation is synchronous and idempotent. It wakes blocked base-call * clients without waiting for callback Promises and does not destroy Animals * or external Workers that hold a reference. */ destroy(): void { this.baseCall.destroy(); } private stopFdListeners(): void { this.destroyed = true; for (let n = 0; n < this.listen_fds_terminator.length; n++) { const terminator = this.listen_fds_terminator[n]; if (terminator) { terminator(); this.listen_fds_terminator[n] = null; } } } /** * Returns cloneable transport data for reconstructing a Worker-side ref. * Cloning transfers no allocator, fd, or base-call payload ownership. */ get_ref(): WASIFarmRefUseArrayBufferObject { return { allocator: this.allocator.get_object(), lock_fds: this.lock_fds, fds_len_and_num: this.fds_len_and_num, fd_func_sig: this.fd_func_sig, baseCall: this.baseCall.get_object(), fd_close_receiver: this.fd_close_receiver.get_object(), stdin: this.stdin, stdout: this.stdout, stderr: this.stderr, default_fds: this.default_allow_fds, }; } // abstract methods implementation // from fd set ex) path_open // received and listen the fd // and set fds.length async notify_set_fd(fd: number) { if (this.destroyed) { return; } if (this.fds[fd] === undefined) { throw new Error("fd is not defined"); } if (fd >= 128) { throw new Error("fd is too big. expand is not supported yet"); } if (this.listen_fds[fd] !== undefined) { if (this.listen_fds[fd] instanceof Promise) { await this.listen_fds[fd]; } } if (this.destroyed) { return; } const { promise, terminate } = this.listen_fd(fd); this.listen_fds[fd] = promise.catch(() => undefined); this.listen_fds_terminator[fd] = terminate; const view = new Int32Array(this.fds_len_and_num); Atomics.store(view, 0, this.fds.length); // const len = Atomics.store(view, 0, this.fds.length); // console.log("notify_set_fd: len: ", len); } // abstract methods implementation // called by fd close ex) fd_close async notify_rm_fd(fd: number) { (async () => { await this.listen_fds[fd]; this.listen_fds[fd] = undefined; this.listen_fds_terminator[fd] = undefined; })(); // console.log("notify_rm_fd", fd); // console.log("fds", this.fds); // console.log("fds_map", this.fds_map); // console.log("notify_rm_fd: fds_map", this.fds_map); // console.log("notify_rm_fd: fd", fd); // console.log("notify_rm_fd: fds_map[fd]", [...this.fds_map[fd]]); await this.fd_close_receiver.send(this.fds_map[fd], fd); this.fds_map[fd] = []; } // abstract methods implementation // wait to close old listener can_set_new_fd(fd: number): [boolean, Promise | undefined] { if (this.listen_fds[fd] instanceof Promise) { return [false, this.listen_fds[fd]]; } return [true, undefined]; } // listen all fds and base // Must be called before was_ref_id is instantiated listen() { if (this.destroyed) { return; } this.listen_fds = []; for (let n = 0; n < this.fds.length; n++) { const { promise, terminate } = this.listen_fd(n); this.listen_fds.push(promise.catch(() => undefined)); this.listen_fds_terminator.push(terminate); } this.baseCall.listen(); } private async handleBaseCall( opcode: BaseCallOpcode, payload: Uint8Array, ): Promise { if (opcode === BaseCallOpcode.SetParkFdsMap) { if ( payload.byteLength < Uint32Array.BYTES_PER_ELEMENT || payload.byteLength % Uint32Array.BYTES_PER_ELEMENT !== 0 ) { throw new TypeError("fd-map payload must contain an id and whole fds"); } const words = new Uint32Array( payload.buffer, payload.byteOffset, payload.byteLength / Uint32Array.BYTES_PER_ELEMENT, ); const id = words[0]; for (let i = 1; i < words.length; i++) { const fd = words[i]; const ids = (this.fds_map[fd] ??= []); if (!ids.includes(id)) { ids.push(id); } } return undefined; } if (opcode !== BaseCallOpcode.CallUnknownFn) { throw new TypeError(`unsupported base-call opcode: ${opcode}`); } const arg = decodeStrictJsonValue(payload); const ret = this.unknown_fn ? await this.unknown_fn(arg) : undefined; return ret === undefined ? undefined : encodeStrictJsonValue(ret); } listen_fd(fd_n: number): { promise: Promise; terminate: () => void; } { return wrap_async(this.listen_fd_inner.bind(this, fd_n)); } // listen fd async listen_fd_inner(fd_n: number): Promise { const lock_view = new Int32Array(this.lock_fds, fd_n * 12); const bytes_offset = fd_n * fd_func_sig_bytes; const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); const func_sig_view_i32 = new Int32Array(this.fd_func_sig, bytes_offset); const func_sig_view_u32 = new Int32Array(this.fd_func_sig, bytes_offset); const func_sig_view_u64 = new BigUint64Array( this.fd_func_sig, bytes_offset, ); const errno_offset = fd_func_sig_u32_size - 1; Atomics.store(lock_view, 0, 0); Atomics.store(lock_view, 1, 1); Atomics.store(func_sig_view_i32, errno_offset, -1); // eslint-disable-next-line no-constant-condition while (true) { const { value } = Atomics.waitAsync(lock_view, 1, 1); if ((await value) === "timed-out") { throw new Error("timed-out"); } const set_error = (errno: number) => { // console.log("set_error", errno, "pointer", errno_offset); Atomics.store(func_sig_view_i32, errno_offset, errno); }; const func_number = Atomics.load(func_sig_view_u32, 0); // console.log("called: func: ", get_func_name_from_number(func_number), "fd: ", fd_n); switch (func_number) { // fd_advise: (fd: u32) => errno; case 7: { const fd = Atomics.load(func_sig_view_u32, 1); const error = this.fd_advise(fd); set_error(error); break; } // fd_allocate: (fd: u32, offset: u64, len: u64) => errno; case 8: { const fd = Atomics.load(func_sig_view_u32, 1); const offset = Atomics.load(func_sig_view_u64, 1); const len = Atomics.load(func_sig_view_u64, 2); const error = this.fd_allocate(fd, offset, len); set_error(error); break; } // fd_close: (fd: u32) => errno; case 9: { const fd = Atomics.load(func_sig_view_u32, 1); const error = await this.fd_close(fd); // console.log("fd_close", fd, error); set_error(error); break; } // fd_datasync: (fd: u32) => errno; case 10: { const fd = Atomics.load(func_sig_view_u32, 1); const error = this.fd_datasync(fd); set_error(error); break; } // fd_fdstat_get: (fd: u32) => [wasi.Fdstat(u32 * 6)], errno]; case 11: { const fd = Atomics.load(func_sig_view_u32, 1); const [fdstat, ret] = this.fd_fdstat_get(fd); if (fdstat) { Atomics.store(func_sig_view_u8, 0, fdstat.fs_filetype); Atomics.store(func_sig_view_u16, 2, fdstat.fs_flags); Atomics.store(func_sig_view_u64, 1, fdstat.fs_rights_base); Atomics.store(func_sig_view_u64, 2, fdstat.fs_rights_inherited); } set_error(ret); break; } // fd_fdstat_set_flags: (fd: u32, flags: u16) => errno; case 12: { const fd = Atomics.load(func_sig_view_u32, 1); const flags = Atomics.load(func_sig_view_u16, 4); const error = this.fd_fdstat_set_flags(fd, flags); set_error(error); break; } // fd_fdstat_set_rights: (fd: u32, fs_rights_base: u64, fs_rights_inheriting: u64) => errno; case 13: { const fd = Atomics.load(func_sig_view_u32, 1); const fs_rights_base = Atomics.load(func_sig_view_u64, 1); const fs_rights_inheriting = Atomics.load(func_sig_view_u64, 2); const error = this.fd_fdstat_set_rights( fd, fs_rights_base, fs_rights_inheriting, ); set_error(error); break; } // fd_filestat_get: (fd: u32) => [wasi.Filestat(u32 * 16)], errno]; case 14: { const fd = Atomics.load(func_sig_view_u32, 1); const [filestat, ret] = this.fd_filestat_get(fd); if (filestat) { Atomics.store(func_sig_view_u64, 0, filestat.dev); Atomics.store(func_sig_view_u64, 1, filestat.ino); Atomics.store(func_sig_view_u8, 16, filestat.filetype); Atomics.store(func_sig_view_u64, 3, filestat.nlink); Atomics.store(func_sig_view_u64, 4, filestat.size); Atomics.store(func_sig_view_u64, 5, filestat.atim); Atomics.store(func_sig_view_u64, 6, filestat.mtim); Atomics.store(func_sig_view_u64, 7, filestat.ctim); } set_error(ret); break; } // fd_filestat_set_size: (fd: u32, size: u64) => errno; case 15: { const fd = Atomics.load(func_sig_view_u32, 1); const size = Atomics.load(func_sig_view_u64, 1); const error = this.fd_filestat_set_size(fd, size); set_error(error); break; } // fd_filestat_set_times: (fd: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; case 16: { const fd = Atomics.load(func_sig_view_u32, 1); const atim = Atomics.load(func_sig_view_u64, 1); const mtim = Atomics.load(func_sig_view_u64, 2); const fst_flags = Atomics.load(func_sig_view_u16, 12); const error = this.fd_filestat_set_times(fd, atim, mtim, fst_flags); set_error(error); break; } // fd_pread: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, data_ptr, errno]; case 17: { const fd = Atomics.load(func_sig_view_u32, 1); const iovs_ptr = Atomics.load(func_sig_view_u32, 2); const iovs_ptr_len = Atomics.load(func_sig_view_u32, 3); const offset = Atomics.load(func_sig_view_u64, 2); const data = new Uint32Array( this.allocator.get_memory(iovs_ptr, iovs_ptr_len), ); this.allocator.free(iovs_ptr, iovs_ptr_len); const iovecs: wasi.Iovec[] = []; for (let i = 0; i < iovs_ptr_len; i += 8) { const iovec = new wasi.Iovec(); iovec.buf = data[i * 2]; iovec.buf_len = data[i * 2 + 1]; iovecs.push(iovec); } // const [[nread, buffer8], error] = this.fd_pread(fd, iovecs, offset); const [nread_and_buffer, error] = this.fd_pread(fd, iovecs, offset); const [nread, buffer8] = nread_and_buffer ?? [undefined, undefined]; if (nread !== undefined) { Atomics.store(func_sig_view_u32, 0, nread); } if (buffer8) { await this.allocator.async_write( buffer8, this.fd_func_sig, fd * fd_func_sig_u32_size + 1, ); } set_error(error); break; } // fd_prestat_get: (fd: u32) => [wasi.Prestat(u32 * 2)], errno]; case 18: { const fd = Atomics.load(func_sig_view_u32, 1); const [prestat, ret] = this.fd_prestat_get(fd); // console.log("fd_prestat_get", prestat, ret); if (prestat) { Atomics.store(func_sig_view_u32, 0, prestat.tag); Atomics.store( func_sig_view_u32, 1, prestat.inner.pr_name.byteLength, ); } set_error(ret); break; } // fd_prestat_dir_name: (fd: u32, path_len: u32) => [path_ptr: pointer, path_len: u32, errno]; case 19: { const fd = Atomics.load(func_sig_view_u32, 1); const path_len = Atomics.load(func_sig_view_u32, 2); const [prestat_dir_name, ret] = this.fd_prestat_dir_name( fd, path_len, ); // console.log("fd_prestat_dir_name: park: ", prestat_dir_name); if ( prestat_dir_name && (ret === wasi.ERRNO_SUCCESS || ret === wasi.ERRNO_NAMETOOLONG) ) { await this.allocator.async_write( prestat_dir_name, this.fd_func_sig, fd * fd_func_sig_u32_size, ); } set_error(ret); break; } // fd_pwrite: (fd: u32, write_data: pointer, write_data_len: u32, offset: u64) => [u32, errno]; case 20: { const fd = Atomics.load(func_sig_view_u32, 1); const write_data_ptr = Atomics.load(func_sig_view_u32, 2); const write_data_len = Atomics.load(func_sig_view_u32, 3); const offset = Atomics.load(func_sig_view_u64, 2); const data = new Uint8Array( this.allocator.get_memory(write_data_ptr, write_data_len), ); this.allocator.free(write_data_ptr, write_data_len); const [nwritten, error] = this.fd_pwrite(fd, data, offset); if (nwritten !== undefined) { Atomics.store(func_sig_view_u32, 0, nwritten); } set_error(error); break; } // fd_read: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, data_ptr, errno]; case 21: { const fd = Atomics.load(func_sig_view_u32, 1); const iovs_ptr = Atomics.load(func_sig_view_u32, 2); const iovs_ptr_len = Atomics.load(func_sig_view_u32, 3); // console.log("fd_read: park: iovs: Uint8Array", this.allocator.get_memory(iovs_ptr, iovs_ptr_len)); // console.log("ptr_len", iovs_ptr_len); const iovs = new Uint32Array( this.allocator.get_memory(iovs_ptr, iovs_ptr_len), ); this.allocator.free(iovs_ptr, iovs_ptr_len); // console.log("fd_read: park: iovs", iovs); const iovecs: wasi.Iovec[] = []; for (let i = 0; i < iovs_ptr_len; i += 8) { const iovec = new wasi.Iovec(); iovec.buf = iovs[i * 2]; iovec.buf_len = iovs[i * 2 + 1]; iovecs.push(iovec); } // console.log("fd_read: park: iovecs", iovecs); const [nread_and_buffer, error] = this.fd_read(fd, iovecs); const [nread, buffer8] = nread_and_buffer ?? [undefined, undefined]; // console.log("fd_read: park: buffer8", new TextDecoder().decode(buffer8)); if (nread !== undefined) { Atomics.store(func_sig_view_u32, 0, nread); } if (buffer8) { await this.allocator.async_write( buffer8, this.fd_func_sig, fd * fd_func_sig_u32_size + 1, ); } set_error(error); break; } // fd_readdir: (fd: u32, buf_len: u32, cookie: u64) => [buf_ptr: pointer, buf_len: u32, buf_used: u32, errno]; case 22: { const fd = Atomics.load(func_sig_view_u32, 1); const buf_len = Atomics.load(func_sig_view_u32, 2); const cookie = Atomics.load(func_sig_view_u64, 2); const [array_and_buf_used, error] = this.fd_readdir( fd, buf_len, cookie, ); const [array, buf_used] = array_and_buf_used ?? [ undefined, undefined, ]; if (array) { await this.allocator.async_write( array, this.fd_func_sig, fd * fd_func_sig_u32_size, ); } if (buf_used !== undefined) { Atomics.store(func_sig_view_u32, 2, buf_used); } set_error(error); break; } // fd_seek: (fd: u32, offset: i64, whence: u8) => [u64, errno]; case 24: { const fd = Atomics.load(func_sig_view_u32, 1); const offset = Atomics.load(func_sig_view_u64, 1); const whence = Atomics.load(func_sig_view_u8, 16); const [new_offset, error] = this.fd_seek(fd, offset, whence); if (new_offset !== undefined) { Atomics.store(func_sig_view_u64, 0, new_offset); } set_error(error); break; } // fd_sync: (fd: u32) => errno; case 25: { const fd = Atomics.load(func_sig_view_u32, 1); const error = this.fd_sync(fd); set_error(error); break; } // fd_tell: (fd: u32) => [u64, errno]; case 26: { const fd = Atomics.load(func_sig_view_u32, 1); const [offset, error] = this.fd_tell(fd); if (offset !== undefined) { Atomics.store(func_sig_view_u64, 0, offset); } set_error(error); break; } // fd_write: (fd: u32, write_data: pointer, write_data_len: u32) => [u32, errno]; case 27: { const fd = Atomics.load(func_sig_view_u32, 1); const write_data_ptr = Atomics.load(func_sig_view_u32, 2); const write_data_len = Atomics.load(func_sig_view_u32, 3); const data = new Uint8Array( this.allocator.get_memory(write_data_ptr, write_data_len), ); this.allocator.free(write_data_ptr, write_data_len); // console.log("allocator", this.allocator); // console.log("fd_write: park: write_data", new TextDecoder().decode(data)); const [nwritten, error] = await this.fd_write(fd, data); // console.log("fd_write: park: error", error); if (nwritten !== undefined) { Atomics.store(func_sig_view_u32, 0, nwritten); } set_error(error); break; } // path_create_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; case 28: { const fd = Atomics.load(func_sig_view_u32, 1); const path_ptr = Atomics.load(func_sig_view_u32, 2); const path_len = Atomics.load(func_sig_view_u32, 3); const path = new Uint8Array( this.allocator.get_memory(path_ptr, path_len), ); const path_str = new TextDecoder().decode(path); this.allocator.free(path_ptr, path_len); const error = this.path_create_directory(fd, path_str); set_error(error); break; } // path_filestat_get: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32) => [wasi.Filestat(u32 * 16), errno]; case 29: { const fd = Atomics.load(func_sig_view_u32, 1); const flags = Atomics.load(func_sig_view_u32, 2); const path_ptr = Atomics.load(func_sig_view_u32, 3); const path_len = Atomics.load(func_sig_view_u32, 4); const path = new Uint8Array( this.allocator.get_memory(path_ptr, path_len), ); const path_str = new TextDecoder().decode(path); this.allocator.free(path_ptr, path_len); const [filestat, ret] = this.path_filestat_get(fd, flags, path_str); if (filestat) { Atomics.store(func_sig_view_u64, 0, filestat.dev); Atomics.store(func_sig_view_u64, 1, filestat.ino); Atomics.store(func_sig_view_u8, 16, filestat.filetype); Atomics.store(func_sig_view_u64, 3, filestat.nlink); Atomics.store(func_sig_view_u64, 4, filestat.size); Atomics.store(func_sig_view_u64, 5, filestat.atim); Atomics.store(func_sig_view_u64, 6, filestat.mtim); Atomics.store(func_sig_view_u64, 7, filestat.ctim); } set_error(ret); break; } // path_filestat_set_times: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; case 30: { const fd = Atomics.load(func_sig_view_u32, 1); const flags = Atomics.load(func_sig_view_u32, 2); const path_ptr = Atomics.load(func_sig_view_u32, 3); const path_len = Atomics.load(func_sig_view_u32, 4); const atim = Atomics.load(func_sig_view_u64, 3); const mtim = Atomics.load(func_sig_view_u64, 4); const fst_flags = Atomics.load(func_sig_view_u16, 12); const path = new Uint8Array( this.allocator.get_memory(path_ptr, path_len), ); const path_str = new TextDecoder().decode(path); this.allocator.free(path_ptr, path_len); const error = this.path_filestat_set_times( fd, flags, path_str, atim, mtim, fst_flags, ); set_error(error); break; } // path_link: (old_fd: u32, old_flags: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; case 31: { const old_fd = Atomics.load(func_sig_view_u32, 1); const old_flags = Atomics.load(func_sig_view_u32, 2); const old_path_ptr = Atomics.load(func_sig_view_u32, 3); const old_path_len = Atomics.load(func_sig_view_u32, 4); const new_fd = Atomics.load(func_sig_view_u32, 5); const new_path_ptr = Atomics.load(func_sig_view_u32, 6); const new_path_len = Atomics.load(func_sig_view_u32, 7); const old_path = new Uint8Array( this.allocator.get_memory(old_path_ptr, old_path_len), ); const old_path_str = new TextDecoder().decode(old_path); this.allocator.free(old_path_ptr, old_path_len); const new_path = new Uint8Array( this.allocator.get_memory(new_path_ptr, new_path_len), ); const new_path_str = new TextDecoder().decode(new_path); this.allocator.free(new_path_ptr, new_path_len); const error = this.path_link( old_fd, old_flags, old_path_str, new_fd, new_path_str, ); set_error(error); break; } // path_open: (fd: u32, dirflags: u32, path_ptr: pointer, path_len: u32, oflags: u32, fs_rights_base: u64, fs_rights_inheriting: u64, fdflags: u16) => [u32, errno]; case 32: { const fd = Atomics.load(func_sig_view_u32, 1); const dirflags = Atomics.load(func_sig_view_u32, 2); const path_ptr = Atomics.load(func_sig_view_u32, 3); const path_len = Atomics.load(func_sig_view_u32, 4); const oflags = Atomics.load(func_sig_view_u32, 5); const fs_rights_base = Atomics.load(func_sig_view_u64, 3); const fs_rights_inheriting = Atomics.load(func_sig_view_u64, 4); const fd_flags = Atomics.load(func_sig_view_u16, 20); const path = new Uint8Array( this.allocator.get_memory(path_ptr, path_len), ); const path_str = new TextDecoder().decode(path); this.allocator.free(path_ptr, path_len); const [opened_fd, error] = await this.path_open( fd, dirflags, path_str, oflags, fs_rights_base, fs_rights_inheriting, fd_flags, ); // console.log("path_open: opend_fd", opened_fd, error); if (opened_fd !== undefined) { Atomics.store(func_sig_view_u32, 0, opened_fd); } set_error(error); break; } // path_readlink: (fd: u32, path_ptr: pointer, path_len: u32, buf_len: u32) => [buf_len: u32, data_ptr: pointer, data_len: u32, errno]; case 33: { const fd = Atomics.load(func_sig_view_u32, 1); const path_ptr = Atomics.load(func_sig_view_u32, 2); const path_len = Atomics.load(func_sig_view_u32, 3); const buf_len = Atomics.load(func_sig_view_u32, 4); const path = new Uint8Array( this.allocator.get_memory(path_ptr, path_len), ); const path_str = new TextDecoder().decode(path); this.allocator.free(path_ptr, path_len); const [buf, error] = this.path_readlink(fd, path_str, buf_len); if (buf) { await this.allocator.async_write( buf, this.fd_func_sig, fd * fd_func_sig_u32_size + 1, ); Atomics.store(func_sig_view_u32, 0, buf.byteLength); } set_error(error); break; } // path_remove_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; case 34: { const fd = Atomics.load(func_sig_view_u32, 1); const path_ptr = Atomics.load(func_sig_view_u32, 2); const path_len = Atomics.load(func_sig_view_u32, 3); const path = new Uint8Array( this.allocator.get_memory(path_ptr, path_len), ); const path_str = new TextDecoder().decode(path); this.allocator.free(path_ptr, path_len); const error = this.path_remove_directory(fd, path_str); set_error(error); break; } // path_rename: (old_fd: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; case 35: { const fd = Atomics.load(func_sig_view_u32, 1); const old_path_ptr = Atomics.load(func_sig_view_u32, 2); const old_path_len = Atomics.load(func_sig_view_u32, 3); const new_fd = Atomics.load(func_sig_view_u32, 4); const new_path_ptr = Atomics.load(func_sig_view_u32, 5); const new_path_len = Atomics.load(func_sig_view_u32, 6); const old_path = new Uint8Array( this.allocator.get_memory(old_path_ptr, old_path_len), ); const old_path_str = new TextDecoder().decode(old_path); this.allocator.free(old_path_ptr, old_path_len); const new_path = new Uint8Array( this.allocator.get_memory(new_path_ptr, new_path_len), ); const new_path_str = new TextDecoder().decode(new_path); this.allocator.free(new_path_ptr, new_path_len); const error = this.path_rename( fd, old_path_str, new_fd, new_path_str, ); set_error(error); break; } // path_symlink: (old_path_ptr: pointer, old_path_len: u32, fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; case 36: { const old_path_ptr = Atomics.load(func_sig_view_u32, 1); const old_path_len = Atomics.load(func_sig_view_u32, 2); const fd = Atomics.load(func_sig_view_u32, 3); const new_path_ptr = Atomics.load(func_sig_view_u32, 4); const new_path_len = Atomics.load(func_sig_view_u32, 5); const old_path = new Uint8Array( this.allocator.get_memory(old_path_ptr, old_path_len), ); const old_path_str = new TextDecoder().decode(old_path); this.allocator.free(old_path_ptr, old_path_len); const new_path = new Uint8Array( this.allocator.get_memory(new_path_ptr, new_path_len), ); const new_path_str = new TextDecoder().decode(new_path); this.allocator.free(new_path_ptr, new_path_len); set_error(this.path_symlink(old_path_str, fd, new_path_str)); break; } // path_unlink_file: (fd: u32, path_ptr: pointer, path_len: u32) => errno; case 37: { const fd = Atomics.load(func_sig_view_u32, 1); const path_ptr = Atomics.load(func_sig_view_u32, 2); const path_len = Atomics.load(func_sig_view_u32, 3); const path = new Uint8Array( this.allocator.get_memory(path_ptr, path_len), ); const path_str = new TextDecoder().decode(path); this.allocator.free(path_ptr, path_len); set_error(this.path_unlink_file(fd, path_str)); break; } default: { throw new Error(`Unknown function number: ${func_number}`); } } Atomics.store(lock_view, 1, 1); Atomics.store(lock_view, 2, 0); const n = Atomics.notify(lock_view, 2); if (n !== 1) { if (n === 0) { console.warn("notify number is 0. ref is late?"); } else { console.warn(`notify number is not 1: ${n}`); } } if (this.fds[fd_n] === undefined) { break; } } } }