@tybys/wasm-util
Version:
WASI polyfill for browser and some wasm util
268 lines • 10.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsyncTable = exports.SyncTable = exports.FileDescriptorTable = exports.toFileStat = exports.toFileType = exports.StandardOutput = exports.FileDescriptor = exports.concatBuffer = void 0;
const types_1 = require("./types");
const rights_1 = require("./rights");
const error_1 = require("./error");
function concatBuffer(buffers, size) {
let total = 0;
if (typeof size === 'number' && size >= 0) {
total = size;
}
else {
for (let i = 0; i < buffers.length; i++) {
const buffer = buffers[i];
total += buffer.length;
}
}
let pos = 0;
const ret = new Uint8Array(total);
for (let i = 0; i < buffers.length; i++) {
const buffer = buffers[i];
ret.set(buffer, pos);
pos += buffer.length;
}
return ret;
}
exports.concatBuffer = concatBuffer;
class FileDescriptor {
constructor(id, fd, path, realPath, type, rightsBase, rightsInheriting, preopen) {
this.id = id;
this.fd = fd;
this.path = path;
this.realPath = realPath;
this.type = type;
this.rightsBase = rightsBase;
this.rightsInheriting = rightsInheriting;
this.preopen = preopen;
this.pos = BigInt(0);
this.size = BigInt(0);
}
seek(offset, whence) {
if (whence === types_1.WasiWhence.SET) {
this.pos = BigInt(offset);
}
else if (whence === types_1.WasiWhence.CUR) {
this.pos += BigInt(offset);
}
else if (whence === types_1.WasiWhence.END) {
this.pos = BigInt(this.size) - BigInt(offset);
}
else {
throw new error_1.WasiError('Unknown whence', types_1.WasiErrno.EIO);
}
return this.pos;
}
}
exports.FileDescriptor = FileDescriptor;
class StandardOutput extends FileDescriptor {
constructor(log, id, fd, path, realPath, type, rightsBase, rightsInheriting, preopen) {
super(id, fd, path, realPath, type, rightsBase, rightsInheriting, preopen);
this._log = log;
this._buf = null;
}
write(buffer) {
const originalBuffer = buffer;
if (this._buf) {
buffer = concatBuffer([this._buf, buffer]);
this._buf = null;
}
if (buffer.indexOf(10) === -1) {
this._buf = buffer;
return originalBuffer.byteLength;
}
let written = 0;
let lastBegin = 0;
let index;
while ((index = buffer.indexOf(10, written)) !== -1) {
const str = new TextDecoder().decode(buffer.subarray(lastBegin, index));
this._log(str);
written += index - lastBegin + 1;
lastBegin = index + 1;
}
if (written < buffer.length) {
this._buf = buffer.slice(written);
}
return originalBuffer.byteLength;
}
}
exports.StandardOutput = StandardOutput;
function toFileType(stat) {
if (stat.isBlockDevice())
return types_1.WasiFileType.BLOCK_DEVICE;
if (stat.isCharacterDevice())
return types_1.WasiFileType.CHARACTER_DEVICE;
if (stat.isDirectory())
return types_1.WasiFileType.DIRECTORY;
if (stat.isSocket())
return types_1.WasiFileType.SOCKET_STREAM;
if (stat.isFile())
return types_1.WasiFileType.REGULAR_FILE;
if (stat.isSymbolicLink())
return types_1.WasiFileType.SYMBOLIC_LINK;
return types_1.WasiFileType.UNKNOWN;
}
exports.toFileType = toFileType;
function toFileStat(view, buf, stat) {
view.setBigUint64(buf, stat.dev, true);
view.setBigUint64(buf + 8, stat.ino, true);
view.setBigUint64(buf + 16, BigInt(toFileType(stat)), true);
view.setBigUint64(buf + 24, stat.nlink, true);
view.setBigUint64(buf + 32, stat.size, true);
view.setBigUint64(buf + 40, stat.atimeMs * BigInt(1000000), true);
view.setBigUint64(buf + 48, stat.mtimeMs * BigInt(1000000), true);
view.setBigUint64(buf + 56, stat.ctimeMs * BigInt(1000000), true);
}
exports.toFileStat = toFileStat;
class FileDescriptorTable {
constructor(options) {
this.used = 0;
this.size = options.size;
this.fds = Array(options.size);
this.stdio = [options.in, options.out, options.err];
this.print = options.print;
this.printErr = options.printErr;
this.insertStdio(options.in, 0, '<stdin>');
this.insertStdio(options.out, 1, '<stdout>');
this.insertStdio(options.err, 2, '<stderr>');
}
insertStdio(fd, expected, name) {
const type = types_1.WasiFileType.CHARACTER_DEVICE;
const { base, inheriting } = (0, rights_1.getRights)(this.stdio, fd, types_1.FileControlFlag.O_RDWR, type);
const wrap = this.insert(fd, name, name, type, base, inheriting, 0);
if (wrap.id !== expected) {
throw new error_1.WasiError(`id: ${wrap.id} !== expected: ${expected}`, types_1.WasiErrno.EBADF);
}
return wrap;
}
insert(fd, mappedPath, realPath, type, rightsBase, rightsInheriting, preopen) {
var _a, _b;
let index = -1;
if (this.used >= this.size) {
const newSize = this.size * 2;
this.fds.length = newSize;
index = this.size;
this.size = newSize;
}
else {
for (let i = 0; i < this.size; ++i) {
if (this.fds[i] == null) {
index = i;
break;
}
}
}
let entry;
if (mappedPath === '<stdout>') {
entry = new StandardOutput((_a = this.print) !== null && _a !== void 0 ? _a : console.log, index, fd, mappedPath, realPath, type, rightsBase, rightsInheriting, preopen);
}
else if (mappedPath === '<stderr>') {
entry = new StandardOutput((_b = this.printErr) !== null && _b !== void 0 ? _b : console.error, index, fd, mappedPath, realPath, type, rightsBase, rightsInheriting, preopen);
}
else {
entry = new FileDescriptor(index, fd, mappedPath, realPath, type, rightsBase, rightsInheriting, preopen);
}
this.fds[index] = entry;
this.used++;
return entry;
}
get(id, base, inheriting) {
if (id >= this.size) {
throw new error_1.WasiError('Invalid fd', types_1.WasiErrno.EBADF);
}
const entry = this.fds[id];
if (!entry || entry.id !== id) {
throw new error_1.WasiError('Bad file descriptor', types_1.WasiErrno.EBADF);
}
/* Validate that the fd has the necessary rights. */
if ((~entry.rightsBase & base) !== BigInt(0) || (~entry.rightsInheriting & inheriting) !== BigInt(0)) {
throw new error_1.WasiError('Capabilities insufficient', types_1.WasiErrno.ENOTCAPABLE);
}
return entry;
}
remove(id) {
if (id >= this.size) {
throw new error_1.WasiError('Invalid fd', types_1.WasiErrno.EBADF);
}
const entry = this.fds[id];
if (!entry || entry.id !== id) {
throw new error_1.WasiError('Bad file descriptor', types_1.WasiErrno.EBADF);
}
this.fds[id] = undefined;
this.used--;
}
}
exports.FileDescriptorTable = FileDescriptorTable;
class SyncTable extends FileDescriptorTable {
constructor(options) {
super(options);
this.fs = options.fs;
}
getFileTypeByFd(fd) {
const stats = this.fs.fstatSync(fd, { bigint: true });
return toFileType(stats);
}
insertPreopen(fd, mappedPath, realPath) {
const type = this.getFileTypeByFd(fd);
if (type !== types_1.WasiFileType.DIRECTORY) {
throw new error_1.WasiError(`Preopen not dir: ["${mappedPath}", "${realPath}"]`, types_1.WasiErrno.ENOTDIR);
}
const result = (0, rights_1.getRights)(this.stdio, fd, 0, type);
return this.insert(fd, mappedPath, realPath, type, result.base, result.inheriting, 1);
}
renumber(dst, src) {
if (dst === src)
return;
if (dst >= this.size || src >= this.size) {
throw new error_1.WasiError('Invalid fd', types_1.WasiErrno.EBADF);
}
const dstEntry = this.fds[dst];
const srcEntry = this.fds[src];
if (!dstEntry || !srcEntry || dstEntry.id !== dst || srcEntry.id !== src) {
throw new error_1.WasiError('Invalid fd', types_1.WasiErrno.EBADF);
}
this.fs.closeSync(dstEntry.fd);
this.fds[dst] = this.fds[src];
this.fds[dst].id = dst;
this.fds[src] = undefined;
this.used--;
}
}
exports.SyncTable = SyncTable;
class AsyncTable extends FileDescriptorTable {
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor(options) {
super(options);
}
async getFileTypeByFd(fd) {
const stats = await fd.stat({ bigint: true });
return toFileType(stats);
}
async insertPreopen(fd, mappedPath, realPath) {
const type = await this.getFileTypeByFd(fd);
if (type !== types_1.WasiFileType.DIRECTORY) {
throw new error_1.WasiError(`Preopen not dir: ["${mappedPath}", "${realPath}"]`, types_1.WasiErrno.ENOTDIR);
}
const result = (0, rights_1.getRights)(this.stdio, fd.fd, 0, type);
return this.insert(fd, mappedPath, realPath, type, result.base, result.inheriting, 1);
}
async renumber(dst, src) {
if (dst === src)
return;
if (dst >= this.size || src >= this.size) {
throw new error_1.WasiError('Invalid fd', types_1.WasiErrno.EBADF);
}
const dstEntry = this.fds[dst];
const srcEntry = this.fds[src];
if (!dstEntry || !srcEntry || dstEntry.id !== dst || srcEntry.id !== src) {
throw new error_1.WasiError('Invalid fd', types_1.WasiErrno.EBADF);
}
await dstEntry.fd.close();
this.fds[dst] = this.fds[src];
this.fds[dst].id = dst;
this.fds[src] = undefined;
this.used--;
}
}
exports.AsyncTable = AsyncTable;
//# sourceMappingURL=fd.js.map