1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 16:49:18 -05:00
denoland-deno/std/wasi/snapshot_preview1.ts

1528 lines
38 KiB
TypeScript

/* eslint-disable */
import { resolve } from "../path/mod.ts";
const CLOCKID_REALTIME = 0;
const CLOCKID_MONOTONIC = 1;
const CLOCKID_PROCESS_CPUTIME_ID = 2;
const CLOCKID_THREAD_CPUTIME_ID = 3;
const ERRNO_SUCCESS = 0;
const ERRNO_2BIG = 1;
const ERRNO_ACCES = 2;
const ERRNO_ADDRINUSE = 3;
const ERRNO_ADDRNOTAVAIL = 4;
const ERRNO_AFNOSUPPORT = 5;
const ERRNO_AGAIN = 6;
const ERRNO_ALREADY = 7;
const ERRNO_BADF = 8;
const ERRNO_BADMSG = 9;
const ERRNO_BUSY = 10;
const ERRNO_CANCELED = 11;
const ERRNO_CHILD = 12;
const ERRNO_CONNABORTED = 13;
const ERRNO_CONNREFUSED = 14;
const ERRNO_CONNRESET = 15;
const ERRNO_DEADLK = 16;
const ERRNO_DESTADDRREQ = 17;
const ERRNO_DOM = 18;
const ERRNO_DQUOT = 19;
const ERRNO_EXIST = 20;
const ERRNO_FAULT = 21;
const ERRNO_FBIG = 22;
const ERRNO_HOSTUNREACH = 23;
const ERRNO_IDRM = 24;
const ERRNO_ILSEQ = 25;
const ERRNO_INPROGRESS = 26;
const ERRNO_INTR = 27;
const ERRNO_INVAL = 28;
const ERRNO_IO = 29;
const ERRNO_ISCONN = 30;
const ERRNO_ISDIR = 31;
const ERRNO_LOOP = 32;
const ERRNO_MFILE = 33;
const ERRNO_MLINK = 34;
const ERRNO_MSGSIZE = 35;
const ERRNO_MULTIHOP = 36;
const ERRNO_NAMETOOLONG = 37;
const ERRNO_NETDOWN = 38;
const ERRNO_NETRESET = 39;
const ERRNO_NETUNREACH = 40;
const ERRNO_NFILE = 41;
const ERRNO_NOBUFS = 42;
const ERRNO_NODEV = 43;
const ERRNO_NOENT = 44;
const ERRNO_NOEXEC = 45;
const ERRNO_NOLCK = 46;
const ERRNO_NOLINK = 47;
const ERRNO_NOMEM = 48;
const ERRNO_NOMSG = 49;
const ERRNO_NOPROTOOPT = 50;
const ERRNO_NOSPC = 51;
const ERRNO_NOSYS = 52;
const ERRNO_NOTCONN = 53;
const ERRNO_NOTDIR = 54;
const ERRNO_NOTEMPTY = 55;
const ERRNO_NOTRECOVERABLE = 56;
const ERRNO_NOTSOCK = 57;
const ERRNO_NOTSUP = 58;
const ERRNO_NOTTY = 59;
const ERRNO_NXIO = 60;
const ERRNO_OVERFLOW = 61;
const ERRNO_OWNERDEAD = 62;
const ERRNO_PERM = 63;
const ERRNO_PIPE = 64;
const ERRNO_PROTO = 65;
const ERRNO_PROTONOSUPPORT = 66;
const ERRNO_PROTOTYPE = 67;
const ERRNO_RANGE = 68;
const ERRNO_ROFS = 69;
const ERRNO_SPIPE = 70;
const ERRNO_SRCH = 71;
const ERRNO_STALE = 72;
const ERRNO_TIMEDOUT = 73;
const ERRNO_TXTBSY = 74;
const ERRNO_XDEV = 75;
const ERRNO_NOTCAPABLE = 76;
const RIGHTS_FD_DATASYNC = 0x0000000000000001n;
const RIGHTS_FD_READ = 0x0000000000000002n;
const RIGHTS_FD_SEEK = 0x0000000000000004n;
const RIGHTS_FD_FDSTAT_SET_FLAGS = 0x0000000000000008n;
const RIGHTS_FD_SYNC = 0x0000000000000010n;
const RIGHTS_FD_TELL = 0x0000000000000020n;
const RIGHTS_FD_WRITE = 0x0000000000000040n;
const RIGHTS_FD_ADVISE = 0x0000000000000080n;
const RIGHTS_FD_ALLOCATE = 0x0000000000000100n;
const RIGHTS_PATH_CREATE_DIRECTORY = 0x0000000000000200n;
const RIGHTS_PATH_CREATE_FILE = 0x0000000000000400n;
const RIGHTS_PATH_LINK_SOURCE = 0x0000000000000800n;
const RIGHTS_PATH_LINK_TARGET = 0x0000000000001000n;
const RIGHTS_PATH_OPEN = 0x0000000000002000n;
const RIGHTS_FD_READDIR = 0x0000000000004000n;
const RIGHTS_PATH_READLINK = 0x0000000000008000n;
const RIGHTS_PATH_RENAME_SOURCE = 0x0000000000010000n;
const RIGHTS_PATH_RENAME_TARGET = 0x0000000000020000n;
const RIGHTS_PATH_FILESTAT_GET = 0x0000000000040000n;
const RIGHTS_PATH_FILESTAT_SET_SIZE = 0x0000000000080000n;
const RIGHTS_PATH_FILESTAT_SET_TIMES = 0x0000000000100000n;
const RIGHTS_FD_FILESTAT_GET = 0x0000000000200000n;
const RIGHTS_FD_FILESTAT_SET_SIZE = 0x0000000000400000n;
const RIGHTS_FD_FILESTAT_SET_TIMES = 0x0000000000800000n;
const RIGHTS_PATH_SYMLINK = 0x0000000001000000n;
const RIGHTS_PATH_REMOVE_DIRECTORY = 0x0000000002000000n;
const RIGHTS_PATH_UNLINK_FILE = 0x0000000004000000n;
const RIGHTS_POLL_FD_READWRITE = 0x0000000008000000n;
const RIGHTS_SOCK_SHUTDOWN = 0x0000000010000000n;
const WHENCE_SET = 0;
const WHENCE_CUR = 1;
const WHENCE_END = 2;
const FILETYPE_UNKNOWN = 0;
const FILETYPE_BLOCK_DEVICE = 1;
const FILETYPE_CHARACTER_DEVICE = 2;
const FILETYPE_DIRECTORY = 3;
const FILETYPE_REGULAR_FILE = 4;
const FILETYPE_SOCKET_DGRAM = 5;
const FILETYPE_SOCKET_STREAM = 6;
const FILETYPE_SYMBOLIC_LINK = 7;
const ADVICE_NORMAL = 0;
const ADVICE_SEQUENTIAL = 1;
const ADVICE_RANDOM = 2;
const ADVICE_WILLNEED = 3;
const ADVICE_DONTNEED = 4;
const ADVICE_NOREUSE = 5;
const FDFLAGS_APPEND = 0x0001;
const FDFLAGS_DSYNC = 0x0002;
const FDFLAGS_NONBLOCK = 0x0004;
const FDFLAGS_RSYNC = 0x0008;
const FDFLAGS_SYNC = 0x0010;
const FSTFLAGS_ATIM = 0x0001;
const FSTFLAGS_ATIM_NOW = 0x0002;
const FSTFLAGS_MTIM = 0x0004;
const FSTFLAGS_MTIM_NOW = 0x0008;
const LOOKUPFLAGS_SYMLINK_FOLLOW = 0x0001;
const OFLAGS_CREAT = 0x0001;
const OFLAGS_DIRECTORY = 0x0002;
const OFLAGS_EXCL = 0x0004;
const OFLAGS_TRUNC = 0x0008;
const EVENTTYPE_CLOCK = 0;
const EVENTTYPE_FD_READ = 1;
const EVENTTYPE_FD_WRITE = 2;
const EVENTRWFLAGS_FD_READWRITE_HANGUP = 1;
const SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME = 1;
const SIGNAL_NONE = 0;
const SIGNAL_HUP = 1;
const SIGNAL_INT = 2;
const SIGNAL_QUIT = 3;
const SIGNAL_ILL = 4;
const SIGNAL_TRAP = 5;
const SIGNAL_ABRT = 6;
const SIGNAL_BUS = 7;
const SIGNAL_FPE = 8;
const SIGNAL_KILL = 9;
const SIGNAL_USR1 = 10;
const SIGNAL_SEGV = 11;
const SIGNAL_USR2 = 12;
const SIGNAL_PIPE = 13;
const SIGNAL_ALRM = 14;
const SIGNAL_TERM = 15;
const SIGNAL_CHLD = 16;
const SIGNAL_CONT = 17;
const SIGNAL_STOP = 18;
const SIGNAL_TSTP = 19;
const SIGNAL_TTIN = 20;
const SIGNAL_TTOU = 21;
const SIGNAL_URG = 22;
const SIGNAL_XCPU = 23;
const SIGNAL_XFSZ = 24;
const SIGNAL_VTALRM = 25;
const SIGNAL_PROF = 26;
const SIGNAL_WINCH = 27;
const SIGNAL_POLL = 28;
const SIGNAL_PWR = 29;
const SIGNAL_SYS = 30;
const RIFLAGS_RECV_PEEK = 0x0001;
const RIFLAGS_RECV_WAITALL = 0x0002;
const ROFLAGS_RECV_DATA_TRUNCATED = 0x0001;
const SDFLAGS_RD = 0x0001;
const SDFLAGS_WR = 0x0002;
const PREOPENTYPE_DIR = 0;
const clock_res_realtime = function (): bigint {
return BigInt(1e6);
};
const clock_res_monotonic = function (): bigint {
return BigInt(1e3);
};
const clock_res_process = clock_res_monotonic;
const clock_res_thread = clock_res_monotonic;
const clock_time_realtime = function (): bigint {
return BigInt(Date.now()) * BigInt(1e6);
};
const clock_time_monotonic = function (): bigint {
const t = performance.now();
const s = Math.trunc(t);
const ms = Math.floor((t - s) * 1e3);
return BigInt(s) * BigInt(1e9) + BigInt(ms) * BigInt(1e6);
};
const clock_time_process = clock_time_monotonic;
const clock_time_thread = clock_time_monotonic;
function errno(err: Error) {
switch (err.name) {
case "NotFound":
return ERRNO_NOENT;
case "PermissionDenied":
return ERRNO_ACCES;
case "ConnectionRefused":
return ERRNO_CONNREFUSED;
case "ConnectionReset":
return ERRNO_CONNRESET;
case "ConnectionAborted":
return ERRNO_CONNABORTED;
case "NotConnected":
return ERRNO_NOTCONN;
case "AddrInUse":
return ERRNO_ADDRINUSE;
case "AddrNotAvailable":
return ERRNO_ADDRNOTAVAIL;
case "BrokenPipe":
return ERRNO_PIPE;
case "InvalidData":
return ERRNO_INVAL;
case "TimedOut":
return ERRNO_TIMEDOUT;
case "Interrupted":
return ERRNO_INTR;
case "BadResource":
return ERRNO_BADF;
case "Busy":
return ERRNO_BUSY;
default:
return ERRNO_INVAL;
}
}
export interface ModuleOptions {
args?: string[];
env?: { [key: string]: string | undefined };
preopens?: { [key: string]: string };
memory?: WebAssembly.Memory;
}
export default class Module {
args: string[];
env: { [key: string]: string | undefined };
memory: WebAssembly.Memory;
// deno-lint-ignore no-explicit-any
fds: any[];
// deno-lint-ignore no-explicit-any
exports: { [key: string]: any };
constructor(options: ModuleOptions) {
this.args = options.args ? options.args : [];
this.env = options.env ? options.env : {};
this.memory = options.memory!;
this.fds = [
{
type: FILETYPE_CHARACTER_DEVICE,
handle: Deno.stdin,
},
{
type: FILETYPE_CHARACTER_DEVICE,
handle: Deno.stdout,
},
{
type: FILETYPE_CHARACTER_DEVICE,
handle: Deno.stderr,
},
];
if (options.preopens) {
for (const [vpath, path] of Object.entries(options.preopens)) {
const type = FILETYPE_DIRECTORY;
const entries = Array.from(Deno.readDirSync(path));
const entry = {
type,
entries,
path,
vpath,
};
this.fds.push(entry);
}
}
this.exports = {
args_get: (argv_ptr: number, argv_buf_ptr: number): number => {
const args = this.args;
const text = new TextEncoder();
const heap = new Uint8Array(this.memory.buffer);
const view = new DataView(this.memory.buffer);
for (let arg of args) {
view.setUint32(argv_ptr, argv_buf_ptr, true);
argv_ptr += 4;
const data = text.encode(`${arg}\0`);
heap.set(data, argv_buf_ptr);
argv_buf_ptr += data.length;
}
return ERRNO_SUCCESS;
},
args_sizes_get: (argc_out: number, argv_buf_size_out: number): number => {
const args = this.args;
const text = new TextEncoder();
const view = new DataView(this.memory.buffer);
view.setUint32(argc_out, args.length, true);
view.setUint32(
argv_buf_size_out,
args.reduce(function (acc, arg) {
return acc + text.encode(`${arg}\0`).length;
}, 0),
true
);
return ERRNO_SUCCESS;
},
environ_get: (environ_ptr: number, environ_buf_ptr: number): number => {
const entries = Object.entries(this.env);
const text = new TextEncoder();
const heap = new Uint8Array(this.memory.buffer);
const view = new DataView(this.memory.buffer);
for (let [key, value] of entries) {
view.setUint32(environ_ptr, environ_buf_ptr, true);
environ_ptr += 4;
const data = text.encode(`${key}=${value}\0`);
heap.set(data, environ_buf_ptr);
environ_buf_ptr += data.length;
}
return ERRNO_SUCCESS;
},
environ_sizes_get: (
environc_out: number,
environ_buf_size_out: number
): number => {
const entries = Object.entries(this.env);
const text = new TextEncoder();
const view = new DataView(this.memory.buffer);
view.setUint32(environc_out, entries.length, true);
view.setUint32(
environ_buf_size_out,
entries.reduce(function (acc, [key, value]) {
return acc + text.encode(`${key}=${value}\0`).length;
}, 0),
true
);
return ERRNO_SUCCESS;
},
clock_res_get: (id: number, resolution_out: number): number => {
const view = new DataView(this.memory.buffer);
switch (id) {
case CLOCKID_REALTIME:
view.setBigUint64(resolution_out, clock_res_realtime(), true);
break;
case CLOCKID_MONOTONIC:
view.setBigUint64(resolution_out, clock_res_monotonic(), true);
break;
case CLOCKID_PROCESS_CPUTIME_ID:
view.setBigUint64(resolution_out, clock_res_process(), true);
break;
case CLOCKID_THREAD_CPUTIME_ID:
view.setBigUint64(resolution_out, clock_res_thread(), true);
break;
default:
return ERRNO_INVAL;
}
return ERRNO_SUCCESS;
},
clock_time_get: (
id: number,
precision: bigint,
time_out: number
): number => {
const view = new DataView(this.memory.buffer);
switch (id) {
case CLOCKID_REALTIME:
view.setBigUint64(time_out, clock_time_realtime(), true);
break;
case CLOCKID_MONOTONIC:
view.setBigUint64(time_out, clock_time_monotonic(), true);
break;
case CLOCKID_PROCESS_CPUTIME_ID:
view.setBigUint64(time_out, clock_time_process(), true);
break;
case CLOCKID_THREAD_CPUTIME_ID:
view.setBigUint64(time_out, clock_time_thread(), true);
break;
default:
return ERRNO_INVAL;
}
return ERRNO_SUCCESS;
},
fd_advise: (
fd: number,
offset: bigint,
len: bigint,
advice: number
): number => {
return ERRNO_NOSYS;
},
fd_allocate: (fd: number, offset: bigint, len: bigint): number => {
return ERRNO_NOSYS;
},
fd_close: (fd: number): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (entry.handle) {
entry.handle.close();
}
delete this.fds[fd];
return ERRNO_SUCCESS;
},
fd_datasync: (fd: number): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
try {
Deno.fdatasyncSync(entry.handle.rid);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
fd_fdstat_get: (fd: number, stat_out: number): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const view = new DataView(this.memory.buffer);
view.setUint8(stat_out, entry.type);
view.setUint16(stat_out + 4, 0, true); // TODO
view.setBigUint64(stat_out + 8, 0n, true); // TODO
view.setBigUint64(stat_out + 16, 0n, true); // TODO
return ERRNO_SUCCESS;
},
fd_fdstat_set_flags: (fd: number, flags: number): number => {
return ERRNO_NOSYS;
},
fd_fdstat_set_rights: (
fd: number,
fs_rights_base: bigint,
fs_rights_inheriting: bigint
): number => {
return ERRNO_NOSYS;
},
fd_filestat_get: (fd: number, buf_out: number): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const view = new DataView(this.memory.buffer);
try {
const info = Deno.fstatSync(entry.handle.rid);
if (entry.type === undefined) {
switch (true) {
case info.isFile:
entry.type = FILETYPE_REGULAR_FILE;
break;
case info.isDirectory:
entry.type = FILETYPE_DIRECTORY;
break;
case info.isSymlink:
entry.type = FILETYPE_SYMBOLIC_LINK;
break;
default:
entry.type = FILETYPE_UNKNOWN;
break;
}
}
view.setBigUint64(buf_out, BigInt(info.dev ? info.dev : 0), true);
buf_out += 8;
view.setBigUint64(buf_out, BigInt(info.ino ? info.ino : 0), true);
buf_out += 8;
view.setUint8(buf_out, entry.type);
buf_out += 8;
view.setUint32(buf_out, Number(info.nlink), true);
buf_out += 8;
view.setBigUint64(buf_out, BigInt(info.size), true);
buf_out += 8;
view.setBigUint64(
buf_out,
BigInt(info.atime ? info.atime.getTime() * 1e6 : 0),
true
);
buf_out += 8;
view.setBigUint64(
buf_out,
BigInt(info.mtime ? info.mtime.getTime() * 1e6 : 0),
true
);
buf_out += 8;
view.setBigUint64(
buf_out,
BigInt(info.birthtime ? info.birthtime.getTime() * 1e6 : 0),
true
);
buf_out += 8;
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
fd_filestat_set_size: (fd: number, size: bigint): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
try {
Deno.ftruncateSync(entry.handle.rid, Number(size));
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
fd_filestat_set_times: (
fd: number,
atim: bigint,
mtim: bigint,
fst_flags: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
if ((fst_flags & FSTFLAGS_ATIM_NOW) == FSTFLAGS_ATIM_NOW) {
atim = BigInt(Date.now() * 1e6);
}
if ((fst_flags & FSTFLAGS_MTIM_NOW) == FSTFLAGS_MTIM_NOW) {
mtim = BigInt(Date.now() * 1e6);
}
try {
Deno.utimeSync(entry.path, Number(atim), Number(mtim));
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
fd_pread: (
fd: number,
iovs_ptr: number,
iovs_len: number,
offset: bigint,
nread_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const seek = entry.handle.seekSync(0, Deno.SeekMode.Current);
const view = new DataView(this.memory.buffer);
let nread = 0;
for (let i = 0; i < iovs_len; i++) {
const data_ptr = view.getUint32(iovs_ptr, true);
iovs_ptr += 4;
const data_len = view.getUint32(iovs_ptr, true);
iovs_ptr += 4;
const data = new Uint8Array(this.memory.buffer, data_ptr, data_len);
nread += entry.handle.readSync(data);
}
entry.handle.seekSync(seek, Deno.SeekMode.Start);
view.setUint32(nread_out, nread, true);
return ERRNO_SUCCESS;
},
fd_prestat_get: (fd: number, buf_out: number): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.vpath) {
return ERRNO_BADF;
}
const view = new DataView(this.memory.buffer);
view.setUint8(buf_out, PREOPENTYPE_DIR);
view.setUint32(
buf_out + 4,
new TextEncoder().encode(entry.vpath).byteLength,
true
);
return ERRNO_SUCCESS;
},
fd_prestat_dir_name: (
fd: number,
path_ptr: number,
path_len: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.vpath) {
return ERRNO_BADF;
}
const data = new Uint8Array(this.memory.buffer, path_ptr, path_len);
data.set(new TextEncoder().encode(entry.vpath));
return ERRNO_SUCCESS;
},
fd_pwrite: (
fd: number,
iovs_ptr: number,
iovs_len: number,
offset: bigint,
nwritten_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const seek = entry.handle.seekSync(0, Deno.SeekMode.Current);
const view = new DataView(this.memory.buffer);
let nwritten = 0;
for (let i = 0; i < iovs_len; i++) {
const data_ptr = view.getUint32(iovs_ptr, true);
iovs_ptr += 4;
const data_len = view.getUint32(iovs_ptr, true);
iovs_ptr += 4;
const data = new Uint8Array(this.memory.buffer, data_ptr, data_len);
nwritten += entry.handle.writeSync(data);
}
entry.handle.seekSync(seek, Deno.SeekMode.Start);
view.setUint32(nwritten_out, nwritten, true);
return ERRNO_SUCCESS;
},
fd_read: (
fd: number,
iovs_ptr: number,
iovs_len: number,
nread_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const view = new DataView(this.memory.buffer);
let nread = 0;
for (let i = 0; i < iovs_len; i++) {
const data_ptr = view.getUint32(iovs_ptr, true);
iovs_ptr += 4;
const data_len = view.getUint32(iovs_ptr, true);
iovs_ptr += 4;
const data = new Uint8Array(this.memory.buffer, data_ptr, data_len);
nread += entry.handle.readSync(data);
}
view.setUint32(nread_out, nread, true);
return ERRNO_SUCCESS;
},
fd_readdir: (
fd: number,
buf_ptr: number,
buf_len: number,
cookie: bigint,
bufused_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const heap = new Uint8Array(this.memory.buffer);
const view = new DataView(this.memory.buffer);
let bufused = 0;
try {
const entries = Array.from(Deno.readDirSync(entry.path));
for (let i = Number(cookie); i < entries.length; i++) {
const name_data = new TextEncoder().encode(entries[i].name);
const entry_info = Deno.statSync(
resolve(entry.path, entries[i].name)
);
const entry_data = new Uint8Array(24 + name_data.byteLength);
const entry_view = new DataView(entry_data.buffer);
entry_view.setBigUint64(0, BigInt(i + 1), true);
entry_view.setBigUint64(
8,
BigInt(entry_info.ino ? entry_info.ino : 0),
true
);
entry_view.setUint32(16, name_data.byteLength, true);
switch (true) {
case entries[i].isFile:
var type = FILETYPE_REGULAR_FILE;
break;
case entries[i].isDirectory:
var type = FILETYPE_REGULAR_FILE;
break;
case entries[i].isSymlink:
var type = FILETYPE_SYMBOLIC_LINK;
break;
default:
var type = FILETYPE_REGULAR_FILE;
break;
}
entry_view.setUint8(20, type);
entry_data.set(name_data, 24);
const data = entry_data.slice(
0,
Math.min(entry_data.length, buf_len - bufused)
);
heap.set(data, buf_ptr + bufused);
bufused += data.byteLength;
}
} catch (err) {
return errno(err);
}
view.setUint32(bufused_out, bufused, true);
return ERRNO_SUCCESS;
},
fd_renumber: (fd: number, to: number): number => {
if (!this.fds[fd]) {
return ERRNO_BADF;
}
if (!this.fds[to]) {
return ERRNO_BADF;
}
this.fds[to].handle.close();
this.fds[to] = this.fds[fd];
delete this.fds[fd];
return ERRNO_SUCCESS;
},
fd_seek: (
fd: number,
offset: bigint,
whence: number,
newoffset_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const view = new DataView(this.memory.buffer);
try {
// FIXME Deno does not support seeking with big integers
const newoffset = entry.handle.seekSync(Number(offset), whence);
view.setBigUint64(newoffset_out, BigInt(newoffset), true);
} catch (err) {
return ERRNO_INVAL;
}
return ERRNO_SUCCESS;
},
fd_sync: (fd: number): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
try {
Deno.fsyncSync(entry.handle.rid);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
fd_tell: (fd: number, offset_out: number): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const view = new DataView(this.memory.buffer);
try {
const offset = entry.handle.seekSync(0, Deno.SeekMode.Current);
view.setBigUint64(offset_out, offset, true);
} catch (err) {
return ERRNO_INVAL;
}
return ERRNO_SUCCESS;
},
fd_write: (
fd: number,
iovs_ptr: number,
iovs_len: number,
nwritten_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
const view = new DataView(this.memory.buffer);
let nwritten = 0;
for (let i = 0; i < iovs_len; i++) {
const data_ptr = view.getUint32(iovs_ptr, true);
iovs_ptr += 4;
const data_len = view.getUint32(iovs_ptr, true);
iovs_ptr += 4;
nwritten += entry.handle.writeSync(
new Uint8Array(this.memory.buffer, data_ptr, data_len)
);
}
view.setUint32(nwritten_out, nwritten, true);
return ERRNO_SUCCESS;
},
path_create_directory: (
fd: number,
path_ptr: number,
path_len: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const data = new Uint8Array(this.memory.buffer, path_ptr, path_len);
const path = resolve(entry.path, text.decode(data));
try {
Deno.mkdirSync(path);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_filestat_get: (
fd: number,
flags: number,
path_ptr: number,
path_len: number,
buf_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const data = new Uint8Array(this.memory.buffer, path_ptr, path_len);
const path = resolve(entry.path, text.decode(data));
const view = new DataView(this.memory.buffer);
try {
const info =
(flags & LOOKUPFLAGS_SYMLINK_FOLLOW) != 0
? Deno.statSync(path)
: Deno.lstatSync(path);
view.setBigUint64(buf_out, BigInt(info.dev ? info.dev : 0), true);
buf_out += 8;
view.setBigUint64(buf_out, BigInt(info.ino ? info.ino : 0), true);
buf_out += 8;
switch (true) {
case info.isFile:
view.setUint8(buf_out, FILETYPE_REGULAR_FILE);
buf_out += 8;
break;
case info.isDirectory:
view.setUint8(buf_out, FILETYPE_DIRECTORY);
buf_out += 8;
break;
case info.isSymlink:
view.setUint8(buf_out, FILETYPE_SYMBOLIC_LINK);
buf_out += 8;
break;
default:
view.setUint8(buf_out, FILETYPE_UNKNOWN);
buf_out += 8;
break;
}
view.setUint32(buf_out, Number(info.nlink), true);
buf_out += 8;
view.setBigUint64(buf_out, BigInt(info.size), true);
buf_out += 8;
view.setBigUint64(
buf_out,
BigInt(info.atime ? info.atime.getTime() * 1e6 : 0),
true
);
buf_out += 8;
view.setBigUint64(
buf_out,
BigInt(info.mtime ? info.mtime.getTime() * 1e6 : 0),
true
);
buf_out += 8;
view.setBigUint64(
buf_out,
BigInt(info.birthtime ? info.birthtime.getTime() * 1e6 : 0),
true
);
buf_out += 8;
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_filestat_set_times: (
fd: number,
flags: number,
path_ptr: number,
path_len: number,
atim: bigint,
mtim: bigint,
fst_flags: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const data = new Uint8Array(this.memory.buffer, path_ptr, path_len);
const path = resolve(entry.path, text.decode(data));
if ((fst_flags & FSTFLAGS_ATIM_NOW) == FSTFLAGS_ATIM_NOW) {
atim = BigInt(Date.now()) * BigInt(1e6);
}
if ((fst_flags & FSTFLAGS_MTIM_NOW) == FSTFLAGS_MTIM_NOW) {
mtim = BigInt(Date.now()) * BigInt(1e6);
}
try {
Deno.utimeSync(path, Number(atim), Number(mtim));
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_link: (
old_fd: number,
old_flags: number,
old_path_ptr: number,
old_path_len: number,
new_fd: number,
new_path_ptr: number,
new_path_len: number
): number => {
const old_entry = this.fds[old_fd];
const new_entry = this.fds[new_fd];
if (!old_entry || !new_entry) {
return ERRNO_BADF;
}
if (!old_entry.path || !new_entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const old_data = new Uint8Array(
this.memory.buffer,
old_path_ptr,
old_path_len
);
const old_path = resolve(old_entry.path, text.decode(old_data));
const new_data = new Uint8Array(
this.memory.buffer,
new_path_ptr,
new_path_len
);
const new_path = resolve(new_entry.path, text.decode(new_data));
try {
Deno.linkSync(old_path, new_path);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_open: (
fd: number,
dirflags: number,
path_ptr: number,
path_len: number,
oflags: number,
fs_rights_base: number | bigint,
fs_rights_inherting: number | bigint,
fdflags: number,
opened_fd_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const data = new Uint8Array(this.memory.buffer, path_ptr, path_len);
const path = resolve(entry.path, text.decode(data));
if ((oflags & OFLAGS_DIRECTORY) !== 0) {
// XXX (caspervonb) this isn't ideal as we can't get a rid for the
// directory this way so there's no native fstat but Deno.open
// doesn't work with directories on windows so we'll have to work
// around it for now.
try {
const entries = Array.from(Deno.readDirSync(path));
const opened_fd =
this.fds.push({
entries,
path,
}) - 1;
const view = new DataView(this.memory.buffer);
view.setUint32(opened_fd_out, opened_fd, true);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
}
const options = {
read: false,
write: false,
append: false,
truncate: false,
create: false,
createNew: false,
};
if ((oflags & OFLAGS_CREAT) !== 0) {
options.create = true;
options.write = true;
}
if ((oflags & OFLAGS_EXCL) !== 0) {
options.createNew = true;
}
if ((oflags & OFLAGS_TRUNC) !== 0) {
options.truncate = true;
options.write = true;
}
if (
(BigInt(fs_rights_base) &
BigInt(RIGHTS_FD_READ | RIGHTS_FD_READDIR)) !=
0n
) {
options.read = true;
}
if (
(BigInt(fs_rights_base) &
BigInt(
RIGHTS_FD_DATASYNC |
RIGHTS_FD_WRITE |
RIGHTS_FD_ALLOCATE |
RIGHTS_FD_FILESTAT_SET_SIZE
)) !=
0n
) {
options.write = true;
}
if ((fdflags & FDFLAGS_APPEND) != 0) {
options.append = true;
}
if ((fdflags & FDFLAGS_DSYNC) != 0) {
// TODO (caspervonb) review if we can emulate this.
}
if ((fdflags & FDFLAGS_NONBLOCK) != 0) {
// TODO (caspervonb) review if we can emulate this.
}
if ((fdflags & FDFLAGS_RSYNC) != 0) {
// TODO (caspervonb) review if we can emulate this.
}
if ((fdflags & FDFLAGS_SYNC) != 0) {
// TODO (caspervonb) review if we can emulate this.
}
if (!options.read && !options.write && !options.truncate) {
options.read = true;
}
try {
const handle = Deno.openSync(path, options);
const opened_fd =
this.fds.push({
handle,
path,
}) - 1;
const view = new DataView(this.memory.buffer);
view.setUint32(opened_fd_out, opened_fd, true);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_readlink: (
fd: number,
path_ptr: number,
path_len: number,
buf_ptr: number,
buf_len: number,
bufused_out: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
const view = new DataView(this.memory.buffer);
const heap = new Uint8Array(this.memory.buffer);
const data = new Uint8Array(this.memory.buffer, path_ptr, path_len);
const path = resolve(entry.path, new TextDecoder().decode(data));
try {
const link = Deno.readLinkSync(path);
const data = new TextEncoder().encode(link);
heap.set(new Uint8Array(data, 0, buf_len), buf_ptr);
const bufused = Math.min(data.byteLength, buf_len);
view.setUint32(bufused_out, bufused, true);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_remove_directory: (
fd: number,
path_ptr: number,
path_len: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const data = new Uint8Array(this.memory.buffer, path_ptr, path_len);
const path = resolve(entry.path, text.decode(data));
try {
if (!Deno.statSync(path).isDirectory) {
return ERRNO_NOTDIR;
}
Deno.removeSync(path);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_rename: (
fd: number,
old_path_ptr: number,
old_path_len: number,
new_fd: number,
new_path_ptr: number,
new_path_len: number
): number => {
const old_entry = this.fds[fd];
const new_entry = this.fds[new_fd];
if (!old_entry || !new_entry) {
return ERRNO_BADF;
}
if (!old_entry.path || !new_entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const old_data = new Uint8Array(
this.memory.buffer,
old_path_ptr,
old_path_len
);
const old_path = resolve(old_entry.path, text.decode(old_data));
const new_data = new Uint8Array(
this.memory.buffer,
new_path_ptr,
new_path_len
);
const new_path = resolve(new_entry.path, text.decode(new_data));
try {
Deno.renameSync(old_path, new_path);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_symlink: (
old_path_ptr: number,
old_path_len: number,
fd: number,
new_path_ptr: number,
new_path_len: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const old_data = new Uint8Array(
this.memory.buffer,
old_path_ptr,
old_path_len
);
const old_path = text.decode(old_data);
const new_data = new Uint8Array(
this.memory.buffer,
new_path_ptr,
new_path_len
);
const new_path = resolve(entry.path, text.decode(new_data));
try {
Deno.symlinkSync(old_path, new_path);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
path_unlink_file: (
fd: number,
path_ptr: number,
path_len: number
): number => {
const entry = this.fds[fd];
if (!entry) {
return ERRNO_BADF;
}
if (!entry.path) {
return ERRNO_INVAL;
}
const text = new TextDecoder();
const data = new Uint8Array(this.memory.buffer, path_ptr, path_len);
const path = resolve(entry.path, text.decode(data));
try {
Deno.removeSync(path);
} catch (err) {
return errno(err);
}
return ERRNO_SUCCESS;
},
poll_oneoff: (
in_ptr: number,
out_ptr: number,
nsubscriptions: number,
nevents_out: number
): number => {
return ERRNO_NOSYS;
},
proc_exit: (rval: number): never => {
Deno.exit(rval);
},
proc_raise: (sig: number): number => {
return ERRNO_NOSYS;
},
sched_yield: (): number => {
return ERRNO_SUCCESS;
},
random_get: (buf_ptr: number, buf_len: number): number => {
const buffer = new Uint8Array(this.memory.buffer, buf_ptr, buf_len);
crypto.getRandomValues(buffer);
return ERRNO_SUCCESS;
},
sock_recv: (
fd: number,
ri_data_ptr: number,
ri_data_len: number,
ri_flags: number,
ro_datalen_out: number,
ro_flags_out: number
): number => {
return ERRNO_NOSYS;
},
sock_send: (
fd: number,
si_data_ptr: number,
si_data_len: number,
si_flags: number,
so_datalen_out: number
): number => {
return ERRNO_NOSYS;
},
sock_shutdown: (fd: number, how: number): number => {
return ERRNO_NOSYS;
},
};
}
}