@wapython/wasi
Version:
Javascript library for interacting with WASI Modules in Node.js and the Browser.
1,050 lines (1,049 loc) • 64.3 kB
JavaScript
"use strict";
/* MIT licensed. See README.md for copyright and history information. */
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var debug_1 = __importDefault(require("debug"));
var log = (0, debug_1.default)("wasi");
var logOpen = (0, debug_1.default)("wasi:open"); // just log opening files, which is useful
var types_1 = require("./types");
var typedarray_to_buffer_1 = __importDefault(require("typedarray-to-buffer"));
var constants_1 = require("./constants");
var STDIN_DEFAULT_RIGHTS = constants_1.WASI_RIGHT_FD_DATASYNC |
constants_1.WASI_RIGHT_FD_READ |
constants_1.WASI_RIGHT_FD_SYNC |
constants_1.WASI_RIGHT_FD_ADVISE |
constants_1.WASI_RIGHT_FD_FILESTAT_GET |
constants_1.WASI_RIGHT_POLL_FD_READWRITE;
var STDOUT_DEFAULT_RIGHTS = constants_1.WASI_RIGHT_FD_DATASYNC |
constants_1.WASI_RIGHT_FD_WRITE |
constants_1.WASI_RIGHT_FD_SYNC |
constants_1.WASI_RIGHT_FD_ADVISE |
constants_1.WASI_RIGHT_FD_FILESTAT_GET |
constants_1.WASI_RIGHT_POLL_FD_READWRITE;
var STDERR_DEFAULT_RIGHTS = STDOUT_DEFAULT_RIGHTS;
var msToNs = function (ms) {
var msInt = Math.trunc(ms);
var decimal = BigInt(Math.round((ms - msInt) * 1000000));
var ns = BigInt(msInt) * BigInt(1000000);
return ns + decimal;
};
var nsToMs = function (ns) {
if (typeof ns === "number") {
ns = Math.trunc(ns);
}
var nsInt = BigInt(ns);
return Number(nsInt / BigInt(1000000));
};
var wrap = function (f) {
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
try {
return f.apply(void 0, __spreadArray([], __read(args), false));
}
catch (err) {
// log("WASI error", err);
var e = err;
// If it's an error from the fs
if ((e === null || e === void 0 ? void 0 : e.code) && typeof (e === null || e === void 0 ? void 0 : e.code) === "string") {
return constants_1.ERROR_MAP[e.code] || constants_1.WASI_EINVAL;
}
// If it's a WASI error, we return it directly
if (e instanceof types_1.WASIError) {
return e.errno;
}
// Otherwise we let the error bubble up
throw e;
}
};
};
var stat = function (wasi, fd) {
var entry = wasi.FD_MAP.get(fd);
// console.log("stat", { fd, entry, FD_MAP: wasi.FD_MAP });
// log("stat", { fd, entry, FD_MAP: wasi.FD_MAP });
if (!entry) {
throw new types_1.WASIError(constants_1.WASI_EBADF);
}
if (entry.filetype === undefined) {
var stats = wasi.bindings.fs.fstatSync(entry.real);
var _a = translateFileAttributes(wasi, fd, stats), filetype = _a.filetype, rightsBase = _a.rightsBase, rightsInheriting = _a.rightsInheriting;
entry.filetype = filetype;
if (!entry.rights) {
entry.rights = {
base: rightsBase,
inheriting: rightsInheriting,
};
}
}
return entry;
};
var translateFileAttributes = function (wasi, fd, stats) {
switch (true) {
case stats.isBlockDevice():
return {
filetype: constants_1.WASI_FILETYPE_BLOCK_DEVICE,
rightsBase: constants_1.RIGHTS_BLOCK_DEVICE_BASE,
rightsInheriting: constants_1.RIGHTS_BLOCK_DEVICE_INHERITING,
};
case stats.isCharacterDevice(): {
var filetype = constants_1.WASI_FILETYPE_CHARACTER_DEVICE;
if (fd !== undefined && wasi.bindings.isTTY(fd)) {
return {
filetype: filetype,
rightsBase: constants_1.RIGHTS_TTY_BASE,
rightsInheriting: constants_1.RIGHTS_TTY_INHERITING,
};
}
return {
filetype: filetype,
rightsBase: constants_1.RIGHTS_CHARACTER_DEVICE_BASE,
rightsInheriting: constants_1.RIGHTS_CHARACTER_DEVICE_INHERITING,
};
}
case stats.isDirectory():
return {
filetype: constants_1.WASI_FILETYPE_DIRECTORY,
rightsBase: constants_1.RIGHTS_DIRECTORY_BASE,
rightsInheriting: constants_1.RIGHTS_DIRECTORY_INHERITING,
};
case stats.isFIFO():
return {
filetype: constants_1.WASI_FILETYPE_SOCKET_STREAM,
rightsBase: constants_1.RIGHTS_SOCKET_BASE,
rightsInheriting: constants_1.RIGHTS_SOCKET_INHERITING,
};
case stats.isFile():
return {
filetype: constants_1.WASI_FILETYPE_REGULAR_FILE,
rightsBase: constants_1.RIGHTS_REGULAR_FILE_BASE,
rightsInheriting: constants_1.RIGHTS_REGULAR_FILE_INHERITING,
};
case stats.isSocket():
return {
filetype: constants_1.WASI_FILETYPE_SOCKET_STREAM,
rightsBase: constants_1.RIGHTS_SOCKET_BASE,
rightsInheriting: constants_1.RIGHTS_SOCKET_INHERITING,
};
case stats.isSymbolicLink():
return {
filetype: constants_1.WASI_FILETYPE_SYMBOLIC_LINK,
rightsBase: BigInt(0),
rightsInheriting: BigInt(0),
};
default:
return {
filetype: constants_1.WASI_FILETYPE_UNKNOWN,
rightsBase: BigInt(0),
rightsInheriting: BigInt(0),
};
}
};
// const logToFile = (...args) => {
// require("fs").appendFileSync(
// "/tmp/wasi.log",
// args.map((x) => `${x}`).join(" ") + "\n"
// );
// };
var WASI = /** @class */ (function () {
function WASI(wasiConfig) {
var e_1, _a;
var _this = this;
this.spinLock = wasiConfig.spinLock;
this.waitForStdin = wasiConfig.waitForStdin;
this.sendStdout = wasiConfig.sendStdout;
this.sendStderr = wasiConfig.sendStderr;
// Destructure our wasiConfig
var preopens = {};
if (wasiConfig.preopens) {
preopens = wasiConfig.preopens;
}
var env = {};
if (wasiConfig && wasiConfig.env) {
env = wasiConfig.env;
}
var args = [];
if (wasiConfig && wasiConfig.args) {
args = wasiConfig.args;
}
var bindings = wasiConfig.bindings;
// @ts-ignore
this.memory = undefined;
// @ts-ignore
this.view = undefined;
this.bindings = bindings;
this.FD_MAP = new Map([
[
constants_1.WASI_STDIN_FILENO,
{
real: 0,
filetype: constants_1.WASI_FILETYPE_CHARACTER_DEVICE,
// offset: BigInt(0),
rights: {
base: STDIN_DEFAULT_RIGHTS,
inheriting: BigInt(0),
},
path: "/dev/stdin",
},
],
[
constants_1.WASI_STDOUT_FILENO,
{
real: 1,
filetype: constants_1.WASI_FILETYPE_CHARACTER_DEVICE,
// offset: BigInt(0),
rights: {
base: STDOUT_DEFAULT_RIGHTS,
inheriting: BigInt(0),
},
path: "/dev/stdout",
},
],
[
constants_1.WASI_STDERR_FILENO,
{
real: 2,
filetype: constants_1.WASI_FILETYPE_CHARACTER_DEVICE,
// offset: BigInt(0),
rights: {
base: STDERR_DEFAULT_RIGHTS,
inheriting: BigInt(0),
},
path: "/dev/stderr",
},
],
]);
var fs = this.bindings.fs;
var path = this.bindings.path;
try {
for (var _b = __values(Object.entries(preopens)), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = __read(_c.value, 2), k = _d[0], v = _d[1];
var real = fs.openSync(v, fs.constants.O_RDONLY);
var newfd = this.getUnusedFileDescriptor();
this.FD_MAP.set(newfd, {
real: real,
filetype: constants_1.WASI_FILETYPE_DIRECTORY,
// offset: BigInt(0),
rights: {
base: constants_1.RIGHTS_DIRECTORY_BASE,
inheriting: constants_1.RIGHTS_DIRECTORY_INHERITING,
},
fakePath: k,
path: v,
});
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
var getiovs = function (iovs, iovsLen) {
// iovs* -> [iov, iov, ...]
// __wasi_ciovec_t {
// void* buf,
// size_t buf_len,
// }
_this.refreshMemory();
var buffers = Array.from({ length: iovsLen }, function (_, i) {
var ptr = iovs + i * 8;
var buf = _this.view.getUint32(ptr, true);
var bufLen = _this.view.getUint32(ptr + 4, true);
var buffer = new Uint8Array(_this.memory.buffer, buf, bufLen);
return (0, typedarray_to_buffer_1.default)(buffer);
});
return buffers;
};
var CHECK_FD = function (fd, rights) {
// log("CHECK_FD", { fd, rights });
var stats = stat(_this, fd);
// log("CHECK_FD", { stats });
if (rights !== BigInt(0) && (stats.rights.base & rights) === BigInt(0)) {
throw new types_1.WASIError(constants_1.WASI_EPERM);
}
return stats;
};
var CPUTIME_START = bindings.hrtime();
var now = function (clockId) {
switch (clockId) {
case constants_1.WASI_CLOCK_MONOTONIC:
return bindings.hrtime();
case constants_1.WASI_CLOCK_REALTIME:
return msToNs(Date.now());
case constants_1.WASI_CLOCK_PROCESS_CPUTIME_ID:
case constants_1.WASI_CLOCK_THREAD_CPUTIME_ID: // TODO -- this assumes 1 thread
return bindings.hrtime() - CPUTIME_START;
default:
return null;
}
};
this.wasiImport = {
args_get: function (argv, argvBuf) {
_this.refreshMemory();
var coffset = argv;
var offset = argvBuf;
args.forEach(function (a) {
_this.view.setUint32(coffset, offset, true);
coffset += 4;
offset += Buffer.from(_this.memory.buffer).write("".concat(a, "\0"), offset);
});
return constants_1.WASI_ESUCCESS;
},
args_sizes_get: function (argc, argvBufSize) {
_this.refreshMemory();
_this.view.setUint32(argc, args.length, true);
var size = args.reduce(function (acc, a) { return acc + Buffer.byteLength(a) + 1; }, 0);
_this.view.setUint32(argvBufSize, size, true);
return constants_1.WASI_ESUCCESS;
},
environ_get: function (environ, environBuf) {
_this.refreshMemory();
var coffset = environ;
var offset = environBuf;
Object.entries(env).forEach(function (_a) {
var _b = __read(_a, 2), key = _b[0], value = _b[1];
_this.view.setUint32(coffset, offset, true);
coffset += 4;
offset += Buffer.from(_this.memory.buffer).write("".concat(key, "=").concat(value, "\0"), offset);
});
return constants_1.WASI_ESUCCESS;
},
environ_sizes_get: function (environCount, environBufSize) {
_this.refreshMemory();
var envProcessed = Object.entries(env).map(function (_a) {
var _b = __read(_a, 2), key = _b[0], value = _b[1];
return "".concat(key, "=").concat(value, "\0");
});
var size = envProcessed.reduce(function (acc, e) { return acc + Buffer.byteLength(e); }, 0);
_this.view.setUint32(environCount, envProcessed.length, true);
_this.view.setUint32(environBufSize, size, true);
return constants_1.WASI_ESUCCESS;
},
clock_res_get: function (clockId, resolution) {
var res;
switch (clockId) {
case constants_1.WASI_CLOCK_MONOTONIC:
case constants_1.WASI_CLOCK_PROCESS_CPUTIME_ID:
case constants_1.WASI_CLOCK_THREAD_CPUTIME_ID: {
res = BigInt(1);
break;
}
case constants_1.WASI_CLOCK_REALTIME: {
res = BigInt(1000);
break;
}
}
if (!res) {
throw Error("invalid clockId");
}
_this.view.setBigUint64(resolution, res);
return constants_1.WASI_ESUCCESS;
},
clock_time_get: function (clockId, _precision, time) {
_this.refreshMemory();
var n = now(clockId);
if (n === null) {
return constants_1.WASI_EINVAL;
}
_this.view.setBigUint64(time, BigInt(n), true);
return constants_1.WASI_ESUCCESS;
},
fd_advise: wrap(function (fd, _offset, _len, _advice) {
CHECK_FD(fd, constants_1.WASI_RIGHT_FD_ADVISE);
return constants_1.WASI_ENOSYS;
}),
fd_allocate: wrap(function (fd, _offset, _len) {
CHECK_FD(fd, constants_1.WASI_RIGHT_FD_ALLOCATE);
return constants_1.WASI_ENOSYS;
}),
fd_close: wrap(function (fd) {
var stats = CHECK_FD(fd, BigInt(0));
fs.closeSync(stats.real);
_this.FD_MAP.delete(fd);
return constants_1.WASI_ESUCCESS;
}),
fd_datasync: wrap(function (fd) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_DATASYNC);
fs.fdatasyncSync(stats.real);
return constants_1.WASI_ESUCCESS;
}),
fd_fdstat_get: wrap(function (fd, bufPtr) {
var stats = CHECK_FD(fd, BigInt(0));
_this.refreshMemory();
if (stats.filetype == null) {
throw Error("stats.filetype must be set");
}
_this.view.setUint8(bufPtr, stats.filetype); // FILETYPE u8
_this.view.setUint16(bufPtr + 2, 0, true); // FDFLAG u16
_this.view.setUint16(bufPtr + 4, 0, true); // FDFLAG u16
_this.view.setBigUint64(bufPtr + 8, BigInt(stats.rights.base), true); // u64
_this.view.setBigUint64(bufPtr + 8 + 8, BigInt(stats.rights.inheriting), true); // u64
return constants_1.WASI_ESUCCESS;
}),
fd_fdstat_set_flags: wrap(function (fd, _flags) {
CHECK_FD(fd, constants_1.WASI_RIGHT_FD_FDSTAT_SET_FLAGS);
return constants_1.WASI_ENOSYS;
}),
fd_fdstat_set_rights: wrap(function (fd, fsRightsBase, fsRightsInheriting) {
var stats = CHECK_FD(fd, BigInt(0));
var nrb = stats.rights.base | fsRightsBase;
if (nrb > stats.rights.base) {
return constants_1.WASI_EPERM;
}
var nri = stats.rights.inheriting | fsRightsInheriting;
if (nri > stats.rights.inheriting) {
return constants_1.WASI_EPERM;
}
stats.rights.base = fsRightsBase;
stats.rights.inheriting = fsRightsInheriting;
return constants_1.WASI_ESUCCESS;
}),
fd_filestat_get: wrap(function (fd, bufPtr) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_FILESTAT_GET);
var rstats = fs.fstatSync(stats.real);
_this.refreshMemory();
_this.view.setBigUint64(bufPtr, BigInt(rstats.dev), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, BigInt(rstats.ino), true);
bufPtr += 8;
if (stats.filetype == null) {
throw Error("stats.filetype must be set");
}
_this.view.setUint8(bufPtr, stats.filetype);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, BigInt(rstats.nlink), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, BigInt(rstats.size), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, msToNs(rstats.atimeMs), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, msToNs(rstats.mtimeMs), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, msToNs(rstats.ctimeMs), true);
return constants_1.WASI_ESUCCESS;
}),
fd_filestat_set_size: wrap(function (fd, stSize) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_FILESTAT_SET_SIZE);
fs.ftruncateSync(stats.real, Number(stSize));
return constants_1.WASI_ESUCCESS;
}),
fd_filestat_set_times: wrap(function (fd, stAtim, stMtim, fstflags) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_FILESTAT_SET_TIMES);
var rstats = fs.fstatSync(stats.real);
var atim = rstats.atime;
var mtim = rstats.mtime;
var n = nsToMs(now(constants_1.WASI_CLOCK_REALTIME));
var atimflags = constants_1.WASI_FILESTAT_SET_ATIM | constants_1.WASI_FILESTAT_SET_ATIM_NOW;
if ((fstflags & atimflags) === atimflags) {
return constants_1.WASI_EINVAL;
}
var mtimflags = constants_1.WASI_FILESTAT_SET_MTIM | constants_1.WASI_FILESTAT_SET_MTIM_NOW;
if ((fstflags & mtimflags) === mtimflags) {
return constants_1.WASI_EINVAL;
}
if ((fstflags & constants_1.WASI_FILESTAT_SET_ATIM) === constants_1.WASI_FILESTAT_SET_ATIM) {
atim = nsToMs(stAtim);
}
else if ((fstflags & constants_1.WASI_FILESTAT_SET_ATIM_NOW) ===
constants_1.WASI_FILESTAT_SET_ATIM_NOW) {
atim = n;
}
if ((fstflags & constants_1.WASI_FILESTAT_SET_MTIM) === constants_1.WASI_FILESTAT_SET_MTIM) {
mtim = nsToMs(stMtim);
}
else if ((fstflags & constants_1.WASI_FILESTAT_SET_MTIM_NOW) ===
constants_1.WASI_FILESTAT_SET_MTIM_NOW) {
mtim = n;
}
fs.futimesSync(stats.real, new Date(atim), new Date(mtim));
return constants_1.WASI_ESUCCESS;
}),
fd_prestat_get: wrap(function (fd, bufPtr) {
var _a;
var stats = CHECK_FD(fd, BigInt(0));
// log("fd_prestat_get", { fd, stats });
if (!stats.path) {
return constants_1.WASI_EINVAL;
}
_this.refreshMemory();
_this.view.setUint8(bufPtr, constants_1.WASI_PREOPENTYPE_DIR);
_this.view.setUint32(bufPtr + 4,
// TODO: this is definitely completely wrong unless preopens=/.
Buffer.byteLength((_a = stats.fakePath) !== null && _a !== void 0 ? _a : stats.path), true);
return constants_1.WASI_ESUCCESS;
}),
fd_prestat_dir_name: wrap(function (fd, pathPtr, pathLen) {
var _a;
var stats = CHECK_FD(fd, BigInt(0));
if (!stats.path) {
return constants_1.WASI_EINVAL;
}
_this.refreshMemory();
Buffer.from(_this.memory.buffer).write((_a = stats.fakePath) !== null && _a !== void 0 ? _a : stats.path /* TODO: wrong in general! */, pathPtr, pathLen, "utf8");
return constants_1.WASI_ESUCCESS;
}),
fd_pwrite: wrap(function (fd, iovs, iovsLen, offset, nwritten) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_WRITE | constants_1.WASI_RIGHT_FD_SEEK);
var written = 0;
getiovs(iovs, iovsLen).forEach(function (iov) {
var w = 0;
while (w < iov.byteLength) {
w += fs.writeSync(stats.real, iov, w, iov.byteLength - w, Number(offset) + written + w);
}
written += w;
});
_this.view.setUint32(nwritten, written, true);
return constants_1.WASI_ESUCCESS;
}),
fd_write: wrap(function (fd, iovs, iovsLen, nwritten) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_WRITE);
var IS_STDOUT = stats.real == 1;
var IS_STDERR = stats.real == 2;
var written = 0;
getiovs(iovs, iovsLen).forEach(function (iov) {
if (iov.byteLength == 0)
return;
// log(
// `writing to fd=${fd}: `,
// JSON.stringify(new TextDecoder().decode(iov)),
// JSON.stringify(iov)
// );
if (IS_STDOUT && _this.sendStdout != null) {
_this.sendStdout(iov);
written += iov.byteLength;
}
else if (IS_STDERR && _this.sendStderr != null) {
_this.sendStderr(iov);
written += iov.byteLength;
}
else {
// useful to be absolutely sure if wasi is writing something:
// log(`write "${new TextDecoder().decode(iov)}" to ${fd})`);
var w = 0;
while (w < iov.byteLength) {
// log(`write ${iov.byteLength} bytes to fd=${stats.real}`);
var i = fs.writeSync(stats.real, iov, w, iov.byteLength - w, stats.offset ? Number(stats.offset) : null);
// log(`just wrote i=${i} bytes`);
if (stats.offset)
stats.offset += BigInt(i);
w += i;
}
written += w;
}
});
_this.view.setUint32(nwritten, written, true);
return constants_1.WASI_ESUCCESS;
}),
fd_pread: wrap(function (fd, iovs, iovsLen, offset, nread) {
var e_2, _a;
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_READ | constants_1.WASI_RIGHT_FD_SEEK);
var read = 0;
try {
outer: for (var _b = __values(getiovs(iovs, iovsLen)), _c = _b.next(); !_c.done; _c = _b.next()) {
var iov = _c.value;
var r = 0;
while (r < iov.byteLength) {
var length_1 = iov.byteLength - r;
var rr = fs.readSync(stats.real, iov, r, iov.byteLength - r, Number(offset) + read + r);
r += rr;
read += rr;
// If we don't read anything, or we receive less than requested
if (rr === 0 || rr < length_1) {
break outer;
}
}
read += r;
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
_this.view.setUint32(nread, read, true);
return constants_1.WASI_ESUCCESS;
}),
fd_read: wrap(function (fd, iovs, iovsLen, nread) {
var e_3, _a;
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_READ);
var IS_STDIN = stats.real === 0;
var read = 0;
try {
// logToFile(
// `fd_read: ${IS_STDIN}, ${
// this.stdinBuffer?.length
// } ${this.stdinBuffer?.toString()}`
// );
// log("stat", { fd, entry, FD_MAP: wasi.FD_MAP });
outer: for (var _b = __values(getiovs(iovs, iovsLen)), _c = _b.next(); !_c.done; _c = _b.next()) {
var iov = _c.value;
var r = 0;
while (r < iov.byteLength) {
var length_2 = iov.byteLength - r;
var position = IS_STDIN || stats.offset === undefined
? null
: Number(stats.offset);
var rr = 0;
if (IS_STDIN && _this.waitForStdin != null) {
if (_this.stdinBuffer != null) {
// just got stdin after waiting for it in poll_oneoff
// TODO: Do we need to limit length or iov will overflow?
// Or will the below just work fine? It might.
// Second remark -- we do not do anything special here to try to
// handle seeing EOF (ctrl+d) in the stream. No matter what I try,
// doing something here (e.g., returning 0 bytes read) doesn't
// properly work with libedit. So we leave it alone and let
// our slightly patched libedit handle control+d.
rr = _this.stdinBuffer.copy(iov);
if (rr == _this.stdinBuffer.length) {
_this.stdinBuffer = undefined;
}
else {
_this.stdinBuffer = _this.stdinBuffer.slice(rr);
}
}
}
else {
// console.log("fs.readSync", {fd:stats.real, iov,r,length, position});
rr = fs.readSync(stats.real, // fd
iov, // buffer
r, // offset
length_2, // length
position // position
);
}
// TODO: I'm not sure which type of files should have an offset yet.
// E.g., obviously a regular file should and obviously stdin (a character
// device) and a pipe (which has type WASI_FILETYPE_SOCKET_STREAM) does not.
if (stats.filetype == constants_1.WASI_FILETYPE_REGULAR_FILE) {
stats.offset =
(stats.offset ? stats.offset : BigInt(0)) + BigInt(rr);
}
r += rr;
read += rr;
// If we don't read anything, or we receive less than requested
if (rr === 0 || rr < length_2) {
break outer;
}
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_3) throw e_3.error; }
}
_this.view.setUint32(nread, read, true);
return constants_1.WASI_ESUCCESS;
}),
fd_readdir: wrap(function (fd, bufPtr, bufLen, cookie, bufusedPtr) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_READDIR);
// log("fd_readdir got stats = ", stats);
_this.refreshMemory();
var entries = fs.readdirSync(stats.path, { withFileTypes: true });
var startPtr = bufPtr;
for (var i = Number(cookie); i < entries.length; i += 1) {
var entry = entries[i];
var nameLength = Buffer.byteLength(entry.name);
if (bufPtr - startPtr > bufLen) {
break;
}
_this.view.setBigUint64(bufPtr, BigInt(i + 1), true);
bufPtr += 8;
if (bufPtr - startPtr > bufLen) {
break;
}
// We use lstat instead of stat, since stat fails on broken links.
// Also, stat resolves the link giving the wrong inode! On the other
// hand, lstat works fine on non-links. This is wrong in upstream,
// which breaks testing test_compileall.py in the python test suite,
// due to doing os.scandir on a directory that contains a broken link.
var rstats = fs.lstatSync(path.resolve(stats.path, entry.name));
_this.view.setBigUint64(bufPtr, BigInt(rstats.ino), true);
bufPtr += 8;
if (bufPtr - startPtr > bufLen) {
break;
}
_this.view.setUint32(bufPtr, nameLength, true);
bufPtr += 4;
if (bufPtr - startPtr > bufLen) {
break;
}
var filetype = void 0;
switch (true) {
case rstats.isBlockDevice():
filetype = constants_1.WASI_FILETYPE_BLOCK_DEVICE;
break;
case rstats.isCharacterDevice():
filetype = constants_1.WASI_FILETYPE_CHARACTER_DEVICE;
break;
case rstats.isDirectory():
filetype = constants_1.WASI_FILETYPE_DIRECTORY;
break;
case rstats.isFIFO():
filetype = constants_1.WASI_FILETYPE_SOCKET_STREAM;
break;
case rstats.isFile():
filetype = constants_1.WASI_FILETYPE_REGULAR_FILE;
break;
case rstats.isSocket():
filetype = constants_1.WASI_FILETYPE_SOCKET_STREAM;
break;
case rstats.isSymbolicLink():
filetype = constants_1.WASI_FILETYPE_SYMBOLIC_LINK;
break;
default:
filetype = constants_1.WASI_FILETYPE_UNKNOWN;
break;
}
_this.view.setUint8(bufPtr, filetype);
bufPtr += 1;
bufPtr += 3; // padding
if (bufPtr + nameLength >= startPtr + bufLen) {
// It doesn't fit in the buffer
break;
}
var memory_buffer = Buffer.from(_this.memory.buffer);
memory_buffer.write(entry.name, bufPtr);
bufPtr += nameLength;
}
var bufused = bufPtr - startPtr;
_this.view.setUint32(bufusedPtr, Math.min(bufused, bufLen), true);
return constants_1.WASI_ESUCCESS;
}),
fd_renumber: wrap(function (from, to) {
CHECK_FD(from, BigInt(0));
CHECK_FD(to, BigInt(0));
fs.closeSync(_this.FD_MAP.get(from).real);
_this.FD_MAP.set(from, _this.FD_MAP.get(to));
_this.FD_MAP.delete(to);
return constants_1.WASI_ESUCCESS;
}),
fd_seek: wrap(function (fd, offset, whence, newOffsetPtr) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_SEEK);
_this.refreshMemory();
switch (whence) {
case constants_1.WASI_WHENCE_CUR:
stats.offset =
(stats.offset ? stats.offset : BigInt(0)) + BigInt(offset);
break;
case constants_1.WASI_WHENCE_END:
var size = fs.fstatSync(stats.real).size;
stats.offset = BigInt(size) + BigInt(offset);
break;
case constants_1.WASI_WHENCE_SET:
stats.offset = BigInt(offset);
break;
}
if (stats.offset == null) {
throw Error("stats.offset must be defined");
}
_this.view.setBigUint64(newOffsetPtr, stats.offset, true);
return constants_1.WASI_ESUCCESS;
}),
fd_tell: wrap(function (fd, offsetPtr) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_TELL);
_this.refreshMemory();
if (!stats.offset) {
stats.offset = BigInt(0);
}
_this.view.setBigUint64(offsetPtr, stats.offset, true);
return constants_1.WASI_ESUCCESS;
}),
fd_sync: wrap(function (fd) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_FD_SYNC);
fs.fsyncSync(stats.real);
return constants_1.WASI_ESUCCESS;
}),
path_create_directory: wrap(function (fd, pathPtr, pathLen) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_PATH_CREATE_DIRECTORY);
if (!stats.path) {
return constants_1.WASI_EINVAL;
}
_this.refreshMemory();
var p = Buffer.from(_this.memory.buffer, pathPtr, pathLen).toString();
fs.mkdirSync(path.resolve(stats.path, p));
return constants_1.WASI_ESUCCESS;
}),
path_filestat_get: wrap(function (fd, flags, pathPtr, pathLen, bufPtr) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_PATH_FILESTAT_GET);
if (!stats.path) {
return constants_1.WASI_EINVAL;
}
_this.refreshMemory();
var p = Buffer.from(_this.memory.buffer, pathPtr, pathLen).toString();
var rstats;
if (flags) {
rstats = fs.statSync(path.resolve(stats.path, p));
}
else {
// there is exactly one flag implemented called "__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW";
// it's 1 and is used to follow links, i.e.,
// implement lstat -- this is ignored in upstream.
// See zig/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c
rstats = fs.lstatSync(path.resolve(stats.path, p));
}
_this.view.setBigUint64(bufPtr, BigInt(rstats.dev), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, BigInt(rstats.ino), true);
bufPtr += 8;
_this.view.setUint8(bufPtr, translateFileAttributes(_this, undefined, rstats).filetype);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, BigInt(rstats.nlink), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, BigInt(rstats.size), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, msToNs(rstats.atimeMs), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, msToNs(rstats.mtimeMs), true);
bufPtr += 8;
_this.view.setBigUint64(bufPtr, msToNs(rstats.ctimeMs), true);
return constants_1.WASI_ESUCCESS;
}),
path_filestat_set_times: wrap(function (fd, _dirflags, pathPtr, pathLen, stAtim, stMtim, fstflags) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_PATH_FILESTAT_SET_TIMES);
if (!stats.path) {
return constants_1.WASI_EINVAL;
}
_this.refreshMemory();
var rstats = fs.fstatSync(stats.real);
var atim = rstats.atime;
var mtim = rstats.mtime;
var n = nsToMs(now(constants_1.WASI_CLOCK_REALTIME));
var atimflags = constants_1.WASI_FILESTAT_SET_ATIM | constants_1.WASI_FILESTAT_SET_ATIM_NOW;
if ((fstflags & atimflags) === atimflags) {
return constants_1.WASI_EINVAL;
}
var mtimflags = constants_1.WASI_FILESTAT_SET_MTIM | constants_1.WASI_FILESTAT_SET_MTIM_NOW;
if ((fstflags & mtimflags) === mtimflags) {
return constants_1.WASI_EINVAL;
}
if ((fstflags & constants_1.WASI_FILESTAT_SET_ATIM) === constants_1.WASI_FILESTAT_SET_ATIM) {
atim = nsToMs(stAtim);
}
else if ((fstflags & constants_1.WASI_FILESTAT_SET_ATIM_NOW) ===
constants_1.WASI_FILESTAT_SET_ATIM_NOW) {
atim = n;
}
if ((fstflags & constants_1.WASI_FILESTAT_SET_MTIM) === constants_1.WASI_FILESTAT_SET_MTIM) {
mtim = nsToMs(stMtim);
}
else if ((fstflags & constants_1.WASI_FILESTAT_SET_MTIM_NOW) ===
constants_1.WASI_FILESTAT_SET_MTIM_NOW) {
mtim = n;
}
var p = Buffer.from(_this.memory.buffer, pathPtr, pathLen).toString();
fs.utimesSync(path.resolve(stats.path, p), new Date(atim), new Date(mtim));
return constants_1.WASI_ESUCCESS;
}),
path_link: wrap(function (oldFd, _oldFlags, oldPath, oldPathLen, newFd, newPath, newPathLen) {
var ostats = CHECK_FD(oldFd, constants_1.WASI_RIGHT_PATH_LINK_SOURCE);
var nstats = CHECK_FD(newFd, constants_1.WASI_RIGHT_PATH_LINK_TARGET);
if (!ostats.path || !nstats.path) {
return constants_1.WASI_EINVAL;
}
_this.refreshMemory();
var op = Buffer.from(_this.memory.buffer, oldPath, oldPathLen).toString();
var np = Buffer.from(_this.memory.buffer, newPath, newPathLen).toString();
fs.linkSync(path.resolve(ostats.path, op), path.resolve(nstats.path, np));
return constants_1.WASI_ESUCCESS;
}),
path_open: wrap(function (dirfd, _dirflags, pathPtr, pathLen, oflags, fsRightsBase, fsRightsInheriting, fsFlags, fd) {
var stats = CHECK_FD(dirfd, constants_1.WASI_RIGHT_PATH_OPEN);
fsRightsBase = BigInt(fsRightsBase);
fsRightsInheriting = BigInt(fsRightsInheriting);
var read = (fsRightsBase & (constants_1.WASI_RIGHT_FD_READ | constants_1.WASI_RIGHT_FD_READDIR)) !==
BigInt(0);
var write = (fsRightsBase &
(constants_1.WASI_RIGHT_FD_DATASYNC |
constants_1.WASI_RIGHT_FD_WRITE |
constants_1.WASI_RIGHT_FD_ALLOCATE |
constants_1.WASI_RIGHT_FD_FILESTAT_SET_SIZE)) !==
BigInt(0);
var noflags;
if (write && read) {
noflags = fs.constants.O_RDWR;
}
else if (read) {
noflags = fs.constants.O_RDONLY;
}
else if (write) {
noflags = fs.constants.O_WRONLY;
}
// fsRightsBase is needed here but perhaps we should do it in neededInheriting
var neededBase = fsRightsBase | constants_1.WASI_RIGHT_PATH_OPEN;
var neededInheriting = fsRightsBase | fsRightsInheriting;
if ((oflags & constants_1.WASI_O_CREAT) !== 0) {
noflags |= fs.constants.O_CREAT;
neededBase |= constants_1.WASI_RIGHT_PATH_CREATE_FILE;
}
if ((oflags & constants_1.WASI_O_DIRECTORY) !== 0) {
noflags |= fs.constants.O_DIRECTORY;
}
if ((oflags & constants_1.WASI_O_EXCL) !== 0) {
noflags |= fs.constants.O_EXCL;
}
if ((oflags & constants_1.WASI_O_TRUNC) !== 0) {
noflags |= fs.constants.O_TRUNC;
neededBase |= constants_1.WASI_RIGHT_PATH_FILESTAT_SET_SIZE;
}
// Convert file descriptor flags.
if ((fsFlags & constants_1.WASI_FDFLAG_APPEND) !== 0) {
noflags |= fs.constants.O_APPEND;
}
if ((fsFlags & constants_1.WASI_FDFLAG_DSYNC) !== 0) {
if (fs.constants.O_DSYNC) {
noflags |= fs.constants.O_DSYNC;
}
else {
noflags |= fs.constants.O_SYNC;
}
neededInheriting |= constants_1.WASI_RIGHT_FD_DATASYNC;
}
if ((fsFlags & constants_1.WASI_FDFLAG_NONBLOCK) !== 0) {
noflags |= fs.constants.O_NONBLOCK;
}
if ((fsFlags & constants_1.WASI_FDFLAG_RSYNC) !== 0) {
if (fs.constants.O_RSYNC) {
noflags |= fs.constants.O_RSYNC;
}
else {
noflags |= fs.constants.O_SYNC;
}
neededInheriting |= constants_1.WASI_RIGHT_FD_SYNC;
}
if ((fsFlags & constants_1.WASI_FDFLAG_SYNC) !== 0) {
noflags |= fs.constants.O_SYNC;
neededInheriting |= constants_1.WASI_RIGHT_FD_SYNC;
}
if (write &&
(noflags & (fs.constants.O_APPEND | fs.constants.O_TRUNC)) === 0) {
neededInheriting |= constants_1.WASI_RIGHT_FD_SEEK;
}
_this.refreshMemory();
var p = Buffer.from(_this.memory.buffer, pathPtr, pathLen).toString();
logOpen("path_open", p);
var fullUnresolved = path.resolve(stats.path, p);
if (path.relative(stats.path, fullUnresolved).startsWith("..")) {
return constants_1.WASI_ENOTCAPABLE;
}
var full;
try {
full = fs.realpathSync(fullUnresolved);
if (path.relative(stats.path, full).startsWith("..")) {
return constants_1.WASI_ENOTCAPABLE;
}
}
catch (e) {
if ((e === null || e === void 0 ? void 0 : e.code) === "ENOENT") {
full = fullUnresolved;
}
else {
// log("** openpath FAIL: p = ", p, e);
throw e;
}
}
/* check if the file is a directory (unless opening for write,
* in which case the file may not exist and should be created) */
var isDirectory;
try {
isDirectory = fs.statSync(full).isDirectory();
}
catch (e) { }
var realfd;
if (!write && isDirectory) {
realfd = fs.openSync(full, fs.constants.O_RDONLY);
}
else {
realfd = fs.openSync(full, noflags);
}
var newfd = _this.getUnusedFileDescriptor();
// log(`** openpath got fd: p='${p}', fd=${newfd}`);
_this.FD_MAP.set(newfd, {
real: realfd,
filetype: undefined,
// offset: BigInt(0),
rights: {
base: neededBase,
inheriting: neededInheriting,
},
path: full,
});
stat(_this, newfd);
_this.view.setUint32(fd, newfd, true);
return constants_1.WASI_ESUCCESS;
}),
path_readlink: wrap(function (fd, pathPtr, pathLen, buf, bufLen, bufused) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_PATH_READLINK);
if (!stats.path) {
return constants_1.WASI_EINVAL;
}
_this.refreshMemory();
var p = Buffer.from(_this.memory.buffer, pathPtr, pathLen).toString();
var full = path.resolve(stats.path, p);
var r = fs.readlinkSync(full);
var used = Buffer.from(_this.memory.buffer).write(r, buf, bufLen);
_this.view.setUint32(bufused, used, true);
return constants_1.WASI_ESUCCESS;
}),
path_remove_directory: wrap(function (fd, pathPtr, pathLen) {
var stats = CHECK_FD(fd, constants_1.WASI_RIGHT_PATH_REMOVE_DIRECTORY);
if (!stats.path) {
return constants_1.WASI_EINVAL;
}
_this.refreshMemory();
var p = Buffer.from(_this.memory.buffer, pathPtr, pathLen).toString();
fs.rmdirSync(path.resolve(stats.path, p));
return constants_1.WASI_ESUCCESS;
}),
path_rename: wrap(function (oldFd, oldPath, oldPathLen, newFd, newPath, newPathLen) {
var ostats = CHECK_FD(oldFd, constants_1.WASI_RIGHT_PATH_RENAME_SOURCE);
var nstats = CHECK_FD(newFd, constants_1.WASI_RIGHT_PATH_RENAME_TARGET);
if (!ostats.path || !nstats.path) {
return constants_1.WASI_EINVAL;
}