UNPKG

@tybys/wasm-util

Version:

WASI polyfill for browser and some wasm util

1,270 lines (1,257 loc) 123 kB
const _WebAssembly = typeof WebAssembly !== 'undefined' ? WebAssembly : typeof WXWebAssembly !== 'undefined' ? WXWebAssembly : undefined; if (!_WebAssembly) { throw new Error('WebAssembly is not supported in this environment'); } /* eslint-disable spaced-comment */ function validateObject(value, name) { if (value === null || typeof value !== 'object') { throw new TypeError(`${name} must be an object. Received ${value === null ? 'null' : typeof value}`); } } function validateArray(value, name) { if (!Array.isArray(value)) { throw new TypeError(`${name} must be an array. Received ${value === null ? 'null' : typeof value}`); } } function validateBoolean(value, name) { if (typeof value !== 'boolean') { throw new TypeError(`${name} must be a boolean. Received ${value === null ? 'null' : typeof value}`); } } function validateString(value, name) { if (typeof value !== 'string') { throw new TypeError(`${name} must be a string. Received ${value === null ? 'null' : typeof value}`); } } function validateFunction(value, name) { if (typeof value !== 'function') { throw new TypeError(`${name} must be a function. Received ${value === null ? 'null' : typeof value}`); } } function validateUndefined(value, name) { if (value !== undefined) { throw new TypeError(`${name} must be undefined. Received ${value === null ? 'null' : typeof value}`); } } function isPromiseLike(obj) { return !!(obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'); } function wrapInstanceExports(exports, mapFn) { const newExports = Object.create(null); Object.keys(exports).forEach(name => { const exportValue = exports[name]; Object.defineProperty(newExports, name, { enumerable: true, value: mapFn(exportValue, name) }); }); return newExports; } function sleepBreakIf(delay, breakIf) { const start = Date.now(); const end = start + delay; let ret = false; while (Date.now() < end) { if (breakIf()) { ret = true; break; } } return ret; } function unsharedSlice(view, start, end) { return ((typeof SharedArrayBuffer === 'function' && view.buffer instanceof SharedArrayBuffer) || (Object.prototype.toString.call(view.buffer.constructor) === '[object SharedArrayBuffer]')) ? view.slice(start, end) : view.subarray(start, end); } const ignoreNames = [ 'asyncify_get_state', 'asyncify_start_rewind', 'asyncify_start_unwind', 'asyncify_stop_rewind', 'asyncify_stop_unwind' ]; function tryAllocate(instance, wasm64, size, mallocName) { if (typeof instance.exports[mallocName] !== 'function' || size <= 0) { return { wasm64, dataPtr: 16, start: wasm64 ? 32 : 24, end: 1024 }; } const malloc = instance.exports[mallocName]; const dataPtr = wasm64 ? Number(malloc(BigInt(16) + BigInt(size))) : malloc(8 + size); if (dataPtr === 0) { throw new Error('Allocate asyncify data failed'); } return wasm64 ? { wasm64, dataPtr, start: dataPtr + 16, end: dataPtr + 16 + size } : { wasm64, dataPtr, start: dataPtr + 8, end: dataPtr + 8 + size }; } /** @public */ class Asyncify { constructor() { this.value = undefined; this.exports = undefined; this.dataPtr = 0; } init(memory, instance, options) { var _a, _b; if (this.exports) { throw new Error('Asyncify has been initialized'); } if (!(memory instanceof _WebAssembly.Memory)) { throw new TypeError('Require WebAssembly.Memory object'); } const exports = instance.exports; for (let i = 0; i < ignoreNames.length; ++i) { if (typeof exports[ignoreNames[i]] !== 'function') { throw new TypeError('Invalid asyncify wasm'); } } let address; const wasm64 = Boolean(options.wasm64); if (!options.tryAllocate) { address = { wasm64, dataPtr: 16, start: wasm64 ? 32 : 24, end: 1024 }; } else { if (options.tryAllocate === true) { address = tryAllocate(instance, wasm64, 4096, 'malloc'); } else { address = tryAllocate(instance, wasm64, (_a = options.tryAllocate.size) !== null && _a !== void 0 ? _a : 4096, (_b = options.tryAllocate.name) !== null && _b !== void 0 ? _b : 'malloc'); } } this.dataPtr = address.dataPtr; if (wasm64) { new BigInt64Array(memory.buffer, this.dataPtr).set([BigInt(address.start), BigInt(address.end)]); } else { new Int32Array(memory.buffer, this.dataPtr).set([address.start, address.end]); } this.exports = this.wrapExports(exports, options.wrapExports); const asyncifiedInstance = Object.create(_WebAssembly.Instance.prototype); Object.defineProperty(asyncifiedInstance, 'exports', { value: this.exports }); // Object.setPrototypeOf(instance, Instance.prototype) return asyncifiedInstance; } assertState() { if (this.exports.asyncify_get_state() !== 0 /* AsyncifyState.NONE */) { throw new Error('Asyncify state error'); } } wrapImportFunction(f) { // eslint-disable-next-line @typescript-eslint/no-this-alias const _this = this; return (function () { // eslint-disable-next-line no-unreachable-loop while (_this.exports.asyncify_get_state() === 2 /* AsyncifyState.REWINDING */) { _this.exports.asyncify_stop_rewind(); return _this.value; } _this.assertState(); const v = f.apply(this, arguments); if (!isPromiseLike(v)) return v; _this.exports.asyncify_start_unwind(_this.dataPtr); _this.value = v; }); } wrapImports(imports) { const importObject = {}; Object.keys(imports).forEach(k => { const mod = imports[k]; const newModule = {}; Object.keys(mod).forEach(name => { const importValue = mod[name]; if (typeof importValue === 'function') { newModule[name] = this.wrapImportFunction(importValue); } else { newModule[name] = importValue; } }); importObject[k] = newModule; }); return importObject; } wrapExportFunction(f) { // eslint-disable-next-line @typescript-eslint/no-this-alias const _this = this; return (async function () { _this.assertState(); let ret = f.apply(this, arguments); while (_this.exports.asyncify_get_state() === 1 /* AsyncifyState.UNWINDING */) { _this.exports.asyncify_stop_unwind(); _this.value = await _this.value; _this.assertState(); _this.exports.asyncify_start_rewind(_this.dataPtr); ret = f.call(this); } _this.assertState(); return ret; }); } wrapExports(exports, needWrap) { return wrapInstanceExports(exports, (exportValue, name) => { let ignore = ignoreNames.indexOf(name) !== -1 || typeof exportValue !== 'function'; if (Array.isArray(needWrap)) { ignore = ignore || (needWrap.indexOf(name) === -1); } return ignore ? exportValue : this.wrapExportFunction(exportValue); }); } } function validateImports(imports) { if (imports && typeof imports !== 'object') { throw new TypeError('imports must be an object or undefined'); } } function fetchWasm(urlOrBuffer, imports) { if (typeof wx !== 'undefined' && typeof __wxConfig !== 'undefined') { return _WebAssembly.instantiate(urlOrBuffer, imports); } return fetch(urlOrBuffer) .then(response => response.arrayBuffer()) .then(buffer => _WebAssembly.instantiate(buffer, imports)); } /** @public */ function load(wasmInput, imports) { validateImports(imports); imports = imports !== null && imports !== void 0 ? imports : {}; let source; if (wasmInput instanceof ArrayBuffer || ArrayBuffer.isView(wasmInput)) { return _WebAssembly.instantiate(wasmInput, imports); } if (wasmInput instanceof _WebAssembly.Module) { return _WebAssembly.instantiate(wasmInput, imports).then((instance) => { return { instance, module: wasmInput }; }); } if (typeof wasmInput !== 'string' && !(wasmInput instanceof URL)) { throw new TypeError('Invalid source'); } if (typeof _WebAssembly.instantiateStreaming === 'function') { let responsePromise; try { responsePromise = fetch(wasmInput); source = _WebAssembly.instantiateStreaming(responsePromise, imports).catch(() => { return fetchWasm(wasmInput, imports); }); } catch (_) { source = fetchWasm(wasmInput, imports); } } else { source = fetchWasm(wasmInput, imports); } return source; } /** @public */ function asyncifyLoad(asyncify, urlOrBuffer, imports) { validateImports(imports); imports = imports !== null && imports !== void 0 ? imports : {}; const asyncifyHelper = new Asyncify(); imports = asyncifyHelper.wrapImports(imports); return load(urlOrBuffer, imports).then(source => { var _a; const memory = source.instance.exports.memory || ((_a = imports.env) === null || _a === void 0 ? void 0 : _a.memory); return { module: source.module, instance: asyncifyHelper.init(memory, source.instance, asyncify) }; }); } /** @public */ function loadSync(wasmInput, imports) { validateImports(imports); imports = imports !== null && imports !== void 0 ? imports : {}; let module; if ((wasmInput instanceof ArrayBuffer) || ArrayBuffer.isView(wasmInput)) { module = new _WebAssembly.Module(wasmInput); } else if (wasmInput instanceof WebAssembly.Module) { module = wasmInput; } else { throw new TypeError('Invalid source'); } const instance = new _WebAssembly.Instance(module, imports); const source = { instance, module }; return source; } /** @public */ function asyncifyLoadSync(asyncify, buffer, imports) { var _a; validateImports(imports); imports = imports !== null && imports !== void 0 ? imports : {}; const asyncifyHelper = new Asyncify(); imports = asyncifyHelper.wrapImports(imports); const source = loadSync(buffer, imports); const memory = source.instance.exports.memory || ((_a = imports.env) === null || _a === void 0 ? void 0 : _a.memory); return { module: source.module, instance: asyncifyHelper.init(memory, source.instance, asyncify) }; } const CHAR_DOT = 46; /* . */ const CHAR_FORWARD_SLASH = 47; /* / */ function isPosixPathSeparator(code) { return code === CHAR_FORWARD_SLASH; } function normalizeString(path, allowAboveRoot, separator, isPathSeparator) { let res = ''; let lastSegmentLength = 0; let lastSlash = -1; let dots = 0; let code = 0; for (let i = 0; i <= path.length; ++i) { if (i < path.length) { code = path.charCodeAt(i); } else if (isPathSeparator(code)) { break; } else { code = CHAR_FORWARD_SLASH; } if (isPathSeparator(code)) { if (lastSlash === i - 1 || dots === 1) ; else if (dots === 2) { if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== CHAR_DOT || res.charCodeAt(res.length - 2) !== CHAR_DOT) { if (res.length > 2) { const lastSlashIndex = res.indexOf(separator); if (lastSlashIndex === -1) { res = ''; lastSegmentLength = 0; } else { res = res.slice(0, lastSlashIndex); lastSegmentLength = res.length - 1 - res.indexOf(separator); } lastSlash = i; dots = 0; continue; } else if (res.length !== 0) { res = ''; lastSegmentLength = 0; lastSlash = i; dots = 0; continue; } } if (allowAboveRoot) { res += res.length > 0 ? `${separator}..` : '..'; lastSegmentLength = 2; } } else { if (res.length > 0) { res += `${separator}${path.slice(lastSlash + 1, i)}`; } else { res = path.slice(lastSlash + 1, i); } lastSegmentLength = i - lastSlash - 1; } lastSlash = i; dots = 0; } else if (code === CHAR_DOT && dots !== -1) { ++dots; } else { dots = -1; } } return res; } function resolve(...args) { let resolvedPath = ''; let resolvedAbsolute = false; for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { const path = i >= 0 ? args[i] : '/'; validateString(path, 'path'); // Skip empty entries if (path.length === 0) { continue; } resolvedPath = `${path}/${resolvedPath}`; resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) // Normalize the path resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator); if (resolvedAbsolute) { return `/${resolvedPath}`; } return resolvedPath.length > 0 ? resolvedPath : '.'; } const FD_DATASYNC = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(0)); const FD_READ = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(1)); const FD_SEEK = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(2)); const FD_FDSTAT_SET_FLAGS = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(3)); const FD_SYNC = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(4)); const FD_TELL = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(5)); const FD_WRITE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(6)); const FD_ADVISE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(7)); const FD_ALLOCATE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(8)); const PATH_CREATE_DIRECTORY = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(9)); const PATH_CREATE_FILE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(10)); const PATH_LINK_SOURCE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(11)); const PATH_LINK_TARGET = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(12)); const PATH_OPEN = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(13)); const FD_READDIR = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(14)); const PATH_READLINK = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(15)); const PATH_RENAME_SOURCE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(16)); const PATH_RENAME_TARGET = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(17)); const PATH_FILESTAT_GET = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(18)); const PATH_FILESTAT_SET_SIZE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(19)); const PATH_FILESTAT_SET_TIMES = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(20)); const FD_FILESTAT_GET = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(21)); const FD_FILESTAT_SET_SIZE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(22)); const FD_FILESTAT_SET_TIMES = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(23)); const PATH_SYMLINK = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(24)); const PATH_REMOVE_DIRECTORY = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(25)); const PATH_UNLINK_FILE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(26)); const POLL_FD_READWRITE = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(27)); const SOCK_SHUTDOWN = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(28)); const SOCK_ACCEPT = ( /*#__PURE__*/BigInt(1) << /*#__PURE__*/ BigInt(29)); const WasiRights = { FD_DATASYNC, FD_READ, FD_SEEK, FD_FDSTAT_SET_FLAGS, FD_SYNC, FD_TELL, FD_WRITE, FD_ADVISE, FD_ALLOCATE, PATH_CREATE_DIRECTORY, PATH_CREATE_FILE, PATH_LINK_SOURCE, PATH_LINK_TARGET, PATH_OPEN, FD_READDIR, PATH_READLINK, PATH_RENAME_SOURCE, PATH_RENAME_TARGET, PATH_FILESTAT_GET, PATH_FILESTAT_SET_SIZE, PATH_FILESTAT_SET_TIMES, FD_FILESTAT_GET, FD_FILESTAT_SET_SIZE, FD_FILESTAT_SET_TIMES, PATH_SYMLINK, PATH_REMOVE_DIRECTORY, PATH_UNLINK_FILE, POLL_FD_READWRITE, SOCK_SHUTDOWN, SOCK_ACCEPT }; function strerror(errno) { switch (errno) { case 0 /* WasiErrno.ESUCCESS */: return 'Success'; case 1 /* WasiErrno.E2BIG */: return 'Argument list too long'; case 2 /* WasiErrno.EACCES */: return 'Permission denied'; case 3 /* WasiErrno.EADDRINUSE */: return 'Address in use'; case 4 /* WasiErrno.EADDRNOTAVAIL */: return 'Address not available'; case 5 /* WasiErrno.EAFNOSUPPORT */: return 'Address family not supported by protocol'; case 6 /* WasiErrno.EAGAIN */: return 'Resource temporarily unavailable'; case 7 /* WasiErrno.EALREADY */: return 'Operation already in progress'; case 8 /* WasiErrno.EBADF */: return 'Bad file descriptor'; case 9 /* WasiErrno.EBADMSG */: return 'Bad message'; case 10 /* WasiErrno.EBUSY */: return 'Resource busy'; case 11 /* WasiErrno.ECANCELED */: return 'Operation canceled'; case 12 /* WasiErrno.ECHILD */: return 'No child process'; case 13 /* WasiErrno.ECONNABORTED */: return 'Connection aborted'; case 14 /* WasiErrno.ECONNREFUSED */: return 'Connection refused'; case 15 /* WasiErrno.ECONNRESET */: return 'Connection reset by peer'; case 16 /* WasiErrno.EDEADLK */: return 'Resource deadlock would occur'; case 17 /* WasiErrno.EDESTADDRREQ */: return 'Destination address required'; case 18 /* WasiErrno.EDOM */: return 'Domain error'; case 19 /* WasiErrno.EDQUOT */: return 'Quota exceeded'; case 20 /* WasiErrno.EEXIST */: return 'File exists'; case 21 /* WasiErrno.EFAULT */: return 'Bad address'; case 22 /* WasiErrno.EFBIG */: return 'File too large'; case 23 /* WasiErrno.EHOSTUNREACH */: return 'Host is unreachable'; case 24 /* WasiErrno.EIDRM */: return 'Identifier removed'; case 25 /* WasiErrno.EILSEQ */: return 'Illegal byte sequence'; case 26 /* WasiErrno.EINPROGRESS */: return 'Operation in progress'; case 27 /* WasiErrno.EINTR */: return 'Interrupted system call'; case 28 /* WasiErrno.EINVAL */: return 'Invalid argument'; case 29 /* WasiErrno.EIO */: return 'I/O error'; case 30 /* WasiErrno.EISCONN */: return 'Socket is connected'; case 31 /* WasiErrno.EISDIR */: return 'Is a directory'; case 32 /* WasiErrno.ELOOP */: return 'Symbolic link loop'; case 33 /* WasiErrno.EMFILE */: return 'No file descriptors available'; case 34 /* WasiErrno.EMLINK */: return 'Too many links'; case 35 /* WasiErrno.EMSGSIZE */: return 'Message too large'; case 36 /* WasiErrno.EMULTIHOP */: return 'Multihop attempted'; case 37 /* WasiErrno.ENAMETOOLONG */: return 'Filename too long'; case 38 /* WasiErrno.ENETDOWN */: return 'Network is down'; case 39 /* WasiErrno.ENETRESET */: return 'Connection reset by network'; case 40 /* WasiErrno.ENETUNREACH */: return 'Network unreachable'; case 41 /* WasiErrno.ENFILE */: return 'Too many files open in system'; case 42 /* WasiErrno.ENOBUFS */: return 'No buffer space available'; case 43 /* WasiErrno.ENODEV */: return 'No such device'; case 44 /* WasiErrno.ENOENT */: return 'No such file or directory'; case 45 /* WasiErrno.ENOEXEC */: return 'Exec format error'; case 46 /* WasiErrno.ENOLCK */: return 'No locks available'; case 47 /* WasiErrno.ENOLINK */: return 'Link has been severed'; case 48 /* WasiErrno.ENOMEM */: return 'Out of memory'; case 49 /* WasiErrno.ENOMSG */: return 'No message of the desired type'; case 50 /* WasiErrno.ENOPROTOOPT */: return 'Protocol not available'; case 51 /* WasiErrno.ENOSPC */: return 'No space left on device'; case 52 /* WasiErrno.ENOSYS */: return 'Function not implemented'; case 53 /* WasiErrno.ENOTCONN */: return 'Socket not connected'; case 54 /* WasiErrno.ENOTDIR */: return 'Not a directory'; case 55 /* WasiErrno.ENOTEMPTY */: return 'Directory not empty'; case 56 /* WasiErrno.ENOTRECOVERABLE */: return 'State not recoverable'; case 57 /* WasiErrno.ENOTSOCK */: return 'Not a socket'; case 58 /* WasiErrno.ENOTSUP */: return 'Not supported'; case 59 /* WasiErrno.ENOTTY */: return 'Not a tty'; case 60 /* WasiErrno.ENXIO */: return 'No such device or address'; case 61 /* WasiErrno.EOVERFLOW */: return 'Value too large for data type'; case 62 /* WasiErrno.EOWNERDEAD */: return 'Previous owner died'; case 63 /* WasiErrno.EPERM */: return 'Operation not permitted'; case 64 /* WasiErrno.EPIPE */: return 'Broken pipe'; case 65 /* WasiErrno.EPROTO */: return 'Protocol error'; case 66 /* WasiErrno.EPROTONOSUPPORT */: return 'Protocol not supported'; case 67 /* WasiErrno.EPROTOTYPE */: return 'Protocol wrong type for socket'; case 68 /* WasiErrno.ERANGE */: return 'Result not representable'; case 69 /* WasiErrno.EROFS */: return 'Read-only file system'; case 70 /* WasiErrno.ESPIPE */: return 'Invalid seek'; case 71 /* WasiErrno.ESRCH */: return 'No such process'; case 72 /* WasiErrno.ESTALE */: return 'Stale file handle'; case 73 /* WasiErrno.ETIMEDOUT */: return 'Operation timed out'; case 74 /* WasiErrno.ETXTBSY */: return 'Text file busy'; case 75 /* WasiErrno.EXDEV */: return 'Cross-device link'; case 76 /* WasiErrno.ENOTCAPABLE */: return 'Capabilities insufficient'; default: return 'Unknown error'; } } class WasiError extends Error { constructor(message, errno) { super(message); this.errno = errno; } getErrorMessage() { return strerror(this.errno); } } Object.defineProperty(WasiError.prototype, 'name', { configurable: true, writable: true, value: 'WasiError' }); const RIGHTS_ALL = WasiRights.FD_DATASYNC | WasiRights.FD_READ | WasiRights.FD_SEEK | WasiRights.FD_FDSTAT_SET_FLAGS | WasiRights.FD_SYNC | WasiRights.FD_TELL | WasiRights.FD_WRITE | WasiRights.FD_ADVISE | WasiRights.FD_ALLOCATE | WasiRights.PATH_CREATE_DIRECTORY | WasiRights.PATH_CREATE_FILE | WasiRights.PATH_LINK_SOURCE | WasiRights.PATH_LINK_TARGET | WasiRights.PATH_OPEN | WasiRights.FD_READDIR | WasiRights.PATH_READLINK | WasiRights.PATH_RENAME_SOURCE | WasiRights.PATH_RENAME_TARGET | WasiRights.PATH_FILESTAT_GET | WasiRights.PATH_FILESTAT_SET_SIZE | WasiRights.PATH_FILESTAT_SET_TIMES | WasiRights.FD_FILESTAT_GET | WasiRights.FD_FILESTAT_SET_TIMES | WasiRights.FD_FILESTAT_SET_SIZE | WasiRights.PATH_SYMLINK | WasiRights.PATH_UNLINK_FILE | WasiRights.PATH_REMOVE_DIRECTORY | WasiRights.POLL_FD_READWRITE | WasiRights.SOCK_SHUTDOWN | WasiRights.SOCK_ACCEPT; const BLOCK_DEVICE_BASE = RIGHTS_ALL; const BLOCK_DEVICE_INHERITING = RIGHTS_ALL; const CHARACTER_DEVICE_BASE = RIGHTS_ALL; const CHARACTER_DEVICE_INHERITING = RIGHTS_ALL; const REGULAR_FILE_BASE = WasiRights.FD_DATASYNC | WasiRights.FD_READ | WasiRights.FD_SEEK | WasiRights.FD_FDSTAT_SET_FLAGS | WasiRights.FD_SYNC | WasiRights.FD_TELL | WasiRights.FD_WRITE | WasiRights.FD_ADVISE | WasiRights.FD_ALLOCATE | WasiRights.FD_FILESTAT_GET | WasiRights.FD_FILESTAT_SET_SIZE | WasiRights.FD_FILESTAT_SET_TIMES | WasiRights.POLL_FD_READWRITE; const REGULAR_FILE_INHERITING = /*#__PURE__*/ BigInt(0); const DIRECTORY_BASE = WasiRights.FD_FDSTAT_SET_FLAGS | WasiRights.FD_SYNC | WasiRights.FD_ADVISE | WasiRights.PATH_CREATE_DIRECTORY | WasiRights.PATH_CREATE_FILE | WasiRights.PATH_LINK_SOURCE | WasiRights.PATH_LINK_TARGET | WasiRights.PATH_OPEN | WasiRights.FD_READDIR | WasiRights.PATH_READLINK | WasiRights.PATH_RENAME_SOURCE | WasiRights.PATH_RENAME_TARGET | WasiRights.PATH_FILESTAT_GET | WasiRights.PATH_FILESTAT_SET_SIZE | WasiRights.PATH_FILESTAT_SET_TIMES | WasiRights.FD_FILESTAT_GET | WasiRights.FD_FILESTAT_SET_TIMES | WasiRights.PATH_SYMLINK | WasiRights.PATH_UNLINK_FILE | WasiRights.PATH_REMOVE_DIRECTORY | WasiRights.POLL_FD_READWRITE; const DIRECTORY_INHERITING = DIRECTORY_BASE | REGULAR_FILE_BASE; const SOCKET_BASE = (WasiRights.FD_READ | WasiRights.FD_FDSTAT_SET_FLAGS | WasiRights.FD_WRITE | WasiRights.FD_FILESTAT_GET | WasiRights.POLL_FD_READWRITE | WasiRights.SOCK_SHUTDOWN); const SOCKET_INHERITING = RIGHTS_ALL; const TTY_BASE = WasiRights.FD_READ | WasiRights.FD_FDSTAT_SET_FLAGS | WasiRights.FD_WRITE | WasiRights.FD_FILESTAT_GET | WasiRights.POLL_FD_READWRITE; const TTY_INHERITING = /*#__PURE__*/ BigInt(0); function getRights(stdio, fd, flags, type) { const ret = { base: BigInt(0), inheriting: BigInt(0) }; if (type === 0 /* WasiFileType.UNKNOWN */) { throw new WasiError('Unknown file type', 28 /* WasiErrno.EINVAL */); } switch (type) { case 4 /* WasiFileType.REGULAR_FILE */: ret.base = REGULAR_FILE_BASE; ret.inheriting = REGULAR_FILE_INHERITING; break; case 3 /* WasiFileType.DIRECTORY */: ret.base = DIRECTORY_BASE; ret.inheriting = DIRECTORY_INHERITING; break; case 6 /* WasiFileType.SOCKET_STREAM */: case 5 /* WasiFileType.SOCKET_DGRAM */: ret.base = SOCKET_BASE; ret.inheriting = SOCKET_INHERITING; break; case 2 /* WasiFileType.CHARACTER_DEVICE */: if (stdio.indexOf(fd) !== -1) { ret.base = TTY_BASE; ret.inheriting = TTY_INHERITING; } else { ret.base = CHARACTER_DEVICE_BASE; ret.inheriting = CHARACTER_DEVICE_INHERITING; } break; case 1 /* WasiFileType.BLOCK_DEVICE */: ret.base = BLOCK_DEVICE_BASE; ret.inheriting = BLOCK_DEVICE_INHERITING; break; default: ret.base = BigInt(0); ret.inheriting = BigInt(0); } /* Disable read/write bits depending on access mode. */ const read_or_write_only = flags & (0 | 1 | 2); if (read_or_write_only === 0) { ret.base &= ~WasiRights.FD_WRITE; } else if (read_or_write_only === 1) { ret.base &= ~WasiRights.FD_READ; } return ret; } 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; } 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 === 0 /* WasiWhence.SET */) { this.pos = BigInt(offset); } else if (whence === 1 /* WasiWhence.CUR */) { this.pos += BigInt(offset); } else if (whence === 2 /* WasiWhence.END */) { this.pos = BigInt(this.size) - BigInt(offset); } else { throw new WasiError('Unknown whence', 29 /* WasiErrno.EIO */); } return this.pos; } } 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; } } function toFileType(stat) { if (stat.isBlockDevice()) return 1 /* WasiFileType.BLOCK_DEVICE */; if (stat.isCharacterDevice()) return 2 /* WasiFileType.CHARACTER_DEVICE */; if (stat.isDirectory()) return 3 /* WasiFileType.DIRECTORY */; if (stat.isSocket()) return 6 /* WasiFileType.SOCKET_STREAM */; if (stat.isFile()) return 4 /* WasiFileType.REGULAR_FILE */; if (stat.isSymbolicLink()) return 7 /* WasiFileType.SYMBOLIC_LINK */; return 0 /* WasiFileType.UNKNOWN */; } 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); } 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 = 2 /* WasiFileType.CHARACTER_DEVICE */; const { base, inheriting } = getRights(this.stdio, fd, 2 /* FileControlFlag.O_RDWR */, type); const wrap = this.insert(fd, name, name, type, base, inheriting, 0); if (wrap.id !== expected) { throw new WasiError(`id: ${wrap.id} !== expected: ${expected}`, 8 /* 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 WasiError('Invalid fd', 8 /* WasiErrno.EBADF */); } const entry = this.fds[id]; if (!entry || entry.id !== id) { throw new WasiError('Bad file descriptor', 8 /* WasiErrno.EBADF */); } /* Validate that the fd has the necessary rights. */ if ((~entry.rightsBase & base) !== BigInt(0) || (~entry.rightsInheriting & inheriting) !== BigInt(0)) { throw new WasiError('Capabilities insufficient', 76 /* WasiErrno.ENOTCAPABLE */); } return entry; } remove(id) { if (id >= this.size) { throw new WasiError('Invalid fd', 8 /* WasiErrno.EBADF */); } const entry = this.fds[id]; if (!entry || entry.id !== id) { throw new WasiError('Bad file descriptor', 8 /* WasiErrno.EBADF */); } this.fds[id] = undefined; this.used--; } } 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 !== 3 /* WasiFileType.DIRECTORY */) { throw new WasiError(`Preopen not dir: ["${mappedPath}", "${realPath}"]`, 54 /* WasiErrno.ENOTDIR */); } const result = 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 WasiError('Invalid fd', 8 /* WasiErrno.EBADF */); } const dstEntry = this.fds[dst]; const srcEntry = this.fds[src]; if (!dstEntry || !srcEntry || dstEntry.id !== dst || srcEntry.id !== src) { throw new WasiError('Invalid fd', 8 /* WasiErrno.EBADF */); } this.fs.closeSync(dstEntry.fd); this.fds[dst] = this.fds[src]; this.fds[dst].id = dst; this.fds[src] = undefined; this.used--; } } 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 !== 3 /* WasiFileType.DIRECTORY */) { throw new WasiError(`Preopen not dir: ["${mappedPath}", "${realPath}"]`, 54 /* WasiErrno.ENOTDIR */); } const result = 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 WasiError('Invalid fd', 8 /* WasiErrno.EBADF */); } const dstEntry = this.fds[dst]; const srcEntry = this.fds[src]; if (!dstEntry || !srcEntry || dstEntry.id !== dst || srcEntry.id !== src) { throw new WasiError('Invalid fd', 8 /* WasiErrno.EBADF */); } await dstEntry.fd.close(); this.fds[dst] = this.fds[src]; this.fds[dst].id = dst; this.fds[src] = undefined; this.used--; } } /** @public */ const WebAssemblyMemory = /*#__PURE__*/ (function () { return _WebAssembly.Memory; })(); /** @public */ class Memory extends WebAssemblyMemory { // eslint-disable-next-line @typescript-eslint/no-useless-constructor constructor(descriptor) { super(descriptor); } get HEAP8() { return new Int8Array(super.buffer); } get HEAPU8() { return new Uint8Array(super.buffer); } get HEAP16() { return new Int16Array(super.buffer); } get HEAPU16() { return new Uint16Array(super.buffer); } get HEAP32() { return new Int32Array(super.buffer); } get HEAPU32() { return new Uint32Array(super.buffer); } get HEAP64() { return new BigInt64Array(super.buffer); } get HEAPU64() { return new BigUint64Array(super.buffer); } get HEAPF32() { return new Float32Array(super.buffer); } get HEAPF64() { return new Float64Array(super.buffer); } get view() { return new DataView(super.buffer); } } /** @public */ function extendMemory(memory) { if (Object.getPrototypeOf(memory) === _WebAssembly.Memory.prototype) { Object.setPrototypeOf(memory, Memory.prototype); } return memory; } function checkWebAssemblyFunction() { const WebAssemblyFunction = _WebAssembly.Function; if (typeof WebAssemblyFunction !== 'function') { throw new Error('WebAssembly.Function is not supported in this environment.' + ' If you are using V8 based browser like Chrome, try to specify' + ' --js-flags="--wasm-staging --experimental-wasm-stack-switching"'); } return WebAssemblyFunction; } /** @public */ function wrapAsyncImport(f, parameterType, returnType) { const WebAssemblyFunction = checkWebAssemblyFunction(); if (typeof f !== 'function') { throw new TypeError('Function required'); } const parameters = parameterType.slice(0); parameters.unshift('externref'); return new WebAssemblyFunction({ parameters, results: returnType }, f, { suspending: 'first' }); } /** @public */ function wrapAsyncExport(f) { const WebAssemblyFunction = checkWebAssemblyFunction(); if (typeof f !== 'function') { throw new TypeError('Function required'); } return new WebAssemblyFunction({ parameters: [...WebAssemblyFunction.type(f).parameters.slice(1)], results: ['externref'] }, f, { promising: 'first' }); } /** @public */ function wrapExports(exports, needWrap) { return wrapInstanceExports(exports, (exportValue, name) => { let ignore = typeof exportValue !== 'function'; if (Array.isArray(needWrap)) { ignore = ignore || (needWrap.indexOf(name) === -1); } return ignore ? exportValue : wrapAsyncExport(exportValue); }); } function copyMemory(targets, src) { if (targets.length === 0 || src.length === 0) return 0; let copied = 0; let left = src.length - copied; for (let i = 0; i < targets.length; ++i) { const target = targets[i]; if (left < target.length) { target.set(src.subarray(copied, copied + left), 0); copied += left; left = 0; return copied; } target.set(src.subarray(copied, copied + target.length), 0); copied += target.length; left -= target.length; } return copied; } const _memory = new WeakMap(); const _wasi = new WeakMap(); const _fs = new WeakMap(); function getMemory(wasi) { return _memory.get(wasi); } function getFs(wasi) { const fs = _fs.get(wasi); if (!fs) throw new Error('filesystem is unavailable'); return fs; } function handleError(err) { if (err instanceof WasiError) { { console.warn(err); } return err.errno; } switch (err.code) { case 'ENOENT': return 44 /* WasiErrno.ENOENT */; case 'EBADF': return 8 /* WasiErrno.EBADF */; case 'EINVAL': return 28 /* WasiErrno.EINVAL */; case 'EPERM': return 63 /* WasiErrno.EPERM */; case 'EPROTO': return 65 /* WasiErrno.EPROTO */; case 'EEXIST': return 20 /* WasiErrno.EEXIST */; case 'ENOTDIR': return 54 /* WasiErrno.ENOTDIR */; case 'EMFILE': return 33 /* WasiErrno.EMFILE */; case 'EACCES': return 2 /* WasiErrno.EACCES */; case 'EISDIR': return 31 /* WasiErrno.EISDIR */; case 'ENOTEMPTY': return 55 /* WasiErrno.ENOTEMPTY */; case 'ENOSYS': return 52 /* WasiErrno.ENOSYS */; } throw err; } function defineName(name, f) { Object.defineProperty(f, 'name', { value: name }); return f; } function tryCall(f, wasi, args) { let r; try { r = f.apply(wasi, args); } catch (err) { return handleError(err); } if (isPromiseLike(r)) { return r.then(_ => _, handleError); } return r; } function syscallWrap(self, name, f) { let debug = false; const NODE_DEBUG_NATIVE = (() => { try { return "wasi"; } catch (_) { return undefined; } })(); if (typeof NODE_DEBUG_NATIVE === 'string' && NODE_DEBUG_NATIVE.split(',').includes('wasi')) { debug = true; } return debug ? defineName(name, function () { const args = Array.prototype.slice.call(arguments); let debugArgs = [`${name}(${Array.from({ length: arguments.length }).map(() => '%d').join(', ')})`]; debugArgs = debugArgs.concat(args); console.debug.apply(console, debugArgs); return tryCall(f, self, args); }) : defineName(name, function () { return tryCall(f, self, arguments); }); } function resolvePathSync(fs, fileDescriptor, path, flags) { let resolvedPath = resolve(fileDescriptor.realPath, path); if ((flags & 1) === 1) { try { resolvedPath = fs.readlinkSync(resolvedPath); } catch (err) { if (err.code !== 'EINVAL' && err.code !== 'ENOENT') { throw err; } } } return resolvedPath; } async function resolvePathAsync(fs, fileDescriptor, path, flags) { let resolvedPath = resolve(fileDescriptor.realPath, path); if ((flags & 1) === 1) { try { resolvedPath = await fs.promises.readlink(resolvedPath); } catch (err) { if (err.code !== 'EINVAL' && err.code !== 'ENOENT') { throw err; } } } return resolvedPath; } // eslint-disable-next-line spaced-comment const encoder = /*#__PURE__*/ new TextEncoder(); // eslint-disable-next-line spaced-comment const decoder = /*#__PURE__*/ new TextDecoder(); const INT64_MAX = (BigInt(1) << BigInt(63)) - BigInt(1); function readStdin() { const value = window.prompt(); if (value === null) return new Uint8Array(); const buffer = new TextEncoder().encode(value + '\n'); return buffer; } function validateFstFlagsOrReturn(flags) { return (Boolean((flags) & ~(1 /* WasiFstFlag.SET_ATIM */ | 2 /* WasiFstFlag.SET_ATIM_NOW */ | 4 /* WasiFstFlag.SET_MTIM */ | 8 /* WasiFstFlag.SET_MTIM_NOW */)) || ((flags) & (1 /* WasiFstFlag.SET_ATIM */ | 2 /* WasiFstFlag.SET_ATIM_NOW */)) === (1 /* WasiFstFlag.SET_ATIM */ | 2 /* WasiFstFlag.SET_ATIM_NOW */) || ((flags) & (4 /* WasiFstFlag.SET_MTIM */ | 8 /* WasiFstFlag.SET_MTIM_NOW */)) === (4 /* WasiFstFlag.SET_MTIM */ | 8 /* WasiFstFlag.SET_MTIM_NOW */)); } class WASI$1 { constructor(args, env, fds, asyncFs, fs, asyncify) { this.args_get = syscallWrap(this, 'args_get', function (argv, argv_buf) { argv = Number(argv); argv_buf = Number(argv_buf); if (argv === 0 || argv_buf === 0) { return 28 /* WasiErrno.EINVAL */; } const { HEAPU8, view } = getMemory(this); const wasi = _wasi.get(this); const args = wasi.args; for (let i = 0; i < args.length; ++i) { const arg = args[i]; view.setInt32(argv, argv_buf, true); argv += 4; const data = encoder.encode(arg + '\0'); HEAPU8.set(data, argv_buf); argv_buf += data.length; } return 0 /* WasiErrno.ESUCCESS */; }); this.args_sizes_get = syscallWrap(this, 'args_sizes_get', function (argc, argv_buf_size) { argc = Number(argc); argv_buf_size = Number(argv_buf_size); if (argc === 0 || argv_buf_size === 0) { return 28 /* WasiErrno.EINVAL */; } const { view } = getMemory(this); const wasi = _wasi.get(this); const args = wasi.args; view.setUint32(argc, args.length, true); view.setUint32(argv_buf_size, encoder.encode(args.join('\0') + '\0').length, true); return 0 /* WasiErrno.ESUCCESS */; }); this.environ_get = syscallWrap(this, 'environ_get', function (environ, environ_buf) { environ = Number(environ); environ_buf = Number(environ_buf); if (environ === 0 || environ_buf === 0) { return 28 /* WasiErrno.EINVAL */; } const { HEAPU8, view } = getMemory(this); const wasi = _wasi.get(this); const env = wasi.env; for (let i = 0; i < env.length; ++i) { const pair = env[i]; view.setInt32(environ, environ_buf, true); environ += 4; const data = encoder.encode(pair + '\0'); HEAPU8.set(data, environ_buf); environ_buf += data.length; } return 0 /* WasiErrno.ESUCCESS */; }); this.environ_sizes_get = syscallWrap(this, 'environ_sizes_get', function (len, buflen) { len = Number(len); buflen = Number(buflen); if (len === 0 || buflen === 0) { return 28 /* WasiErrno.EINVAL */; } const { view } = getMemory(this); const wasi = _wasi.get(this); view.setUint32(len, wasi.env.length, true); view.setUint32(buflen, encoder.encode(wasi.env.join('\0') + '\0').length, true); return 0 /* WasiErrno.ESUCCESS */; }); this.clock_res_get = syscallWrap(this, 'clock_res_get', function (id, resolution) { resolution = Number(resolution); if (resolution === 0) { return 28 /* WasiErrno.EINVAL */; } const { view } = getMemory(this); switch (id) { case 0 /* WasiClockid.REALTIME */: view.setBigUint64(resolution, BigInt(1000000), true); return 0 /* WasiErrno.ESUCCESS */; case 1 /* WasiClockid.MONOTONIC */: case 2 /* WasiClockid.PROCESS_CPUTIME_ID */: case 3 /* WasiClockid.THREAD_CPUTIME_ID */: view.setBigUint64(resolution, BigInt(1000), true); return 0 /* WasiErrno.ESUCCESS */; default: return 28 /* WasiErrno.EINVAL */; } }); this.clock_time_get = syscallWrap(this, 'clock_time_get', function (id, _percision, time) { time = Number(time); if (time === 0) { return 28 /* WasiErrno.EINVAL */; } const { view } = getMemory(this); switch (id) { case 0 /* WasiClockid.REALTIME */: view.setBigUint64(time, BigInt(Date.now()) * BigInt(1000000), true); return 0 /* WasiErrno.ESUCCESS */; case 1 /* WasiClockid.MONOTONIC */: case 2 /* WasiClockid.PROCESS_CPUTIME_ID */: case 3 /* WasiClockid.THREAD_CPUTIME_ID */: { const t = performance.now() / 1000; const s = Math.trunc(t); const ms = Math.floor((t - s) * 1000); const result = BigInt(s) * BigInt(1000000000) + BigInt(ms) * BigInt(1000000); view.setBigUint64(time, result, true); return 0 /* WasiErrno.ESUCCESS */; } default: return 28 /* WasiErrno.EINVAL */; } });