UNPKG

@sqlite.org/sqlite-wasm

Version:

SQLite Wasm conveniently wrapped as an ES Module.

1,623 lines (1,611 loc) 572 kB
//#region src/bin/sqlite3.mjs /* @preserve ** ** LICENSE for the sqlite3 WebAssembly/JavaScript APIs. ** ** This bundle (typically released as sqlite3.js or sqlite3.mjs) ** is an amalgamation of JavaScript source code from two projects: ** ** 1) https://emscripten.org: the Emscripten "glue code" is covered by ** the terms of the MIT license and University of Illinois/NCSA ** Open Source License, as described at: ** ** https://emscripten.org/docs/introducing_emscripten/emscripten_license.html ** ** 2) https://sqlite.org: all code and documentation labeled as being ** from this source are released under the same terms as the sqlite3 ** C library: ** ** 2022-10-16 ** ** The author disclaims copyright to this source code. In place of a ** legal notice, here is a blessing: ** ** * May you do good and not evil. ** * May you find forgiveness for yourself and forgive others. ** * May you share freely, never taking more than you give. */ /* @preserve ** This code was built from sqlite3 version... ** ** SQLITE_VERSION "3.53.0" ** SQLITE_VERSION_NUMBER 3053000 ** SQLITE_SOURCE_ID "2026-04-09 11:41:38 4525003a53a7fc63ca75c59b22c79608659ca12f0131f52c18637f829977f20b" ** ** Emscripten SDK: 5.0.5 */ async function sqlite3InitModule(moduleArg = {}) { var moduleRtn; var Module = moduleArg; var ENVIRONMENT_IS_WEB = !!globalThis.window; var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope; globalThis.process?.versions?.node && globalThis.process?.type; /** BEGIN FILE: api/pre-js.js This file is intended to be prepended to the sqlite3.js build using Emscripten's --pre-js=THIS_FILE flag (or equivalent). It is run from inside of sqlite3InitModule(), after Emscripten's Module is defined, but early enough that we can ammend, or even outright replace, Module from here. Because this runs in-between Emscripten's own bootstrapping and Emscripten's main work, we must be careful with file-local symbol names. e.g. don't overwrite anything Emscripten defines and do not use 'const' for local symbols which Emscripten might try to use for itself. i.e. try to keep file-local symbol names obnoxiously collision-resistant. */ /** This file was preprocessed using: ./c-pp -o ./bld/pre-js.esm.js -Dtarget:es6-module -DModule.instantiateWasm api/pre-js.c-pp.js */ (function(Module) { const sIMS = globalThis.sqlite3InitModuleState || Object.assign(Object.create(null), { debugModule: function() { console.warn("globalThis.sqlite3InitModuleState is missing", arguments); } }); delete globalThis.sqlite3InitModuleState; sIMS.debugModule("pre-js.js sqlite3InitModuleState =", sIMS); /** This custom locateFile() tries to figure out where to load `path` from. The intent is to provide a way for foo/bar/X.js loaded from a Worker constructor or importScripts() to be able to resolve foo/bar/X.wasm (in the latter case, with some help): 1) If URL param named the same as `path` is set, it is returned. 2) If sqlite3InitModuleState.sqlite3Dir is set, then (thatName + path) is returned (it's assumed to end with '/'). 3) If this code is running in the main UI thread AND it was loaded from a SCRIPT tag, the directory part of that URL is used as the prefix. (This form of resolution unfortunately does not function for scripts loaded via importScripts().) 4) If none of the above apply, (prefix+path) is returned. None of the above apply in ES6 builds, which uses a much simpler approach. */ Module["locateFile"] = function(path, prefix) { if (this.emscriptenLocateFile instanceof Function) return this.emscriptenLocateFile(path, prefix); return new URL(path, import.meta.url).href; }.bind(sIMS); /** Override Module.instantiateWasm(). A custom Module.instantiateWasm() does not work in WASMFS builds: https://github.com/emscripten-core/emscripten/issues/17951 In such builds we must disable this. It's disabled in the (unsupported/untested) node builds because node does not do fetch(). */ Module["instantiateWasm"] = function callee(imports, onSuccess) { if (this.emscriptenInstantiateWasm instanceof Function) return this.emscriptenInstantiateWasm(imports, onSuccess); const sims = this; const uri = Module.locateFile(sims.wasmFilename, "undefined" === typeof scriptDirectory ? "" : scriptDirectory); sims.debugModule("instantiateWasm() uri =", uri, "sIMS =", this); const wfetch = () => fetch(uri, { credentials: "same-origin" }); const finalThen = (arg) => { arg.imports = imports; sims.instantiateWasm = arg; onSuccess(arg.instance, arg.module); }; return (WebAssembly.instantiateStreaming ? async () => WebAssembly.instantiateStreaming(wfetch(), imports).then(finalThen) : async () => wfetch().then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes, imports)).then(finalThen))(); }.bind(sIMS); })(Module); var thisProgram = "./this.program"; var _scriptName = import.meta.url; var scriptDirectory = ""; function locateFile(path) { if (Module["locateFile"]) return Module["locateFile"](path, scriptDirectory); return scriptDirectory + path; } var readAsync, readBinary; if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { try { scriptDirectory = new URL(".", _scriptName).href; } catch {} if (ENVIRONMENT_IS_WORKER) readBinary = (url) => { var xhr = new XMLHttpRequest(); xhr.open("GET", url, false); xhr.responseType = "arraybuffer"; xhr.send(null); return new Uint8Array(xhr.response); }; readAsync = async (url) => { var response = await fetch(url, { credentials: "same-origin" }); if (response.ok) return response.arrayBuffer(); throw new Error(response.status + " : " + response.url); }; } var out = console.log.bind(console); var err = console.error.bind(console); var wasmBinary; var ABORT = false; var readyPromiseResolve, readyPromiseReject; var runtimeInitialized = false; function updateMemoryViews() { var b = wasmMemory.buffer; HEAP8 = new Int8Array(b); HEAP16 = new Int16Array(b); HEAPU8 = new Uint8Array(b); HEAPU16 = new Uint16Array(b); HEAP32 = new Int32Array(b); HEAPU32 = new Uint32Array(b); HEAPF32 = new Float32Array(b); HEAPF64 = new Float64Array(b); HEAP64 = new BigInt64Array(b); HEAPU64 = new BigUint64Array(b); } function initMemory() { if (Module["wasmMemory"]) wasmMemory = Module["wasmMemory"]; else { var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 8388608; /** @suppress {checkTypes} */ wasmMemory = new WebAssembly.Memory({ "initial": INITIAL_MEMORY / 65536, "maximum": 32768 }); } updateMemoryViews(); } function preRun() { if (Module["preRun"]) { if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]]; while (Module["preRun"].length) addOnPreRun(Module["preRun"].shift()); } callRuntimeCallbacks(onPreRuns); } function initRuntime() { runtimeInitialized = true; if (!Module["noFSInit"] && !FS.initialized) FS.init(); TTY.init(); wasmExports["__wasm_call_ctors"](); FS.ignorePermissions = false; } function postRun() { if (Module["postRun"]) { if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]]; while (Module["postRun"].length) addOnPostRun(Module["postRun"].shift()); } callRuntimeCallbacks(onPostRuns); } /** * @param {string|number=} what * @noreturn */ function abort(what) { Module["onAbort"]?.(what); what = `Aborted(${what})`; err(what); ABORT = true; what += ". Build with -sASSERTIONS for more info."; /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); readyPromiseReject?.(e); throw e; } var wasmBinaryFile; function findWasmBinary() { if (Module["locateFile"]) return locateFile("sqlite3.wasm"); return new URL("sqlite3.wasm", import.meta.url).href; } function getBinarySync(file) { if (file == wasmBinaryFile && wasmBinary) return new Uint8Array(wasmBinary); if (readBinary) return readBinary(file); throw "both async and sync fetching of the wasm failed"; } async function getWasmBinary(binaryFile) { if (!wasmBinary) try { var response = await readAsync(binaryFile); return new Uint8Array(response); } catch {} return getBinarySync(binaryFile); } async function instantiateArrayBuffer(binaryFile, imports) { try { var binary = await getWasmBinary(binaryFile); return await WebAssembly.instantiate(binary, imports); } catch (reason) { err(`failed to asynchronously prepare wasm: ${reason}`); abort(reason); } } async function instantiateAsync(binary, binaryFile, imports) { if (!binary) try { var response = fetch(binaryFile, { credentials: "same-origin" }); return await WebAssembly.instantiateStreaming(response, imports); } catch (reason) { err(`wasm streaming compile failed: ${reason}`); err("falling back to ArrayBuffer instantiation"); } return instantiateArrayBuffer(binaryFile, imports); } function getWasmImports() { return { "env": wasmImports, "wasi_snapshot_preview1": wasmImports }; } async function createWasm() { /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { wasmExports = instance.exports; assignWasmExports(wasmExports); return wasmExports; } function receiveInstantiationResult(result) { return receiveInstance(result["instance"]); } var info = getWasmImports(); if (Module["instantiateWasm"]) return new Promise((resolve, reject) => { Module["instantiateWasm"](info, (inst, mod) => { resolve(receiveInstance(inst, mod)); }); }); wasmBinaryFile ??= findWasmBinary(); return receiveInstantiationResult(await instantiateAsync(wasmBinary, wasmBinaryFile, info)); } /** @type {!Int16Array} */ var HEAP16; /** @type {!Int32Array} */ var HEAP32; /** not-@type {!BigInt64Array} */ var HEAP64; /** @type {!Int8Array} */ var HEAP8; /** @type {!Float32Array} */ var HEAPF32; /** @type {!Float64Array} */ var HEAPF64; /** @type {!Uint16Array} */ var HEAPU16; /** @type {!Uint32Array} */ var HEAPU32; /** not-@type {!BigUint64Array} */ var HEAPU64; /** @type {!Uint8Array} */ var HEAPU8; var callRuntimeCallbacks = (callbacks) => { while (callbacks.length > 0) callbacks.shift()(Module); }; var onPostRuns = []; var addOnPostRun = (cb) => onPostRuns.push(cb); var onPreRuns = []; var addOnPreRun = (cb) => onPreRuns.push(cb); var wasmMemory; var PATH = { isAbs: (path) => path.charAt(0) === "/", splitPath: (filename) => { return /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(filename).slice(1); }, normalizeArray: (parts, allowAboveRoot) => { var up = 0; for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i]; if (last === ".") parts.splice(i, 1); else if (last === "..") { parts.splice(i, 1); up++; } else if (up) { parts.splice(i, 1); up--; } } if (allowAboveRoot) for (; up; up--) parts.unshift(".."); return parts; }, normalize: (path) => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.slice(-1) === "/"; path = PATH.normalizeArray(path.split("/").filter((p) => !!p), !isAbsolute).join("/"); if (!path && !isAbsolute) path = "."; if (path && trailingSlash) path += "/"; return (isAbsolute ? "/" : "") + path; }, dirname: (path) => { var result = PATH.splitPath(path), root = result[0], dir = result[1]; if (!root && !dir) return "."; if (dir) dir = dir.slice(0, -1); return root + dir; }, basename: (path) => path && path.match(/([^\/]+|\/)\/*$/)[1], join: (...paths) => PATH.normalize(paths.join("/")), join2: (l, r) => PATH.normalize(l + "/" + r) }; var initRandomFill = () => { return (view) => (crypto.getRandomValues(view), 0); }; var randomFill = (view) => (randomFill = initRandomFill())(view); var PATH_FS = { resolve: (...args) => { var resolvedPath = "", resolvedAbsolute = false; for (var i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? args[i] : FS.cwd(); if (typeof path != "string") throw new TypeError("Arguments to path.resolve must be strings"); else if (!path) return ""; resolvedPath = path + "/" + resolvedPath; resolvedAbsolute = PATH.isAbs(path); } resolvedPath = PATH.normalizeArray(resolvedPath.split("/").filter((p) => !!p), !resolvedAbsolute).join("/"); return (resolvedAbsolute ? "/" : "") + resolvedPath || "."; }, relative: (from, to) => { from = PATH_FS.resolve(from).slice(1); to = PATH_FS.resolve(to).slice(1); function trim(arr) { var start = 0; for (; start < arr.length; start++) if (arr[start] !== "") break; var end = arr.length - 1; for (; end >= 0; end--) if (arr[end] !== "") break; if (start > end) return []; return arr.slice(start, end - start + 1); } var fromParts = trim(from.split("/")); var toParts = trim(to.split("/")); var length = Math.min(fromParts.length, toParts.length); var samePartsLength = length; for (var i = 0; i < length; i++) if (fromParts[i] !== toParts[i]) { samePartsLength = i; break; } var outputParts = []; for (var i = samePartsLength; i < fromParts.length; i++) outputParts.push(".."); outputParts = outputParts.concat(toParts.slice(samePartsLength)); return outputParts.join("/"); } }; var UTF8Decoder = new TextDecoder(); var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var maxIdx = idx + maxBytesToRead; if (ignoreNul) return maxIdx; while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; return idx; }; /** * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given * array that contains uint8 values, returns a copy of that string as a * Javascript String object. * heapOrArray is either a regular array, or a JavaScript typed array view. * @param {number=} idx * @param {number=} maxBytesToRead * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. * @return {string} */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); return UTF8Decoder.decode(heapOrArray.buffer ? heapOrArray.subarray(idx, endPtr) : new Uint8Array(heapOrArray.slice(idx, endPtr))); }; var FS_stdin_getChar_buffer = []; var lengthBytesUTF8 = (str) => { var len = 0; for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i); if (c <= 127) len++; else if (c <= 2047) len += 2; else if (c >= 55296 && c <= 57343) { len += 4; ++i; } else len += 3; } return len; }; var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { if (!(maxBytesToWrite > 0)) return 0; var startIdx = outIdx; var endIdx = outIdx + maxBytesToWrite - 1; for (var i = 0; i < str.length; ++i) { var u = str.codePointAt(i); if (u <= 127) { if (outIdx >= endIdx) break; heap[outIdx++] = u; } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break; heap[outIdx++] = 192 | u >> 6; heap[outIdx++] = 128 | u & 63; } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break; heap[outIdx++] = 224 | u >> 12; heap[outIdx++] = 128 | u >> 6 & 63; heap[outIdx++] = 128 | u & 63; } else { if (outIdx + 3 >= endIdx) break; heap[outIdx++] = 240 | u >> 18; heap[outIdx++] = 128 | u >> 12 & 63; heap[outIdx++] = 128 | u >> 6 & 63; heap[outIdx++] = 128 | u & 63; i++; } } heap[outIdx] = 0; return outIdx - startIdx; }; /** @type {function(string, boolean=, number=)} */ var intArrayFromString = (stringy, dontAddNull, length) => { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1; var u8array = new Array(len); var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); if (dontAddNull) u8array.length = numBytesWritten; return u8array; }; var FS_stdin_getChar = () => { if (!FS_stdin_getChar_buffer.length) { var result = null; if (globalThis.window?.prompt) { result = window.prompt("Input: "); if (result !== null) result += "\n"; } if (!result) return null; FS_stdin_getChar_buffer = intArrayFromString(result, true); } return FS_stdin_getChar_buffer.shift(); }; var TTY = { ttys: [], init() {}, shutdown() {}, register(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops }; FS.registerDevice(dev, TTY.stream_ops); }, stream_ops: { open(stream) { var tty = TTY.ttys[stream.node.rdev]; if (!tty) throw new FS.ErrnoError(43); stream.tty = tty; stream.seekable = false; }, close(stream) { stream.tty.ops.fsync(stream.tty); }, fsync(stream) { stream.tty.ops.fsync(stream.tty); }, read(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) throw new FS.ErrnoError(60); var bytesRead = 0; for (var i = 0; i < length; i++) { var result; try { result = stream.tty.ops.get_char(stream.tty); } catch (e) { throw new FS.ErrnoError(29); } if (result === void 0 && bytesRead === 0) throw new FS.ErrnoError(6); if (result === null || result === void 0) break; bytesRead++; buffer[offset + i] = result; } if (bytesRead) stream.node.atime = Date.now(); return bytesRead; }, write(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) throw new FS.ErrnoError(60); try { for (var i = 0; i < length; i++) stream.tty.ops.put_char(stream.tty, buffer[offset + i]); } catch (e) { throw new FS.ErrnoError(29); } if (length) stream.node.mtime = stream.node.ctime = Date.now(); return i; } }, default_tty_ops: { get_char(tty) { return FS_stdin_getChar(); }, put_char(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output)); tty.output = []; } else if (val != 0) tty.output.push(val); }, fsync(tty) { if (tty.output?.length > 0) { out(UTF8ArrayToString(tty.output)); tty.output = []; } }, ioctl_tcgets(tty) { return { c_iflag: 25856, c_oflag: 5, c_cflag: 191, c_lflag: 35387, c_cc: [ 3, 28, 127, 21, 4, 0, 1, 0, 17, 19, 26, 0, 18, 15, 23, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }; }, ioctl_tcsets(tty, optional_actions, data) { return 0; }, ioctl_tiocgwinsz(tty) { return [24, 80]; } }, default_tty1_ops: { put_char(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output)); tty.output = []; } else if (val != 0) tty.output.push(val); }, fsync(tty) { if (tty.output?.length > 0) { err(UTF8ArrayToString(tty.output)); tty.output = []; } } } }; var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size); var alignMemory = (size, alignment) => { return Math.ceil(size / alignment) * alignment; }; var mmapAlloc = (size) => { size = alignMemory(size, 65536); var ptr = _emscripten_builtin_memalign(65536, size); if (ptr) zeroMemory(ptr, size); return ptr; }; var MEMFS = { ops_table: null, mount(mount) { return MEMFS.createNode(null, "/", 16895, 0); }, createNode(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) throw new FS.ErrnoError(63); MEMFS.ops_table ||= { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink }, stream: { llseek: MEMFS.stream_ops.llseek } }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync } }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink }, stream: {} }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr }, stream: FS.chrdev_stream_ops } }; var node = FS.createNode(parent, name, mode, dev); if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node; node.stream_ops = MEMFS.ops_table.dir.stream; node.contents = {}; } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node; node.stream_ops = MEMFS.ops_table.file.stream; node.usedBytes = 0; node.contents = MEMFS.emptyFileContents ??= new Uint8Array(0); } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node; node.stream_ops = MEMFS.ops_table.link.stream; } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node; node.stream_ops = MEMFS.ops_table.chrdev.stream; } node.atime = node.mtime = node.ctime = Date.now(); if (parent) { parent.contents[name] = node; parent.atime = parent.mtime = parent.ctime = node.atime; } return node; }, getFileDataAsTypedArray(node) { return node.contents.subarray(0, node.usedBytes); }, expandFileStorage(node, newCapacity) { var prevCapacity = node.contents.length; if (prevCapacity >= newCapacity) return; newCapacity = Math.max(newCapacity, prevCapacity * (prevCapacity < 1024 * 1024 ? 2 : 1.125) >>> 0); if (prevCapacity) newCapacity = Math.max(newCapacity, 256); var oldContents = MEMFS.getFileDataAsTypedArray(node); node.contents = new Uint8Array(newCapacity); node.contents.set(oldContents); }, resizeFileStorage(node, newSize) { if (node.usedBytes == newSize) return; var oldContents = node.contents; node.contents = new Uint8Array(newSize); node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); node.usedBytes = newSize; }, node_ops: { getattr(node) { var attr = {}; attr.dev = FS.isChrdev(node.mode) ? node.id : 1; attr.ino = node.id; attr.mode = node.mode; attr.nlink = 1; attr.uid = 0; attr.gid = 0; attr.rdev = node.rdev; if (FS.isDir(node.mode)) attr.size = 4096; else if (FS.isFile(node.mode)) attr.size = node.usedBytes; else if (FS.isLink(node.mode)) attr.size = node.link.length; else attr.size = 0; attr.atime = new Date(node.atime); attr.mtime = new Date(node.mtime); attr.ctime = new Date(node.ctime); attr.blksize = 4096; attr.blocks = Math.ceil(attr.size / attr.blksize); return attr; }, setattr(node, attr) { for (const key of [ "mode", "atime", "mtime", "ctime" ]) if (attr[key] != null) node[key] = attr[key]; if (attr.size !== void 0) MEMFS.resizeFileStorage(node, attr.size); }, lookup(parent, name) { if (!MEMFS.doesNotExistError) { MEMFS.doesNotExistError = new FS.ErrnoError(44); /** @suppress {checkTypes} */ MEMFS.doesNotExistError.stack = "<generic error, no stack>"; } throw MEMFS.doesNotExistError; }, mknod(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev); }, rename(old_node, new_dir, new_name) { var new_node; try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) {} if (new_node) { if (FS.isDir(old_node.mode)) for (var i in new_node.contents) throw new FS.ErrnoError(55); FS.hashRemoveNode(new_node); } delete old_node.parent.contents[old_node.name]; new_dir.contents[new_name] = old_node; old_node.name = new_name; new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now(); }, unlink(parent, name) { delete parent.contents[name]; parent.ctime = parent.mtime = Date.now(); }, rmdir(parent, name) { for (var i in FS.lookupNode(parent, name).contents) throw new FS.ErrnoError(55); delete parent.contents[name]; parent.ctime = parent.mtime = Date.now(); }, readdir(node) { return [ ".", "..", ...Object.keys(node.contents) ]; }, symlink(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 41471, 0); node.link = oldpath; return node; }, readlink(node) { if (!FS.isLink(node.mode)) throw new FS.ErrnoError(28); return node.link; } }, stream_ops: { read(stream, buffer, offset, length, position) { var contents = stream.node.contents; if (position >= stream.node.usedBytes) return 0; var size = Math.min(stream.node.usedBytes - position, length); buffer.set(contents.subarray(position, position + size), offset); return size; }, write(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) canOwn = false; if (!length) return 0; var node = stream.node; node.mtime = node.ctime = Date.now(); if (canOwn) { node.contents = buffer.subarray(offset, offset + length); node.usedBytes = length; } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length); node.usedBytes = length; } else { MEMFS.expandFileStorage(node, position + length); node.contents.set(buffer.subarray(offset, offset + length), position); node.usedBytes = Math.max(node.usedBytes, position + length); } return length; }, llseek(stream, offset, whence) { var position = offset; if (whence === 1) position += stream.position; else if (whence === 2) { if (FS.isFile(stream.node.mode)) position += stream.node.usedBytes; } if (position < 0) throw new FS.ErrnoError(28); return position; }, mmap(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) throw new FS.ErrnoError(43); var ptr; var allocated; var contents = stream.node.contents; if (!(flags & 2) && contents.buffer === HEAP8.buffer) { allocated = false; ptr = contents.byteOffset; } else { allocated = true; ptr = mmapAlloc(length); if (!ptr) throw new FS.ErrnoError(48); if (contents) { if (position > 0 || position + length < contents.length) if (contents.subarray) contents = contents.subarray(position, position + length); else contents = Array.prototype.slice.call(contents, position, position + length); HEAP8.set(contents, ptr); } } return { ptr, allocated }; }, msync(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); return 0; } } }; var FS_modeStringToFlags = (str) => { if (typeof str != "string") return str; var flags = { "r": 0, "r+": 2, "w": 577, "w+": 578, "a": 1089, "a+": 1090 }[str]; if (typeof flags == "undefined") throw new Error(`Unknown file open mode: ${str}`); return flags; }; var FS_fileDataToTypedArray = (data) => { if (typeof data == "string") data = intArrayFromString(data, true); if (!data.subarray) data = new Uint8Array(data); return data; }; var FS_getMode = (canRead, canWrite) => { var mode = 0; if (canRead) mode |= 365; if (canWrite) mode |= 146; return mode; }; var asyncLoad = async (url) => { var arrayBuffer = await readAsync(url); return new Uint8Array(arrayBuffer); }; var FS_createDataFile = (...args) => FS.createDataFile(...args); var getUniqueRunDependency = (id) => { return id; }; var runDependencies = 0; var dependenciesFulfilled = null; var removeRunDependency = (id) => { runDependencies--; Module["monitorRunDependencies"]?.(runDependencies); if (runDependencies == 0) { if (dependenciesFulfilled) { var callback = dependenciesFulfilled; dependenciesFulfilled = null; callback(); } } }; var addRunDependency = (id) => { runDependencies++; Module["monitorRunDependencies"]?.(runDependencies); }; var preloadPlugins = []; var FS_handledByPreloadPlugin = async (byteArray, fullname) => { if (typeof Browser != "undefined") Browser.init(); for (var plugin of preloadPlugins) if (plugin["canHandle"](fullname)) return plugin["handle"](byteArray, fullname); return byteArray; }; var FS_preloadFile = async (parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; var dep = getUniqueRunDependency(`cp ${fullname}`); addRunDependency(dep); try { var byteArray = url; if (typeof url == "string") byteArray = await asyncLoad(url); byteArray = await FS_handledByPreloadPlugin(byteArray, fullname); preFinish?.(); if (!dontCreateFile) FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); } finally { removeRunDependency(dep); } }; var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => { FS_preloadFile(parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish).then(onload).catch(onerror); }; var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: "/", initialized: false, ignorePermissions: true, filesystems: null, syncFSRequests: 0, ErrnoError: class { name = "ErrnoError"; constructor(errno) { this.errno = errno; } }, FSStream: class { shared = {}; get object() { return this.node; } set object(val) { this.node = val; } get isRead() { return (this.flags & 2097155) !== 1; } get isWrite() { return (this.flags & 2097155) !== 0; } get isAppend() { return this.flags & 1024; } get flags() { return this.shared.flags; } set flags(val) { this.shared.flags = val; } get position() { return this.shared.position; } set position(val) { this.shared.position = val; } }, FSNode: class { node_ops = {}; stream_ops = {}; readMode = 365; writeMode = 146; mounted = null; constructor(parent, name, mode, rdev) { if (!parent) parent = this; this.parent = parent; this.mount = parent.mount; this.id = FS.nextInode++; this.name = name; this.mode = mode; this.rdev = rdev; this.atime = this.mtime = this.ctime = Date.now(); } get read() { return (this.mode & this.readMode) === this.readMode; } set read(val) { val ? this.mode |= this.readMode : this.mode &= ~this.readMode; } get write() { return (this.mode & this.writeMode) === this.writeMode; } set write(val) { val ? this.mode |= this.writeMode : this.mode &= ~this.writeMode; } get isFolder() { return FS.isDir(this.mode); } get isDevice() { return FS.isChrdev(this.mode); } }, lookupPath(path, opts = {}) { if (!path) throw new FS.ErrnoError(44); opts.follow_mount ??= true; if (!PATH.isAbs(path)) path = FS.cwd() + "/" + path; linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { var parts = path.split("/").filter((p) => !!p); var current = FS.root; var current_path = "/"; for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1; if (islast && opts.parent) break; if (parts[i] === ".") continue; if (parts[i] === "..") { current_path = PATH.dirname(current_path); if (FS.isRoot(current)) { path = current_path + "/" + parts.slice(i + 1).join("/"); nlinks--; continue linkloop; } else current = current.parent; continue; } current_path = PATH.join2(current_path, parts[i]); try { current = FS.lookupNode(current, parts[i]); } catch (e) { if (e?.errno === 44 && islast && opts.noent_okay) return { path: current_path }; throw e; } if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) current = current.mounted.root; if (FS.isLink(current.mode) && (!islast || opts.follow)) { if (!current.node_ops.readlink) throw new FS.ErrnoError(52); var link = current.node_ops.readlink(current); if (!PATH.isAbs(link)) link = PATH.dirname(current_path) + "/" + link; path = link + "/" + parts.slice(i + 1).join("/"); continue linkloop; } } return { path: current_path, node: current }; } throw new FS.ErrnoError(32); }, getPath(node) { var path; while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint; if (!path) return mount; return mount[mount.length - 1] !== "/" ? `${mount}/${path}` : mount + path; } path = path ? `${node.name}/${path}` : node.name; node = node.parent; } }, hashName(parentid, name) { var hash = 0; for (var i = 0; i < name.length; i++) hash = (hash << 5) - hash + name.charCodeAt(i) | 0; return (parentid + hash >>> 0) % FS.nameTable.length; }, hashAddNode(node) { var hash = FS.hashName(node.parent.id, node.name); node.name_next = FS.nameTable[hash]; FS.nameTable[hash] = node; }, hashRemoveNode(node) { var hash = FS.hashName(node.parent.id, node.name); if (FS.nameTable[hash] === node) FS.nameTable[hash] = node.name_next; else { var current = FS.nameTable[hash]; while (current) { if (current.name_next === node) { current.name_next = node.name_next; break; } current = current.name_next; } } }, lookupNode(parent, name) { var errCode = FS.mayLookup(parent); if (errCode) throw new FS.ErrnoError(errCode); var hash = FS.hashName(parent.id, name); for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name; if (node.parent.id === parent.id && nodeName === name) return node; } return FS.lookup(parent, name); }, createNode(parent, name, mode, rdev) { var node = new FS.FSNode(parent, name, mode, rdev); FS.hashAddNode(node); return node; }, destroyNode(node) { FS.hashRemoveNode(node); }, isRoot(node) { return node === node.parent; }, isMountpoint(node) { return !!node.mounted; }, isFile(mode) { return (mode & 61440) === 32768; }, isDir(mode) { return (mode & 61440) === 16384; }, isLink(mode) { return (mode & 61440) === 40960; }, isChrdev(mode) { return (mode & 61440) === 8192; }, isBlkdev(mode) { return (mode & 61440) === 24576; }, isFIFO(mode) { return (mode & 61440) === 4096; }, isSocket(mode) { return (mode & 49152) === 49152; }, flagsToPermissionString(flag) { var perms = [ "r", "w", "rw" ][flag & 3]; if (flag & 512) perms += "w"; return perms; }, nodePermissions(node, perms) { if (FS.ignorePermissions) return 0; if (perms.includes("r") && !(node.mode & 292)) return 2; if (perms.includes("w") && !(node.mode & 146)) return 2; if (perms.includes("x") && !(node.mode & 73)) return 2; return 0; }, mayLookup(dir) { if (!FS.isDir(dir.mode)) return 54; var errCode = FS.nodePermissions(dir, "x"); if (errCode) return errCode; if (!dir.node_ops.lookup) return 2; return 0; }, mayCreate(dir, name) { if (!FS.isDir(dir.mode)) return 54; try { FS.lookupNode(dir, name); return 20; } catch (e) {} return FS.nodePermissions(dir, "wx"); }, mayDelete(dir, name, isdir) { var node; try { node = FS.lookupNode(dir, name); } catch (e) { return e.errno; } var errCode = FS.nodePermissions(dir, "wx"); if (errCode) return errCode; if (isdir) { if (!FS.isDir(node.mode)) return 54; if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) return 10; } else if (FS.isDir(node.mode)) return 31; return 0; }, mayOpen(node, flags) { if (!node) return 44; if (FS.isLink(node.mode)) return 32; var mode = FS.flagsToPermissionString(flags); if (FS.isDir(node.mode)) { if (mode !== "r" || flags & 576) return 31; } return FS.nodePermissions(node, mode); }, checkOpExists(op, err) { if (!op) throw new FS.ErrnoError(err); return op; }, MAX_OPEN_FDS: 4096, nextfd() { for (var fd = 0; fd <= FS.MAX_OPEN_FDS; fd++) if (!FS.streams[fd]) return fd; throw new FS.ErrnoError(33); }, getStreamChecked(fd) { var stream = FS.getStream(fd); if (!stream) throw new FS.ErrnoError(8); return stream; }, getStream: (fd) => FS.streams[fd], createStream(stream, fd = -1) { stream = Object.assign(new FS.FSStream(), stream); if (fd == -1) fd = FS.nextfd(); stream.fd = fd; FS.streams[fd] = stream; return stream; }, closeStream(fd) { FS.streams[fd] = null; }, dupStream(origStream, fd = -1) { var stream = FS.createStream(origStream, fd); stream.stream_ops?.dup?.(stream); return stream; }, doSetAttr(stream, node, attr) { var setattr = stream?.stream_ops.setattr; var arg = setattr ? stream : node; setattr ??= node.node_ops.setattr; FS.checkOpExists(setattr, 63); setattr(arg, attr); }, chrdev_stream_ops: { open(stream) { stream.stream_ops = FS.getDevice(stream.node.rdev).stream_ops; stream.stream_ops.open?.(stream); }, llseek() { throw new FS.ErrnoError(70); } }, major: (dev) => dev >> 8, minor: (dev) => dev & 255, makedev: (ma, mi) => ma << 8 | mi, registerDevice(dev, ops) { FS.devices[dev] = { stream_ops: ops }; }, getDevice: (dev) => FS.devices[dev], getMounts(mount) { var mounts = []; var check = [mount]; while (check.length) { var m = check.pop(); mounts.push(m); check.push(...m.mounts); } return mounts; }, syncfs(populate, callback) { if (typeof populate == "function") { callback = populate; populate = false; } FS.syncFSRequests++; if (FS.syncFSRequests > 1) err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`); var mounts = FS.getMounts(FS.root.mount); var completed = 0; function doCallback(errCode) { FS.syncFSRequests--; return callback(errCode); } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true; return doCallback(errCode); } return; } if (++completed >= mounts.length) doCallback(null); } for (var mount of mounts) if (mount.type.syncfs) mount.type.syncfs(mount, populate, done); else done(null); }, mount(type, opts, mountpoint) { var root = mountpoint === "/"; var pseudo = !mountpoint; var node; if (root && FS.root) throw new FS.ErrnoError(10); else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); mountpoint = lookup.path; node = lookup.node; if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); if (!FS.isDir(node.mode)) throw new FS.ErrnoError(54); } var mount = { type, opts, mountpoint, mounts: [] }; var mountRoot = type.mount(mount); mountRoot.mount = mount; mount.root = mountRoot; if (root) FS.root = mountRoot; else if (node) { node.mounted = mount; if (node.mount) node.mount.mounts.push(mount); } return mountRoot; }, unmount(mountpoint) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); if (!FS.isMountpoint(lookup.node)) throw new FS.ErrnoError(28); var node = lookup.node; var mount = node.mounted; var mounts = FS.getMounts(mount); for (var [hash, current] of Object.entries(FS.nameTable)) while (current) { var next = current.name_next; if (mounts.includes(current.mount)) FS.destroyNode(current); current = next; } node.mounted = null; var idx = node.mount.mounts.indexOf(mount); node.mount.mounts.splice(idx, 1); }, lookup(parent, name) { return parent.node_ops.lookup(parent, name); }, mknod(path, mode, dev) { var parent = FS.lookupPath(path, { parent: true }).node; var name = PATH.basename(path); if (!name) throw new FS.ErrnoError(28); if (name === "." || name === "..") throw new FS.ErrnoError(20); var errCode = FS.mayCreate(parent, name); if (errCode) throw new FS.ErrnoError(errCode); if (!parent.node_ops.mknod) throw new FS.ErrnoError(63); return parent.node_ops.mknod(parent, name, mode, dev); }, statfs(path) { return FS.statfsNode(FS.lookupPath(path, { follow: true }).node); }, statfsStream(stream) { return FS.statfsNode(stream.node); }, statfsNode(node) { var rtn = { bsize: 4096, frsize: 4096, blocks: 1e6, bfree: 5e5, bavail: 5e5, files: FS.nextInode, ffree: FS.nextInode - 1, fsid: 42, flags: 2, namelen: 255 }; if (node.node_ops.statfs) Object.assign(rtn, node.node_ops.statfs(node.mount.opts.root)); return rtn; }, create(path, mode = 438) { mode &= 4095; mode |= 32768; return FS.mknod(path, mode, 0); }, mkdir(path, mode = 511) { mode &= 1023; mode |= 16384; return FS.mknod(path, mode, 0); }, mkdirTree(path, mode) { var dirs = path.split("/"); var d = ""; for (var dir of dirs) { if (!dir) continue; if (d || PATH.isAbs(path)) d += "/"; d += dir; try { FS.mkdir(d, mode); } catch (e) { if (e.errno != 20) throw e; } } }, mkdev(path, mode, dev) { if (typeof dev == "undefined") { dev = mode; mode = 438; } mode |= 8192; return FS.mknod(path, mode, dev); }, symlink(oldpath, newpath) { if (!PATH_FS.resolve(oldpath)) throw new FS.ErrnoError(44); var parent = FS.lookupPath(newpath, { parent: true }).node; if (!parent) throw new FS.ErrnoError(44); var newname = PATH.basename(newpath); var errCode = FS.mayCreate(parent, newname); if (errCode) throw new FS.ErrnoError(errCode); if (!parent.node_ops.symlink) throw new FS.ErrnoError(63); return parent.node_ops.symlink(parent, newname, oldpath); }, rename(old_path, new_path) { var old_dirname = PATH.dirname(old_path); var new_dirname = PATH.dirname(new_path); var old_name = PATH.basename(old_path); var new_name = PATH.basename(new_path); var lookup = FS.lookupPath(old_path, { parent: true }), old_dir = lookup.node, new_dir; lookup = FS.lookupPath(new_path, { parent: true }); new_dir = lookup.node; if (!old_dir || !new_dir) throw new FS.ErrnoError(44); if (old_dir.mount !== new_dir.mount) throw new FS.ErrnoError(75); var old_node = FS.lookupNode(old_dir, old_name); var relative = PATH_FS.relative(old_path, new_dirname); if (relative.charAt(0) !== ".") throw new FS.ErrnoError(28); relative = PATH_FS.relative(new_path, old_dirname); if (relative.charAt(0) !== ".") throw new FS.ErrnoError(55); var new_node; try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) {} if (old_node === new_node) return; var isdir = FS.isDir(old_node.mode); var errCode = FS.mayDelete(old_dir, old_name, isdir); if (errCode) throw new FS.ErrnoError(errCode); errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name); if (errCode) throw new FS.ErrnoError(errCode); if (!old_dir.node_ops.rename) throw new FS.ErrnoError(63); if (FS.isMountpoint(old_node) || new_node && FS.isMountpoint(new_node)) throw new FS.ErrnoError(10); if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, "w"); if (errCode) throw new FS.ErrnoError(errCode); } FS.hashRemoveNode(old_node); try { old_dir.node_ops.rename(old_node, new_dir, new_name); old_node.parent = new_dir; } catch (e) { throw e; } finally { FS.hashAddNode(old_node); } }, rmdir(path) { var parent = FS.lookupPath(path, { parent: true }).node; var name = PATH.basename(path); var node = FS.lookupNode(parent, name); var errCode = FS.mayDelete(parent, name, true); if (errCode) throw new FS.ErrnoError(errCode); if (!parent.node_ops.rmdir) throw new FS.ErrnoError(63); if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); parent.node_ops.rmdir(parent, name); FS.destroyNode(node); }, readdir(path) { var node = FS.lookupPath(path, { follow: true }).node; return FS.checkOpExists(node.node_ops.readdir, 54)(node); }, unlink(path) { var parent = FS.lookupPath(path, { parent: true }).node; if (!parent) throw new FS.ErrnoError(44); var name = PATH.basename(path); var node = FS.lookupNode(parent, name); var errCode = FS.mayDelete(parent, name, false); if (errCode) throw new FS.ErrnoError(errCode); if (!parent.node_ops.unlink) throw new FS.ErrnoError(63); if (FS.isMountpoint(node)) throw new FS.ErrnoError(10); parent.node_ops.unlink(parent, name); FS.destroyNode(node); }, readlink(path) { var link = FS.lookupPath(path).node; if (!link) throw new FS.ErrnoError(44); if (!link.node_ops.readlink) throw new FS.ErrnoError(28); return link.node_ops.readlink(link); }, stat(path, dontFollow) { var node = FS.lookupPath(path, { follow: !dontFollow }).node; return FS.checkOpExists(node.node_ops.getattr, 63)(node); }, fstat(fd) { var stream = FS.getStreamChecked(fd); var node = stream.node; var getattr = stream.stream_ops.getattr; var arg = getattr ? stream : node; getattr ??= node.node_ops.getattr; FS.checkOpExists(getattr, 63); return getattr(arg); }, lstat(path) { return FS.stat(path, true); }, doChmod(stream, node, mode, dontFollow) { FS.doSetAttr(stream, node, { mode: mode & 4095 | node.mode & -4096, ctime: Date.now(), dontFollow }); }, chmod(path, mode, dontFollow) { var node; if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; else node = path; FS.doChmod(null, node, mode, dontFollow); }, lchmod(path, mode) { FS.chmod(path, mode, true); }, fchmod(fd, mode) { var stream = FS.getStreamChecked(fd); FS.doChmod(stream, stream.node, mode, false); }, doChown(stream, node, dontFollow) { FS.doSetAttr(stream, node, { timestamp: Date.now(), dontFollow }); }, chown(path, uid, gid, dontFollow) { var node; if (typeof path == "string") node = FS.lookupPath(path, { follow: !dontFollow }).node; else node = path; FS.doChown(null, node, dontFollow); }, lchown(path, uid, gid) { FS.chown(path, uid, gid, true); }, fchown(fd, uid, gid) { var stream = FS.getStreamChecked(fd); FS.doChown(stream, stream.node, false); }, doTruncate(stream, node, len) { if (FS.isDir(node.mode)) throw new FS.ErrnoError(31); if (!FS.isFile(node.mode)) throw new FS.ErrnoError(28); var errCode = FS.nodePermissions(node, "w"); if (errCode) throw new FS.ErrnoError(errCode); FS.doSetAttr(stream, node, { size: len, timestamp: Date.now() }); }, truncate(path, len) { if (len < 0) throw new FS.ErrnoError(28); var node; if (typeof path == "string") node = FS.lookupPath(path, { follow: true }).node; else node = path; FS.doTruncate(null, node, len); }, ftruncate(fd, len) { var stream = FS.getStreamChecked(fd); if (len < 0 || (stream.flags & 2097155) === 0) throw new FS.ErrnoError(28); FS.doTruncate(stream, stream.node, len); }, utime(path, atime, mtime) { var node = FS.lookupPath(path, { follow: true }).node; FS.checkOpExists(node.node_ops.setattr, 63)(node, { atime, mtime }); }, open(path, flags, mode = 438) { if (path === "") throw new FS.ErrnoError(44); flags = FS_modeStringToFlags(flags); if (flags & 64) mode = mode & 4095 | 32768; else mode = 0; var node; var isDirPath; if (typeof path == "object") node = path; else { isDirPath = path.endsWith("/"); var lookup = FS.lookupPath(path, { follow: !(flags & 131072), noent_okay: true }); node = lookup.node; path = lookup.path; } var created = false; if (flags & 64) if (node) { if (flags & 128) throw new FS.ErrnoError(20); } else if (isDirPath) throw new FS.ErrnoError(31); else { node = FS.mknod(path, mode | 511, 0); created = true; } if (!node) throw new FS.ErrnoError(44); if (FS.isChrdev(node.mode)) flags &= -513; if (flags & 65536 && !FS.isDir(node.mode)) throw new FS.ErrnoError(54); if (!created) { var errCode = FS.mayOpen(node, flags); if (errCode) throw new FS.ErrnoError(errCode); } if (flags & 512 && !created) FS.truncate(node, 0); flags &= -131713; var stream = FS.createStream({ node, path: FS.getPath(node), flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false }); if (stream.stream_ops.open) stream.stream_ops.open(stream); if (created) FS.chmod(node, mode & 511); return stream; }, close(stream) { if (FS.isClosed(stream)) throw new FS.ErrnoError(8); if (stream.getdents) stream.getdents = null; try { if (stream.stream_ops.close) stream.stream_ops.close(stream); } catch (e) { throw e; } finally { FS.closeStream(stream.fd); } stream.fd = null; }, isClosed(stream) { return stream.fd === null; }, llseek(stream, offset, whence) { if (FS.isClosed(stream)) throw new FS.ErrnoError(8); if (!stream.seekable || !stream.stream_ops.llseek) throw new FS.ErrnoError(70); if (whence != 0 && whence != 1 && whence != 2) throw new FS.ErrnoError(28); stream.position = stream.stream_ops.llseek(stream, offset, whence); stream.ungotten = []; return stream.position; }, read