diff --git a/Dockerfile b/Dockerfile index ad2c48a..1d30f10 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -ARG EMSDK_VERSION=5.0.5 +ARG EMSDK_VERSION=6.0.13 FROM emscripten/emsdk:${EMSDK_VERSION} AS wasm-tools # The SDK already supplies the build dependencies. SQLite's release build diff --git a/scripts/build-sqlite3-wasm.sh b/scripts/build-sqlite3-wasm.sh index 968245d..227d043 100644 --- a/scripts/build-sqlite3-wasm.sh +++ b/scripts/build-sqlite3-wasm.sh @@ -106,8 +106,56 @@ make sqlite3.c cd ext/wasm -log "Running make -j4 npm (ext/wasm)" -make -j4 npm +# SQLite invokes wasm-opt with --all-features. Newer Binaryen versions then +# emit compact imports, which are not supported by our browser/Node targets. +# Append the compatibility flag after SQLite's flags, preserving its passes. +WASM_OPT_WRAPPER="$(mktemp /tmp/sqlite-wasm-opt.XXXXXX)" +printf '#!/usr/bin/env bash\nexec %q "$@" --disable-compact-imports\n' \ + "$(command -v wasm-opt)" > "$WASM_OPT_WRAPPER" +chmod +x "$WASM_OPT_WRAPPER" + +log "Running make -j4 npm with Cross-Origin Storage for the bundler-friendly browser loader" +make -j4 npm 'emcc.flags.bundler=-sCROSS_ORIGIN_STORAGE' "bin.wasm-opt=$WASM_OPT_WRAPPER" + +# SQLite strips the Wasm after emcc embeds its hash. Update only the COS +# loader's hash to describe the final, shared binary before packaging it. +log "Updating and verifying the COS hash after Wasm post-processing" +python3 - <<'PY' +import hashlib +from pathlib import Path +import re +import zipfile + +wasm = Path('jswasm/sqlite3.wasm') +loader = Path('jswasm/sqlite3-bundler-friendly.mjs') +digest = hashlib.sha256(wasm.read_bytes()).hexdigest() +pattern = re.compile( + r"(Module\['wasmHash'\]\s*=\s*\{\s*algorithm:\s*'SHA-256',\s*value:\s*')" + r"[0-9a-f]{64}('\s*\})" +) +source, count = pattern.subn(lambda match: match[1] + digest + match[2], loader.read_text()) +if count != 1: + raise SystemExit(f'Expected exactly one COS hash in {loader}, found {count}') +loader.write_text(source) + +# The npm target has already created the archive. Replace its loader so the +# extracted artifacts and archived binary have the same verified hash. +with zipfile.ZipFile('npm-bundle.zip') as archive: + entries = [(info, archive.read(info)) for info in archive.infolist()] +with zipfile.ZipFile('npm-bundle.zip', 'w') as archive: + for info, data in entries: + if info.filename == loader.as_posix(): + data = loader.read_bytes() + archive.writestr(info, data) +with zipfile.ZipFile('npm-bundle.zip') as archive: + packaged_loader = archive.read(loader.as_posix()).decode() + packaged_digest = hashlib.sha256(archive.read(wasm.as_posix())).hexdigest() + if packaged_digest != digest or not pattern.search(packaged_loader): + raise SystemExit('Packaged COS loader or Wasm hash verification failed') + if pattern.search(packaged_loader)[0] != pattern.search(source)[0]: + raise SystemExit('Packaged COS hash differs from the final Wasm hash') +print(f'COS SHA-256: {digest}') +PY # ensure artifact exists if [ ! -f npm-bundle.zip ]; then diff --git a/src/bin/sqlite3-bundler-friendly.mjs b/src/bin/sqlite3-bundler-friendly.mjs index c55b1ed..b8656d4 100644 --- a/src/bin/sqlite3-bundler-friendly.mjs +++ b/src/bin/sqlite3-bundler-friendly.mjs @@ -31,7 +31,7 @@ ** SQLITE_VERSION_NUMBER 3054001 ** SQLITE_SOURCE_ID "2026-10-16 14:45:20 555e07ef33f69a0957d24d8e555b702464a131d6f1607f4c3c1e4551e8e6ca76" ** -** Emscripten SDK: 5.0.5 +** Emscripten SDK: 6.0.13 */ // This code implements the `-sMODULARIZE` settings by taking the generated // JS program code (INNER_JS_CODE) and wrapping it in a factory function. @@ -39,8 +39,7 @@ // When targeting node and ES6 we use `await import ..` in the generated code // so the outer function needs to be marked as async. async function sqlite3InitModule(moduleArg = {}) { - var moduleRtn; - + var Module = moduleArg; // include: shell.js // include: minimum_runtime_check.js // end include: minimum_runtime_check.js @@ -57,7 +56,6 @@ async function sqlite3InitModule(moduleArg = {}) { // after the generated code, you will need to define var Module = {}; // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. -var Module = moduleArg; // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). @@ -98,7 +96,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR // end include: ./bld/pre-js.bundler.js -var arguments_ = []; +var programArgs = []; var thisProgram = './this.program'; var quit_ = (status, toThrow) => { throw toThrow; @@ -208,8 +206,6 @@ function assert(condition, text) { var isFileURI = (filename) => filename.startsWith('file://'); // include: runtime_common.js -// include: runtime_stack_check.js -// end include: runtime_stack_check.js // include: runtime_exceptions.js // Base Emscripten EH error class class EmscriptenEH {} @@ -219,34 +215,39 @@ class EmscriptenSjLj extends EmscriptenEH {} // end include: runtime_exceptions.js // include: runtime_debug.js // end include: runtime_debug.js -var readyPromiseResolve, readyPromiseReject; - // Memory management var runtimeInitialized = false; +// When ALLOW_MEMORY_GROWTH is enabled, the conversion from Wasm +// memory to ArrayBuffer requires some additional logic. +function getMemoryBuffer() { + return wasmMemory.buffer; +} + function updateMemoryViews() { - var b = wasmMemory.buffer; + // If we already have a heap that is resizeable/growable buffer we don't + // need to do anything in updateMemoryViews. + if (HEAP8?.buffer?.resizable) return; + var b = getMemoryBuffer(); 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); + } // In non-standalone/normal mode, we create the memory here. // include: runtime_init_memory.js // Create the wasm memory. (Note: this only applies if IMPORTED_MEMORY is defined) -// check for full engine support (use string 'subarray' to avoid closure compiler confusion) - function initMemory() { @@ -278,11 +279,10 @@ function initMemory() { // end include: memoryprofiler.js // end include: runtime_common.js function preRun() { - if (Module['preRun']) { - if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; - while (Module['preRun'].length) { - addOnPreRun(Module['preRun'].shift()); - } + var preRun = Module['preRun']; + if (preRun) { + if (typeof preRun === 'function') preRun = [preRun]; + onPreRuns.push(...preRun); } // Begin ATPRERUNS hooks callRuntimeCallbacks(onPreRuns); @@ -302,16 +302,15 @@ TTY.init(); // Begin ATPOSTCTORS hooks FS.ignorePermissions = false; // End ATPOSTCTORS hooks + } function postRun() { - // PThreads reuse the runtime from the main thread. - if (Module['postRun']) { - if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; - while (Module['postRun'].length) { - addOnPostRun(Module['postRun'].shift()); - } + var postRun = Module['postRun']; + if (postRun) { + if (typeof postRun === 'function') postRun = [postRun]; + onPostRuns.push(...postRun); } // Begin ATPOSTRUNS hooks @@ -321,7 +320,6 @@ function postRun() { /** * @param {string|number=} what - * @noreturn */ function abort(what) { Module['onAbort']?.(what); @@ -343,19 +341,7 @@ function abort(what) { // a trap or not based on a hidden field within the object. So at the moment // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that // allows this in the wasm spec. - - // Suppress closure compiler warning here. Closure compiler's builtin extern - // definition for WebAssembly.RuntimeError claims it takes no arguments even - // though it can. - // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. - /** @suppress {checkTypes} */ - var e = new WebAssembly.RuntimeError(what); - - readyPromiseReject?.(e); - // Throw the error whether or not MODULARIZE is set because abort is used - // in code paths apart from instantiation where an exception is expected - // to be thrown when abort is called. - throw e; + throw new WebAssembly.RuntimeError(what); } var wasmBinaryFile; @@ -372,9 +358,6 @@ function findWasmBinary() { } function getBinarySync(file) { - if (file == wasmBinaryFile && wasmBinary) { - return new Uint8Array(wasmBinary); - } if (readBinary) { return readBinary(file); } @@ -411,7 +394,68 @@ async function instantiateArrayBuffer(binaryFile, imports) { } } +// Stream Wasm bytes into the compiler. A fixed `application/wasm` type is +// used rather than any server-supplied MIME type: on a cache hit the bytes +// were hash-verified when written into COS, and on a cache miss the store +// branch is already consuming the body, so the standard path's re-download +// fallback for a bad MIME type is not available. A wrong file still fails +// compilation and falls through to the standard path. +function cosInstantiateStream(stream, imports) { + var response = new Response(stream, { headers: { 'Content-Type': 'application/wasm' } }); + return WebAssembly.instantiateStreaming(response, imports); +} + async function instantiateAsync(binary, binaryFile, imports) { + // Cross-Origin Storage (COS) progressive enhancement. + // https://github.com/WICG/cross-origin-storage + // Any error (not found, not allowed, network failure, …) falls through + // to the standard Emscripten streaming path so the page always loads. + if (globalThis.navigator?.crossOriginStorage?.getFileHandle) { + var cosHash = Module['wasmHash']; + try { + var cosHandle = await navigator.crossOriginStorage.getFileHandle(cosHash); + // Cache hit. getFile() resolves to a lazy File reference; no bytes are + // read until the stream is consumed by the compiler below. + var cosFile = await cosHandle.getFile(); + return cosInstantiateStream(cosFile.stream(), imports); + } catch { + // Any error (not found, not allowed, …) — fetch from the network and + // attempt to store in COS for future page loads. + try { + var networkResponse = await fetch(binaryFile, { credentials: 'same-origin' }); + if (!networkResponse.ok) { + throw new Error(`HTTP ${networkResponse.status}`); + } + // Split the body so that one branch feeds streaming compilation while + // the other is written into COS in the background. This keeps the + // download/compile overlap of the standard instantiateStreaming() + // path instead of waiting for the whole file to arrive first. + var [compileStream, storeStream] = networkResponse.body.tee(); + // Fire-and-forget store; never block instantiation on the write. + // pipeTo() closes the writable, and COS verifies the hash on close. + (async () => { + try { + var writeHandle = await navigator.crossOriginStorage.getFileHandle( + cosHash, + { create: true, origins: '*' }, + ); + var writable = await writeHandle.createWritable(); + await storeStream.pipeTo(writable); + } catch (storeErr) { + err(`COS store failed: ${storeErr}`); + // Release the tee branch so the body is not buffered indefinitely + // on behalf of a writer that will never consume it. + storeStream.cancel().catch(() => {}); + } + })(); + return cosInstantiateStream(compileStream, imports); + } catch (fetchErr) { + // Network fetch failed; fall through to the standard path below. + err(`COS fallback fetch failed: ${fetchErr}`); + } + // Fall through to the standard streaming path below. + } + } if (!binary ) { try { @@ -444,8 +488,7 @@ async function createWasm() { // Load the wasm module and create an instance of using native support in the JS engine. // handle a generated wasm instance, receiving its exports and // performing other necessary setup - /** @param {WebAssembly.Module=} module*/ - function receiveInstance(instance, module) { + function receiveInstance(instance) { wasmExports = instance.exports; assignWasmExports(wasmExports); @@ -464,17 +507,20 @@ async function createWasm() { var info = getWasmImports(); + // Expose the build-time hash so that custom Module['instantiateWasm'] + // callbacks can implement their own COS-aware loading path. + Module['wasmHash'] = { algorithm: 'SHA-256', value: 'ceb5f93d05868430e1eabb2633ea1c1aceb2bf4e6e0ad1d82e3244e437310843' }; + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback // to manually instantiate the Wasm module themselves. This allows pages to // run the instantiation parallel to any other async startup actions they are // performing. // Also pthreads and wasm workers initialize the wasm instance through this // path. - if (Module['instantiateWasm']) { - return new Promise((resolve, reject) => { - Module['instantiateWasm'](info, (inst, mod) => { - resolve(receiveInstance(inst, mod)); - }); + var instantiateWasm = Module['instantiateWasm']; + if (instantiateWasm) { + return new Promise((resolve) => { + instantiateWasm(info, (inst) => resolve(receiveInstance(inst))); }); } @@ -497,36 +543,9 @@ async function createWasm() { } } - /** @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) { // Pass the module as the first argument. @@ -540,49 +559,8 @@ async function createWasm() { var addOnPreRun = (cb) => onPreRuns.push(cb); - - /** - * @param {number} ptr - * @param {string} type - */ - function getValue(ptr, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': return HEAP8[ptr]; - case 'i8': return HEAP8[ptr]; - case 'i16': return HEAP16[((ptr)>>1)]; - case 'i32': return HEAP32[((ptr)>>2)]; - case 'i64': return HEAP64[((ptr)>>3)]; - case 'float': return HEAPF32[((ptr)>>2)]; - case 'double': return HEAPF64[((ptr)>>3)]; - case '*': return HEAPU32[((ptr)>>2)]; - default: abort(`invalid type for getValue: ${type}`); - } - } - var noExitRuntime = true; - - /** - * @param {number} ptr - * @param {number} value - * @param {string} type - */ - function setValue(ptr, value, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': HEAP8[ptr] = value; break; - case 'i8': HEAP8[ptr] = value; break; - case 'i16': HEAP16[((ptr)>>1)] = value; break; - case 'i32': HEAP32[((ptr)>>2)] = value; break; - case 'i64': HEAP64[((ptr)>>3)] = BigInt(value); break; - case 'float': HEAPF32[((ptr)>>2)] = value; break; - case 'double': HEAPF64[((ptr)>>3)] = value; break; - case '*': HEAPU32[((ptr)>>2)] = value; break; - default: abort(`invalid type for setValue: ${type}`); - } - } - var stackRestore = (val) => __emscripten_stack_restore(val); var stackSave = () => _emscripten_stack_get_current(); @@ -665,7 +643,7 @@ resolve:(...args) => { for (var i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = (i >= 0) ? args[i] : FS.cwd(); // Skip empty and invalid entries - if (typeof path != 'string') { + if (typeof path !== 'string') { throw new TypeError('Arguments to path.resolve must be strings'); } else if (!path) { return ''; // an invalid portion invalidates the whole thing @@ -715,18 +693,26 @@ relative:(from, to) => { var UTF8Decoder = new TextDecoder(); -var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { - var maxIdx = idx + maxBytesToRead; - if (ignoreNul) return maxIdx; - // TextDecoder needs to know the byte length in advance, it doesn't stop on - // null terminator by itself. - // As a tiny code save trick, compare idx against maxIdx using a negation, - // so that maxBytesToRead=undefined/NaN means Infinity. - while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; - return idx; - }; /** + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul + * @return {number} + */ + var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + var maxIdx = idx + maxBytesToRead; + if (ignoreNul) return maxIdx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. + // As a tiny code save trick, compare idx against maxIdx using a negation, + // so that maxBytesToRead=undefined/NaN means Infinity. + 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. @@ -887,12 +873,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } catch (e) { throw new FS.ErrnoError(29); } - if (result === undefined && bytesRead === 0) { + if (result === undefined && !bytesRead) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; bytesRead++; buffer[offset+i] = result; + // We currently only support canonical mode (ICANON), where + // read(2) returns as soon as a line delimiter is read. + if (result === 10) break; } if (bytesRead) { stream.node.atime = Date.now(); @@ -975,6 +964,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; + /** @type {!Uint8Array} */ + var HEAPU8; var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size); var alignMemory = (size, alignment) => { @@ -986,12 +977,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 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) { + createNode(parent, name, mode, dev = undefined) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { // not supported throw new FS.ErrnoError(63); @@ -1084,7 +1076,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // For small filesizes (<1MB), perform size*2 geometric increase, but for // large sizes, do a much more conservative size*1.125 increase to avoid // overshooting the allocation cap by a very large margin. - var CAPACITY_DOUBLING_MAX = 1024 * 1024; + const CAPACITY_DOUBLING_MAX = 1024 * 1024; newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); if (prevCapacity) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. var oldContents = MEMFS.getFileDataAsTypedArray(node); @@ -1128,7 +1120,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return attr; }, setattr(node, attr) { - for (const key of ["mode", "atime", "mtime", "ctime"]) { + for (const key of ['mode', 'atime', 'mtime', 'ctime']) { if (attr[key] != null) { node[key] = attr[key]; } @@ -1147,7 +1139,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } throw MEMFS.doesNotExistError; }, - mknod(parent, name, mode, dev) { + mknod(parent, name, mode, dev = undefined) { return MEMFS.createNode(parent, name, mode, dev); }, rename(old_node, new_dir, new_name) { @@ -1155,14 +1147,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) {} - if (new_node) { - if (FS.isDir(old_node.mode)) { - // if we're overwriting a directory at new_name, make sure it's empty. - for (var i in new_node.contents) { - throw new FS.ErrnoError(55); - } + if (new_node && FS.isDir(old_node.mode)) { + // if we're overwriting a directory at new_name, make sure it's empty. + for (var i in new_node.contents) { + throw new FS.ErrnoError(55); } - FS.hashRemoveNode(new_node); } // do the internal rewiring delete old_node.parent.contents[old_node.name]; @@ -1221,7 +1210,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (canOwn) { node.contents = buffer.subarray(offset, offset + length); node.usedBytes = length; - } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + } else if (!node.usedBytes && !position) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. node.contents = buffer.slice(offset, offset + length); node.usedBytes = length; } else { @@ -1288,7 +1277,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; var FS_modeStringToFlags = (str) => { - if (typeof str != 'string') return str; + if (typeof str !== 'string') return str; var flagModes = { 'r': 0, 'r+': 2, @@ -1298,14 +1287,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 'a+': 1024 | 64 | 2, }; var flags = flagModes[str]; - if (typeof flags == 'undefined') { + if (typeof flags === 'undefined') { throw new Error(`Unknown file open mode: ${str}`); } return flags; }; var FS_fileDataToTypedArray = (data) => { - if (typeof data == 'string') { + if (typeof data === 'string') { data = intArrayFromString(data, true); } if (!data.subarray) { @@ -1334,24 +1323,27 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return id; }; + var dependenciesPromise = null; + var resolveRunDependencies = async () => dependenciesPromise; var runDependencies = 0; - var dependenciesFulfilled = null; + var dependenciesPromiseResolve = null; var removeRunDependency = (id) => { runDependencies--; Module['monitorRunDependencies']?.(runDependencies); - if (runDependencies == 0) { - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); // can add another dependenciesFulfilled - } + if (!runDependencies) { + dependenciesPromiseResolve(); } }; + + var addRunDependency = (id) => { + if (!runDependencies) { + dependenciesPromise = new Promise((resolve) => dependenciesPromiseResolve = resolve); + } runDependencies++; Module['monitorRunDependencies']?.(runDependencies); @@ -1362,7 +1354,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var preloadPlugins = []; var FS_handledByPreloadPlugin = async (byteArray, fullname) => { // Ensure plugins are ready. - if (typeof Browser != 'undefined') Browser.init(); + if (typeof Browser !== 'undefined') Browser.init(); for (var plugin of preloadPlugins) { if (plugin['canHandle'](fullname)) { @@ -1382,7 +1374,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { var byteArray = url; - if (typeof url == 'string') { + if (typeof url === 'string') { byteArray = await asyncLoad(url); } @@ -1398,6 +1390,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 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:[], @@ -1405,11 +1398,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }, streams:[], nextInode:1, - nameTable:null, + nameTable:[], currentPath:"/", initialized:false, ignorePermissions:true, - filesystems:null, syncFSRequests:0, ErrnoError:class { name = 'ErrnoError'; @@ -1489,6 +1481,48 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { get isDevice() { return FS.isChrdev(this.mode); } + // The per-inode readiness wait-queue. The node carries a Set of listener + // entries {cb}; producers (SOCKFS, PIPEFS) call notifyListeners on a + // readiness transition, and poll()/epoll consume it. It lives on the node + // (not the fd) so dup'd fds share one queue. Only nodes that derive real + // readiness (sockets, pipes, and an epoll's own node) ever use this - + // always-ready types (regular files, ttys) never register or notify. + addListener(cb, exclusive = false) { + var entry = {cb, exclusive}; + var listeners = (this.listeners ??= new Set()); + listeners.add(entry); + return {listeners, entry}; + } + notifyListeners(flags) { + // Iterates the set without copying, which is safe ONLY under a + // load-bearing contract that every internal listener must honour: + // 1. A listener must not run user code synchronously (a poll waiter only + // resolves a Promise; an epoll registration only re-lists + + // re-notifies; the epoll callback only schedules a tick). User code + // runs on a later tick, never inside this loop. + // 2. A listener may delete entries only from ITS OWN waiter, never from + // a sibling node's set that may be mid-iteration. (Deleting an entry + // of the set being iterated here is fine - a Set tolerates removal of + // a not-yet-visited entry mid-iteration; mutating a *different* node's + // set is fine because that set is not being iterated.) + // Violating either gives silently skipped wakeups that are near-impossible + // to reproduce. Any new producer/listener must preserve it. + if (!this.listeners) return; + // Fire every non-exclusive listener. Among EPOLLEXCLUSIVE registrations + // (one fd watched by several epolls) wake only one, rotating round-robin + // per node, to avoid a thundering herd. (Only epoll registrations are ever + // exclusive; poll waiters and a node's own consumers are not.) + var excl; + for (var entry of this.listeners) { + if (entry.exclusive) (excl ||= []).push(entry); + else entry.cb(flags); + } + if (excl) { + var i = (this.exclTurn || 0) % excl.length; + this.exclTurn = i + 1; + excl[i].cb(flags); + } + } }, lookupPath(path, opts = {}) { if (!path) { @@ -1500,7 +1534,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = FS.cwd() + '/' + path; } - // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + // limit max consecutive symlinks to SYMLOOP_MAX. linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { // split the absolute path var parts = path.split('/').filter((p) => !!p); @@ -1625,7 +1659,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // if we failed to find it in the cache, call into the VFS return FS.lookup(parent, name); }, - createNode(parent, name, mode, rdev) { + createNode(parent, name, mode, rdev = undefined) { var node = new FS.FSNode(parent, name, mode, rdev); FS.hashAddNode(node); @@ -1790,7 +1824,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var arg = setattr ? stream : node; setattr ??= node.node_ops.setattr; FS.checkOpExists(setattr, 63) - setattr(arg, attr); + try { + setattr(arg, attr); + } catch (e) { + if (e instanceof RangeError) { + throw new FS.ErrnoError(22); + } + throw e; + } }, chrdev_stream_ops:{ open(stream) { @@ -1826,7 +1867,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return mounts; }, syncfs(populate, callback) { - if (typeof populate == 'function') { + if (typeof populate === 'function') { callback = populate; populate = false; } @@ -1984,9 +2025,9 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var rtn = { bsize: 4096, frsize: 4096, - blocks: 1e6, - bfree: 5e5, - bavail: 5e5, + blocks: 1_000_000, + bfree: 500_000, + bavail: 500_000, files: FS.nextInode, ffree: FS.nextInode - 1, fsid: 42, @@ -2009,7 +2050,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { mode |= 16384; return FS.mknod(path, mode, 0); }, - mkdirTree(path, mode) { + mkdirTree(path, mode = 0o777) { var dirs = path.split('/'); var d = ''; for (var dir of dirs) { @@ -2023,8 +2064,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - mkdev(path, mode, dev) { - if (typeof dev == 'undefined') { + mkdev(path, mode, dev = undefined) { + if (typeof dev === 'undefined') { dev = mode; mode = 0o666; } @@ -2050,6 +2091,25 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return parent.node_ops.symlink(parent, newname, oldpath); }, + link(oldpath, newpath, flags) { + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.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); + } + // Hardlinks are only supported by filesystem backends that provide a + // `link` node op (e.g. NODERAWFS backed by the host). NODEFS omits it: + // a host hardlink cannot be confined to the mount root. + if (!parent.node_ops.link) { + throw new FS.ErrnoError(34); + } + return parent.node_ops.link(parent, newname, oldpath, flags); + }, rename(old_path, new_path) { var old_dirname = PATH.dirname(old_path); var new_dirname = PATH.dirname(new_path); @@ -2124,6 +2184,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // do the underlying fs rename try { old_dir.node_ops.rename(old_node, new_dir, new_name); + // The replaced node is stale now. Evict it only after the rename + // succeeded: backends like NODEFS report node.id as st_ino. + if (new_node) { + FS.hashRemoveNode(new_node); + } // update old node (we do this here to avoid each backend // needing to) old_node.parent = new_dir; @@ -2194,7 +2259,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return link.node_ops.readlink(link); }, - stat(path, dontFollow) { + stat(path, dontFollow = false) { var lookup = FS.lookupPath(path, { follow: !dontFollow }); var node = lookup.node; var getattr = FS.checkOpExists(node.node_ops.getattr, 63); @@ -2212,16 +2277,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { lstat(path) { return FS.stat(path, true); }, - doChmod(stream, node, mode, dontFollow) { + doChmod(stream, node, mode, dontFollow = false) { FS.doSetAttr(stream, node, { mode: (mode & 4095) | (node.mode & ~4095), ctime: Date.now(), dontFollow }); }, - chmod(path, mode, dontFollow) { + chmod(path, mode, dontFollow = false) { var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { @@ -2236,16 +2301,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var stream = FS.getStreamChecked(fd); FS.doChmod(stream, stream.node, mode, false); }, - doChown(stream, node, dontFollow) { + doChown(stream, node, dontFollow = false) { FS.doSetAttr(stream, node, { timestamp: Date.now(), dontFollow // we ignore the uid / gid for now }); }, - chown(path, uid, gid, dontFollow) { + chown(path, uid, gid, dontFollow = false) { var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { @@ -2281,7 +2346,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { throw new FS.ErrnoError(28); } var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: true }); node = lookup.node; } else { @@ -2296,17 +2361,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } FS.doTruncate(stream, stream.node, len); }, - utime(path, atime, mtime) { - var lookup = FS.lookupPath(path, { follow: true }); - var node = lookup.node; - var setattr = FS.checkOpExists(node.node_ops.setattr, 63); - setattr(node, { + utime(path, atime, mtime, dontFollow = false) { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + FS.doSetAttr(null, lookup.node, { atime: atime, - mtime: mtime + mtime: mtime, + dontFollow }); }, open(path, flags, mode = 0o666) { - if (path === "") { + if (path === '') { throw new FS.ErrnoError(44); } flags = FS_modeStringToFlags(flags); @@ -2317,10 +2381,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } var node; var isDirPath; - if (typeof path == 'object') { + if (typeof path === 'object') { node = path; } else { - isDirPath = path.endsWith("/"); + isDirPath = path.endsWith('/'); // noent_okay makes it so that if the final component of the path // doesn't exist, lookupPath returns `node: undefined`. `path` will be // updated to point to the target of all symlinks. @@ -2403,6 +2467,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { throw new FS.ErrnoError(8); } if (stream.getdents) stream.getdents = null; // free readdir state + // The fd is going away: wake anything waiting on it (poll/epoll) with + // POLLNVAL so a blocking wait unblocks and an epoll registration is evicted + // on its next derive. Only sockets/pipes/epoll ever carry a wait-queue, so + // for every other stream (incl. nodeless noderawfs stdio) this is a no-op. + stream.node?.notifyListeners(32); try { if (stream.stream_ops.close) { stream.stream_ops.close(stream); @@ -2431,7 +2500,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { stream.ungotten = []; return stream.position; }, - read(stream, buffer, offset, length, position) { + read(stream, buffer, offset, length, position = undefined) { if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } @@ -2447,7 +2516,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (!stream.stream_ops.read) { throw new FS.ErrnoError(28); } - var seeking = typeof position != 'undefined'; + var seeking = typeof position !== 'undefined'; if (!seeking) { position = stream.position; } else if (!stream.seekable) { @@ -2457,7 +2526,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (!seeking) stream.position += bytesRead; return bytesRead; }, - write(stream, buffer, offset, length, position, canOwn) { + write(stream, buffer, offset, length, position = undefined, canOwn = undefined) { if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } @@ -2477,7 +2546,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // seek to the end before writing in append mode FS.llseek(stream, 0, 2); } - var seeking = typeof position != 'undefined'; + var seeking = typeof position !== 'undefined'; if (!seeking) { position = stream.position; } else if (!stream.seekable) { @@ -2494,8 +2563,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // to write to file opened in read-only mode with MAP_PRIVATE flag, // as all modifications will be visible only in the memory of // the current process. - if ((prot & 2) !== 0 - && (flags & 2) === 0 + if ((prot & 2) + && !(flags & 2) && (stream.flags & 2097155) !== 2) { throw new FS.ErrnoError(2); } @@ -2523,8 +2592,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return stream.stream_ops.ioctl(stream, cmd, arg); }, readFile(path, opts = {}) { - opts.flags = opts.flags || 0; - opts.encoding = opts.encoding || 'binary'; + opts.flags ??= 0; + opts.encoding ??= 'binary'; if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { abort(`Invalid encoding type "${opts.encoding}"`); } @@ -2540,7 +2609,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return buf; }, writeFile(path, data, opts = {}) { - opts.flags = opts.flags || 577; + opts.flags ??= 577; var stream = FS.open(path, opts.flags, opts.mode); data = FS_fileDataToTypedArray(data); FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); @@ -2587,7 +2656,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // use a buffer to avoid overhead of individual crypto calls per byte var randomBuffer = new Uint8Array(1024), randomLeft = 0; var randomByte = () => { - if (randomLeft === 0) { + if (!randomLeft) { randomFill(randomBuffer); randomLeft = randomBuffer.byteLength; } @@ -2666,19 +2735,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var stderr = FS.open('/dev/stderr', 1); }, staticInit() { - FS.nameTable = new Array(4096); + FS.nameTable.length = 4096; FS.mount(MEMFS, {}, '/'); FS.createDefaultDirectories(); FS.createDefaultDevices(); FS.createSpecialDirectories(); - - FS.filesystems = { - 'MEMFS': MEMFS, - }; }, - init(input, output, error) { + init(input = undefined, output = undefined, error = undefined) { FS.initialized = true; // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here @@ -2698,14 +2763,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - findObject(path, dontResolveLastLink) { - var ret = FS.analyzePath(path, dontResolveLastLink); - if (!ret.exists) { - return null; - } - return ret.object; - }, - analyzePath(path, dontResolveLastLink) { + analyzePath(path, dontResolveLastLink = false) { // operate from within the context of the symlink's target try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); @@ -2733,8 +2791,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; return ret; }, - createPath(parent, path, canRead, canWrite) { - parent = typeof parent == 'string' ? parent : FS.getPath(parent); + createPath(parent, path, canRead = undefined, canWrite = undefined) { + parent = typeof parent === 'string' ? parent : FS.getPath(parent); var parts = path.split('/').reverse(); while (parts.length) { var part = parts.pop(); @@ -2749,15 +2807,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return current; }, - createFile(parent, name, properties, canRead, canWrite) { - var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name); + createFile(parent, name, properties, canRead = undefined, canWrite = undefined) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS_getMode(canRead, canWrite); return FS.create(path, mode); }, - createDataFile(parent, name, data, canRead, canWrite, canOwn) { + createDataFile(parent, name, data = undefined, canRead = undefined, canWrite = undefined, canOwn = undefined) { var path = name; if (parent) { - parent = typeof parent == 'string' ? parent : FS.getPath(parent); + parent = typeof parent === 'string' ? parent : FS.getPath(parent); path = name ? PATH.join2(parent, name) : parent; } var mode = FS_getMode(canRead, canWrite); @@ -2772,8 +2830,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.chmod(node, mode); } }, - createDevice(parent, name, input, output) { - var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name); + createDevice(parent, name, input = undefined, output = undefined) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS_getMode(!!input, !!output); FS.createDevice.major ??= 64; var dev = FS.makedev(FS.createDevice.major++, 0); @@ -2798,7 +2856,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } catch (e) { throw new FS.ErrnoError(29); } - if (result === undefined && bytesRead === 0) { + if (result === undefined && !bytesRead) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; @@ -2829,7 +2887,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { forceLoadFile(obj) { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; if (globalThis.XMLHttpRequest) { - abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + abort('Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.'); } else { // Command-line. try { obj.contents = readBinary(obj.url); @@ -2838,7 +2896,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - createLazyFile(parent, name, url, canRead, canWrite) { + createLazyFile(parent, name, url, canRead = undefined, canWrite = undefined) { // Lazy chunked Uint8Array (implements get and length from Uint8Array). // Actual getting is abstracted away for eventual reuse. class LazyUint8Array { @@ -2860,11 +2918,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var xhr = new XMLHttpRequest(); xhr.open('HEAD', url, false); xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort("Couldn't load " + url + ". Status: " + xhr.status); - var datalength = Number(xhr.getResponseHeader("Content-length")); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort(`Couldn't load ${url}. Status: ${xhr.status}`); + var datalength = Number(xhr.getResponseHeader('Content-length')); var header; - var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; - var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; + var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes'; + var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip'; var chunkSize = 1024*1024; // Chunk size in bytes @@ -2872,13 +2930,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // Function to get a range from the remote URL. var doXHR = (from, to) => { - if (from > to) abort("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength-1) abort("only " + datalength + " bytes available! programmer error!"); + if (from > to) abort(`invalid range (${from}, ${to}) or no bytes requested!`); + if (to > datalength-1) abort(`only ${datalength} bytes available! programmer error!`); // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); - if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + if (datalength !== chunkSize) xhr.setRequestHeader('Range', `bytes=${from}-${to}`); // Some hints to the browser that we want binary data. xhr.responseType = 'arraybuffer'; @@ -2887,21 +2945,21 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort("Couldn't load " + url + ". Status: " + xhr.status); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort(`Couldn't load ${url}. Status: ${xhr.status}`); if (xhr.response !== undefined) { return new Uint8Array(/** @type{Array} */(xhr.response || [])); } - return intArrayFromString(xhr.responseText || '', true); + return intArrayFromString(xhr.responseText ?? '', true); }; var lazyArray = this; lazyArray.setDataGetter((chunkNum) => { var start = chunkNum * chunkSize; var end = (chunkNum+1) * chunkSize - 1; // including this byte end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block - if (typeof lazyArray.chunks[chunkNum] == 'undefined') { + if (typeof lazyArray.chunks[chunkNum] === 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end); } - if (typeof lazyArray.chunks[chunkNum] == 'undefined') abort('doXHR failed!'); + if (typeof lazyArray.chunks[chunkNum] === 'undefined') abort('doXHR failed!'); return lazyArray.chunks[chunkNum]; }); @@ -2910,7 +2968,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file datalength = this.getter(0).length; chunkSize = datalength; - out("LazyFiles on gzip forces download of the whole file when length is accessed"); + out('LazyFiles on gzip forces download of the whole file when length is accessed'); } this._length = datalength; @@ -3001,6 +3059,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + /** * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the * emscripten HEAP, returns a copy of that string as a Javascript String object. @@ -3019,8 +3078,19 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul); return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); }; + + + /** @type {!Int32Array} */ + var HEAP32; + + /** @type {!Uint32Array} */ + var HEAPU32; + + /** not-@type {!BigInt64Array} */ + var HEAP64; var SYSCALLS = { - calculateAt(dirfd, path, allowEmpty) { + currentUmask:18, + calculateAt(dirfd, path, allowEmpty = false) { if (PATH.isAbs(path)) { return path; } @@ -3082,7 +3152,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // MAP_PRIVATE calls need not to be synced back to underlying fs return 0; } - var buffer = HEAPU8.slice(addr, addr + len); + var buffer = HEAPU8.subarray(addr, addr + len); FS.msync(stream, buffer, offset, len, flags); }, getStreamFromFD(fd) { @@ -3102,10 +3172,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.chmod(path, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_faccessat(dirfd, path, amode, flags) { try { @@ -3130,10 +3201,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fchmod(fd, mode) { try { @@ -3141,10 +3213,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.fchmod(fd, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fchown32(fd, owner, group) { try { @@ -3152,10 +3225,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.fchown(fd, owner, group); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var syscallGetVarargI = () => { // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. @@ -3166,6 +3240,9 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var syscallGetVarargP = syscallGetVarargI; + + /** @type {!Int16Array} */ + var HEAP16; function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs; try { @@ -3191,7 +3268,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return stream.flags; case 4: { var arg = syscallGetVarargI(); - stream.flags |= arg; + var mask = 289792; + stream.flags = (stream.flags & ~mask) | (arg & mask); return 0; } case 12: { @@ -3211,20 +3289,22 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return -28; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fstat64(fd, buf) { try { return SYSCALLS.writeStat(buf, FS.fstat(fd)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var INT53_MAX = 9007199254740992; @@ -3236,36 +3316,43 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { - if (isNaN(length)) return -61; + if (isNaN(length)) return -22; FS.ftruncate(fd, length); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; } + var stringToUTF8 = (str, outPtr, maxBytesToWrite) => { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); }; function ___syscall_getcwd(buf, size) { try { - if (size === 0) return -28; + if (!size) return -28; var cwd = FS.cwd(); var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1; if (size < cwdLengthInBytes) return -68; stringToUTF8(cwd, buf, size); return cwdLengthInBytes; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + + var ___syscall_geteuid32 = () => 0; + + + function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs; try { @@ -3357,10 +3444,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { default: return -28; // not supported } } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_lstat64(path, buf) { try { @@ -3368,23 +3456,26 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); return SYSCALLS.writeStat(buf, FS.lstat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_mkdirat(dirfd, path, mode) { try { path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path); - FS.mkdir(path, mode, 0); + mode &= ~SYSCALLS.currentUmask; + FS.mkdir(path, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_newfstatat(dirfd, path, buf, flags) { try { @@ -3396,10 +3487,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_openat(dirfd, path, flags, varargs) { @@ -3409,15 +3501,20 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path); var mode = varargs ? syscallGetVarargI() : 0; + if (flags & 64) { + mode &= ~SYSCALLS.currentUmask; + } return FS.open(path, flags, mode).fd; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { @@ -3434,10 +3531,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAP8[buf+len] = endChar; return len; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_rmdir(path) { try { @@ -3446,10 +3544,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.rmdir(path); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_stat64(path, buf) { try { @@ -3457,10 +3556,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); return SYSCALLS.writeStat(buf, FS.stat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_unlinkat(dirfd, path, flags) { try { @@ -3476,18 +3576,22 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + var readI53FromI64 = (ptr) => { return HEAPU32[((ptr)>>2)] + HEAP32[(((ptr)+(4))>>2)] * 4294967296; }; + function ___syscall_utimensat(dirfd, path, times, flags) { try { + var nofollow = flags & 256; path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path, true); var now = Date.now(), atime, mtime; @@ -3495,37 +3599,32 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { atime = now; mtime = now; } else { - var seconds = readI53FromI64(times); - var nanoseconds = HEAP32[(((times)+(8))>>2)]; - if (nanoseconds == 1073741823) { - atime = now; - } else if (nanoseconds == 1073741822) { - atime = null; - } else { - atime = (seconds*1000) + (nanoseconds/(1000*1000)); - } - times += 16; - seconds = readI53FromI64(times); - nanoseconds = HEAP32[(((times)+(8))>>2)]; - if (nanoseconds == 1073741823) { - mtime = now; - } else if (nanoseconds == 1073741822) { - mtime = null; - } else { - mtime = (seconds*1000) + (nanoseconds/(1000*1000)); + function readTimespec(ptr) { + var tv_nsec = HEAP32[(((ptr)+(8))>>2)]; + if (tv_nsec == 1073741823) { + return now; + } + if (tv_nsec == 1073741822) { + return null; + } + var tv_sec = readI53FromI64(ptr); + return (tv_sec*1000) + Math.floor(tv_nsec/(1000*1000)); } + atime = readTimespec(times); + mtime = readTimespec(times + 16); } // null here means UTIME_OMIT was passed. If both were set to UTIME_OMIT then // we can skip the call completely. if ((mtime ?? atime) !== null) { - FS.utime(path, atime, mtime); + FS.utime(path, atime, mtime, nofollow); } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var isLeapYear = (year) => year%4 === 0 && (year%100 !== 0 || year%400 === 0); @@ -3540,11 +3639,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return yday; }; + function __localtime_js(time, tmPtr) { time = bigintToI53Checked(time); var date = new Date(time*1000); + if (isNaN(date.getTime())) { + return 1; + } HEAP32[((tmPtr)>>2)] = date.getSeconds(); HEAP32[(((tmPtr)+(4))>>2)] = date.getMinutes(); HEAP32[(((tmPtr)+(8))>>2)] = date.getHours(); @@ -3563,6 +3666,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var winterOffset = start.getTimezoneOffset(); var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0; HEAP32[(((tmPtr)+(32))>>2)] = dst; + return 0; ; } @@ -3571,6 +3675,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + + function __mmap_js(len, prot, flags, fd, offset, allocated, addr) { offset = bigintToI53Checked(offset); @@ -3584,7 +3690,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((addr)>>2)] = ptr; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; @@ -3602,12 +3708,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { SYSCALLS.doMsync(addr, stream, len, flags, offset); } } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; } + + var __tzset_js = (timezone, daylight, std_name, dst_name) => { // TODO: Use (malleable) environment variables instead of system settings. var currentYear = new Date().getFullYear(); @@ -3636,11 +3744,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var extractZone = (timezoneOffset) => { // Why inverse sign? // Read here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset - var sign = timezoneOffset >= 0 ? "-" : "+"; + var sign = timezoneOffset >= 0 ? '-' : '+'; var absOffset = Math.abs(timezoneOffset) - var hours = String(Math.floor(absOffset / 60)).padStart(2, "0"); - var minutes = String(absOffset % 60).padStart(2, "0"); + var hours = String(Math.floor(absOffset / 60)).padStart(2, '0'); + var minutes = String(absOffset % 60).padStart(2, '0'); return `UTC${sign}${hours}${minutes}`; } @@ -3665,6 +3773,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; + function _clock_time_get(clk_id, ignored_precision, ptime) { ignored_precision = bigintToI53Checked(ignored_precision); @@ -3682,7 +3791,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return 52; } // "now" is in ms, and wasi times are in ns. - var nsec = Math.round(now * 1000 * 1000); + var nsec = Math.round(now * 1_000_000); HEAP64[((ptime)>>3)] = BigInt(nsec); return 0; ; @@ -3710,9 +3819,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return 1 /*success*/; } catch(e) { } - // implicit 0 return to save code size (caller will cast "undefined" into 0 + // implicit 0 return to save code size (caller will cast 'undefined' into 0 // anyhow) }; + var _emscripten_resize_heap = (requestedSize) => { var oldSize = HEAPU8.length; // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. @@ -3766,11 +3876,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var ENV = { }; - var getExecutableName = () => thisProgram || './this.program'; + var getExecutableName = () => thisProgram; var getEnvStrings = () => { if (!getEnvStrings.strings) { // Default values. - // Browser language detection #8751 var lang = (globalThis.navigator?.language ?? 'C').replace('-', '_') + '.UTF-8'; var env = { 'USER': 'web_user', @@ -3798,6 +3907,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return getEnvStrings.strings; }; + var _environ_get = (__environ, environ_buf) => { var bufSize = 0; var envp = 0; @@ -3811,6 +3921,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; + var _environ_sizes_get = (penviron_count, penviron_buf_size) => { var strings = getEnvStrings(); HEAPU32[((penviron_count)>>2)] = strings.length; @@ -3829,11 +3940,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.close(stream); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + + function _fd_fdstat_get(fd, pbuf) { try { @@ -3855,11 +3970,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAP64[(((pbuf)+(16))>>3)] = BigInt(rightsInheriting); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + /** @param {number=} offset */ var doReadv = (stream, iov, iovcnt, offset) => { var ret = 0; @@ -3867,17 +3984,29 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var ptr = HEAPU32[((iov)>>2)]; var len = HEAPU32[(((iov)+(4))>>2)]; iov += 8; - var curr = FS.read(stream, HEAP8, ptr, len, offset); + try { + var curr = FS.read(stream, HEAP8, ptr, len, offset); + } catch (e) { + // On a non-blocking stream a subsequent read may would-block after we + // already gathered data. POSIX readv is a single gather-read: return + // what we have rather than failing the whole call. + if (ret > 0 && e instanceof FS.ErrnoError && + (e.errno == 6 || e.errno == 6)) { + break; + } + throw e; + } if (curr < 0) return -1; ret += curr; if (curr < len) break; // nothing more to read - if (typeof offset != 'undefined') { + if (typeof offset !== 'undefined') { offset += curr; } } return ret; }; + function _fd_read(fd, iov, iovcnt, pnum) { try { @@ -3886,26 +4015,28 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((pnum)>>2)] = num; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + function _fd_seek(fd, offset, whence, newOffset) { offset = bigintToI53Checked(offset); try { - if (isNaN(offset)) return 61; + if (isNaN(offset)) return 22; var stream = SYSCALLS.getStreamFromFD(fd); FS.llseek(stream, offset, whence); HEAP64[((newOffset)>>3)] = BigInt(stream.position); - if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; // reset readdir state + if (stream.getdents && !offset && whence === 0) stream.getdents = null; // reset readdir state return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } ; @@ -3918,32 +4049,40 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var rtn = stream.stream_ops?.fsync?.(stream); return rtn; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + /** @param {number=} offset */ var doWritev = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { + // Gather all iovecs into one contiguous buffer and issue a single + // FS.write, matching POSIX writev's single gather-write semantics (as + // __syscall_sendmsg already does). Per-iovec writes fragment a stream + // socket send into multiple segments, breaking stream byte semantics. + if (iovcnt == 1) { + // Single iovec: write directly from HEAP8, no gather buffer needed. + return FS.write(stream, HEAP8, HEAPU32[((iov)>>2)], HEAPU32[(((iov)+(4))>>2)], offset); + } + var total = 0; + for (var i = 0, p = iov; i < iovcnt; i++, p += 8) { + total += HEAPU32[(((p)+(4))>>2)]; + } + var view = new Uint8Array(total); + var voff = 0; + for (var i = 0; i < iovcnt; i++, iov += 8) { var ptr = HEAPU32[((iov)>>2)]; var len = HEAPU32[(((iov)+(4))>>2)]; - iov += 8; - var curr = FS.write(stream, HEAP8, ptr, len, offset); - if (curr < 0) return -1; - ret += curr; - if (curr < len) { - // No more space to write. - break; - } - if (typeof offset != 'undefined') { - offset += curr; - } + view.set(HEAPU8.subarray(ptr, ptr + len), voff); + voff += len; } - return ret; + return FS.write(stream, view, 0, total, offset); }; + function _fd_write(fd, iov, iovcnt, pnum) { try { @@ -3952,10 +4091,72 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((pnum)>>2)] = num; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + + + + + /** @type {!Float32Array} */ + var HEAPF32; + + /** @type {!Float64Array} */ + var HEAPF64; + + + /** + * @param {number} ptr + * @param {string} type + */ + function getValue(ptr, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': return HEAP8[ptr]; + case 'i8': return HEAP8[ptr]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': return HEAP64[((ptr)>>3)]; + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + case '*': return HEAPU32[((ptr)>>2)]; + default: abort(`invalid type for getValue: ${type}`); + } + } + + + + + + + + + /** + * @param {number} ptr + * @param {number} value + * @param {string} type + */ + function setValue(ptr, value, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': HEAP8[ptr] = value; break; + case 'i8': HEAP8[ptr] = value; break; + case 'i16': HEAP16[((ptr)>>1)] = value; break; + case 'i32': HEAP32[((ptr)>>2)] = value; break; + case 'i64': HEAP64[((ptr)>>3)] = BigInt(value); break; + case 'float': HEAPF32[((ptr)>>2)] = value; break; + case 'double': HEAPF64[((ptr)>>3)] = value; break; + case '*': HEAPU32[((ptr)>>2)] = value; break; + default: abort(`invalid type for setValue: ${type}`); + } + } + + + + FS.createPreloadedFile = FS_createPreloadedFile; @@ -3974,19 +4175,21 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // Begin ATMODULES hooks if (Module['noExitRuntime']) noExitRuntime = Module['noExitRuntime']; -if (Module['preloadPlugins']) preloadPlugins = Module['preloadPlugins']; + if (Module['print']) out = Module['print']; if (Module['printErr']) err = Module['printErr']; -if (Module['wasmBinary']) wasmBinary = Module['wasmBinary']; // End ATMODULES hooks - if (Module['arguments']) arguments_ = Module['arguments']; + if (Module['arguments']) programArgs = Module['arguments']; if (Module['thisProgram']) thisProgram = Module['thisProgram']; - if (Module['preInit']) { - if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; - while (Module['preInit'].length > 0) { - Module['preInit'].shift()(); + var preInit = Module['preInit']; + if (preInit) { + if (typeof preInit === 'function') Module['preInit'] = preInit = [preInit]; + // Written as a loop so that preInit functions that themselves add more + // preInit functions. Is this actually needed? + while (preInit.length > 0) { + preInit.shift()(); } } } @@ -4263,6 +4466,7 @@ var _sqlite3_status64, _free, _realloc, _emscripten_builtin_memalign, + _setThrew, __emscripten_stack_restore, __emscripten_stack_alloc, _emscripten_stack_get_current, @@ -4532,6 +4736,7 @@ function assignWasmExports(wasmExports) { _free = Module['_free'] = wasmExports['free']; _realloc = Module['_realloc'] = wasmExports['realloc']; _emscripten_builtin_memalign = wasmExports['emscripten_builtin_memalign']; + _setThrew = wasmExports['setThrew']; __emscripten_stack_restore = wasmExports['_emscripten_stack_restore']; __emscripten_stack_alloc = wasmExports['_emscripten_stack_alloc']; _emscripten_stack_get_current = wasmExports['emscripten_stack_get_current']; @@ -4556,6 +4761,8 @@ var wasmImports = { /** @export */ __syscall_getcwd: ___syscall_getcwd, /** @export */ + __syscall_geteuid32: ___syscall_geteuid32, + /** @export */ __syscall_ioctl: ___syscall_ioctl, /** @export */ __syscall_lstat64: ___syscall_lstat64, @@ -4617,55 +4824,38 @@ var wasmImports = { // include: postamble.js // === Auto-generated postamble setup entry stuff === -function run() { - - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; - } +async function run() { preRun(); - // a preRun added a dependency, run will be called later - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; + if (runDependencies) { + await resolveRunDependencies(); } - function doRun() { - // run may have just been called through dependencies being fulfilled just in this very frame, - // or while the async setStatus time below was happening - Module['calledRun'] = true; - - if (ABORT) return; + var setStatus = Module['setStatus']; + if (setStatus) { + setStatus('Running...'); + // Yield to the event loop to allow the browser to paint "Running..." + await new Promise((resolve) => setTimeout(resolve, 1)); + // Then we want to clear the status text, but only after the rest of this function runs. + setTimeout(setStatus, 1, ''); + } - initRuntime(); + if (ABORT) return; - readyPromiseResolve?.(Module); - Module['onRuntimeInitialized']?.(); + initRuntime(); - postRun(); - } + Module['onRuntimeInitialized']?.(); - if (Module['setStatus']) { - Module['setStatus']('Running...'); - setTimeout(() => { - setTimeout(() => Module['setStatus'](''), 1); - doRun(); - }, 1); - } else - { - doRun(); - } + postRun(); } var wasmExports; // In modularize mode the generated code is within a factory function so we // can use await here (since it's not top-level-await). -wasmExports = await (createWasm()); - -run(); +wasmExports = await createWasm(); +await run(); // end include: postamble.js @@ -4745,7 +4935,7 @@ Module.runSQLite3PostLoadInit = async function( ** SQLITE_VERSION_NUMBER 3054001 ** SQLITE_SOURCE_ID "2026-10-16 14:45:20 555e07ef33f69a0957d24d8e555b702464a131d6f1607f4c3c1e4551e8e6ca76" ** -** Emscripten SDK: 5.0.5 +** Emscripten SDK: 6.0.13 */ /* 2022-05-22 @@ -21581,25 +21771,12 @@ try{ // include: postamble_modularize.js // In MODULARIZE mode we wrap the generated code in a factory function // and return either the Module itself, or a promise of the module. -// -// We assign to the `moduleRtn` global here and configure closure to see -// this as an extern so it won't get minified. - -if (runtimeInitialized) { - moduleRtn = Module; -} else { - // Set up the promise that indicates the Module is initialized - moduleRtn = new Promise((resolve, reject) => { - readyPromiseResolve = resolve; - readyPromiseReject = reject; - }); -} // end include: postamble_modularize.js - return moduleRtn; + return Module; } // Export using a UMD style export, or ES6 exports if selected diff --git a/src/bin/sqlite3-node.mjs b/src/bin/sqlite3-node.mjs index 068e049..48ba552 100644 --- a/src/bin/sqlite3-node.mjs +++ b/src/bin/sqlite3-node.mjs @@ -31,7 +31,7 @@ ** SQLITE_VERSION_NUMBER 3054001 ** SQLITE_SOURCE_ID "2026-10-16 14:45:20 555e07ef33f69a0957d24d8e555b702464a131d6f1607f4c3c1e4551e8e6ca76" ** -** Emscripten SDK: 5.0.5 +** Emscripten SDK: 6.0.13 */ // This code implements the `-sMODULARIZE` settings by taking the generated // JS program code (INNER_JS_CODE) and wrapping it in a factory function. @@ -39,8 +39,7 @@ // When targeting node and ES6 we use `await import ..` in the generated code // so the outer function needs to be marked as async. async function sqlite3InitModule(moduleArg = {}) { - var moduleRtn; - + var Module = moduleArg; // include: shell.js // include: minimum_runtime_check.js // end include: minimum_runtime_check.js @@ -57,15 +56,14 @@ async function sqlite3InitModule(moduleArg = {}) { // after the generated code, you will need to define var Module = {}; // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. -var Module = moduleArg; // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). -var ENVIRONMENT_IS_WEB = false; -var ENVIRONMENT_IS_WORKER = false; -var ENVIRONMENT_IS_NODE = true; -var ENVIRONMENT_IS_SHELL = false; +const ENVIRONMENT_IS_WEB = false; +const ENVIRONMENT_IS_WORKER = false; +const ENVIRONMENT_IS_NODE = true; +const ENVIRONMENT_IS_SHELL = false; if (ENVIRONMENT_IS_NODE) { // When building an ES module `require` is not normally available. @@ -115,7 +113,7 @@ if (ENVIRONMENT_IS_NODE) { // end include: ./bld/pre-js.node.js -var arguments_ = []; +var programArgs = []; var thisProgram = './this.program'; var quit_ = (status, toThrow) => { throw toThrow; @@ -164,7 +162,7 @@ readAsync = async (filename, binary = true) => { thisProgram = process.argv[1].replace(/\\/g, '/'); } - arguments_ = process.argv.slice(2); + programArgs = process.argv.slice(2); quit_ = (status, toThrow) => { process.exitCode = status; @@ -233,8 +231,6 @@ function assert(condition, text) { var isFileURI = (filename) => filename.startsWith('file://'); // include: runtime_common.js -// include: runtime_stack_check.js -// end include: runtime_stack_check.js // include: runtime_exceptions.js // Base Emscripten EH error class class EmscriptenEH {} @@ -244,34 +240,39 @@ class EmscriptenSjLj extends EmscriptenEH {} // end include: runtime_exceptions.js // include: runtime_debug.js // end include: runtime_debug.js -var readyPromiseResolve, readyPromiseReject; - // Memory management var runtimeInitialized = false; +// When ALLOW_MEMORY_GROWTH is enabled, the conversion from Wasm +// memory to ArrayBuffer requires some additional logic. +function getMemoryBuffer() { + return wasmMemory.buffer; +} + function updateMemoryViews() { - var b = wasmMemory.buffer; + // If we already have a heap that is resizeable/growable buffer we don't + // need to do anything in updateMemoryViews. + if (HEAP8?.buffer?.resizable) return; + var b = getMemoryBuffer(); 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); + } // In non-standalone/normal mode, we create the memory here. // include: runtime_init_memory.js // Create the wasm memory. (Note: this only applies if IMPORTED_MEMORY is defined) -// check for full engine support (use string 'subarray' to avoid closure compiler confusion) - function initMemory() { @@ -303,11 +304,10 @@ function initMemory() { // end include: memoryprofiler.js // end include: runtime_common.js function preRun() { - if (Module['preRun']) { - if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; - while (Module['preRun'].length) { - addOnPreRun(Module['preRun'].shift()); - } + var preRun = Module['preRun']; + if (preRun) { + if (typeof preRun === 'function') preRun = [preRun]; + onPreRuns.push(...preRun); } // Begin ATPRERUNS hooks callRuntimeCallbacks(onPreRuns); @@ -327,16 +327,15 @@ TTY.init(); // Begin ATPOSTCTORS hooks FS.ignorePermissions = false; // End ATPOSTCTORS hooks + } function postRun() { - // PThreads reuse the runtime from the main thread. - if (Module['postRun']) { - if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; - while (Module['postRun'].length) { - addOnPostRun(Module['postRun'].shift()); - } + var postRun = Module['postRun']; + if (postRun) { + if (typeof postRun === 'function') postRun = [postRun]; + onPostRuns.push(...postRun); } // Begin ATPOSTRUNS hooks @@ -346,7 +345,6 @@ function postRun() { /** * @param {string|number=} what - * @noreturn */ function abort(what) { Module['onAbort']?.(what); @@ -368,19 +366,7 @@ function abort(what) { // a trap or not based on a hidden field within the object. So at the moment // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that // allows this in the wasm spec. - - // Suppress closure compiler warning here. Closure compiler's builtin extern - // definition for WebAssembly.RuntimeError claims it takes no arguments even - // though it can. - // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. - /** @suppress {checkTypes} */ - var e = new WebAssembly.RuntimeError(what); - - readyPromiseReject?.(e); - // Throw the error whether or not MODULARIZE is set because abort is used - // in code paths apart from instantiation where an exception is expected - // to be thrown when abort is called. - throw e; + throw new WebAssembly.RuntimeError(what); } var wasmBinaryFile; @@ -397,9 +383,6 @@ function findWasmBinary() { } function getBinarySync(file) { - if (file == wasmBinaryFile && wasmBinary) { - return new Uint8Array(wasmBinary); - } if (readBinary) { return readBinary(file); } @@ -438,12 +421,9 @@ async function instantiateArrayBuffer(binaryFile, imports) { async function instantiateAsync(binary, binaryFile, imports) { if (!binary - // Avoid instantiateStreaming() on Node.js environment for now, as while - // Node.js v18.1.0 implements it, it does not have a full fetch() - // implementation yet. - // - // Reference: - // https://github.com/emscripten-core/emscripten/pull/16917 + // Avoid using instantiateStreaming() on Node.js since the `fetch()` API + // does not support `file://` URLs. + // See: https://github.com/emscripten-core/emscripten/pull/16917 && !ENVIRONMENT_IS_NODE ) { try { @@ -476,8 +456,7 @@ async function createWasm() { // Load the wasm module and create an instance of using native support in the JS engine. // handle a generated wasm instance, receiving its exports and // performing other necessary setup - /** @param {WebAssembly.Module=} module*/ - function receiveInstance(instance, module) { + function receiveInstance(instance) { wasmExports = instance.exports; assignWasmExports(wasmExports); @@ -502,11 +481,10 @@ async function createWasm() { // performing. // Also pthreads and wasm workers initialize the wasm instance through this // path. - if (Module['instantiateWasm']) { - return new Promise((resolve, reject) => { - Module['instantiateWasm'](info, (inst, mod) => { - resolve(receiveInstance(inst, mod)); - }); + var instantiateWasm = Module['instantiateWasm']; + if (instantiateWasm) { + return new Promise((resolve) => { + instantiateWasm(info, (inst) => resolve(receiveInstance(inst))); }); } @@ -529,36 +507,9 @@ async function createWasm() { } } - /** @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) { // Pass the module as the first argument. @@ -572,49 +523,8 @@ async function createWasm() { var addOnPreRun = (cb) => onPreRuns.push(cb); - - /** - * @param {number} ptr - * @param {string} type - */ - function getValue(ptr, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': return HEAP8[ptr]; - case 'i8': return HEAP8[ptr]; - case 'i16': return HEAP16[((ptr)>>1)]; - case 'i32': return HEAP32[((ptr)>>2)]; - case 'i64': return HEAP64[((ptr)>>3)]; - case 'float': return HEAPF32[((ptr)>>2)]; - case 'double': return HEAPF64[((ptr)>>3)]; - case '*': return HEAPU32[((ptr)>>2)]; - default: abort(`invalid type for getValue: ${type}`); - } - } - var noExitRuntime = true; - - /** - * @param {number} ptr - * @param {number} value - * @param {string} type - */ - function setValue(ptr, value, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': HEAP8[ptr] = value; break; - case 'i8': HEAP8[ptr] = value; break; - case 'i16': HEAP16[((ptr)>>1)] = value; break; - case 'i32': HEAP32[((ptr)>>2)] = value; break; - case 'i64': HEAP64[((ptr)>>3)] = BigInt(value); break; - case 'float': HEAPF32[((ptr)>>2)] = value; break; - case 'double': HEAPF64[((ptr)>>3)] = value; break; - case '*': HEAPU32[((ptr)>>2)] = value; break; - default: abort(`invalid type for setValue: ${type}`); - } - } - var stackRestore = (val) => __emscripten_stack_restore(val); var stackSave = () => _emscripten_stack_get_current(); @@ -686,7 +596,7 @@ var initRandomFill = () => { // This block is not needed on v19+ since crypto.getRandomValues is builtin if (ENVIRONMENT_IS_NODE) { var nodeCrypto = require('node:crypto'); - return (view) => nodeCrypto.randomFillSync(view); + return (view) => (nodeCrypto.randomFillSync(view), 0); } return (view) => (crypto.getRandomValues(view), 0); @@ -702,7 +612,7 @@ resolve:(...args) => { for (var i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = (i >= 0) ? args[i] : FS.cwd(); // Skip empty and invalid entries - if (typeof path != 'string') { + if (typeof path !== 'string') { throw new TypeError('Arguments to path.resolve must be strings'); } else if (!path) { return ''; // an invalid portion invalidates the whole thing @@ -752,18 +662,26 @@ relative:(from, to) => { var UTF8Decoder = new TextDecoder(); -var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { - var maxIdx = idx + maxBytesToRead; - if (ignoreNul) return maxIdx; - // TextDecoder needs to know the byte length in advance, it doesn't stop on - // null terminator by itself. - // As a tiny code save trick, compare idx against maxIdx using a negation, - // so that maxBytesToRead=undefined/NaN means Infinity. - while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; - return idx; - }; /** + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul + * @return {number} + */ + var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + var maxIdx = idx + maxBytesToRead; + if (ignoreNul) return maxIdx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. + // As a tiny code save trick, compare idx against maxIdx using a negation, + // so that maxBytesToRead=undefined/NaN means Infinity. + 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. @@ -860,17 +778,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var buf = Buffer.alloc(BUFSIZE); var bytesRead = 0; - // For some reason we must suppress a closure warning here, even though - // fd definitely exists on process.stdin, and is even the proper way to - // get the fd of stdin, - // https://github.com/nodejs/help/issues/2136#issuecomment-523649904 - // This started to happen after moving this logic out of library_tty.js, - // so it is related to the surrounding code in some unclear manner. - /** @suppress {missingProperties} */ - var fd = process.stdin.fd; - try { - bytesRead = fs.readSync(fd, buf, 0, BUFSIZE); + bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE); } catch(e) { // Cross-platform differences: on Windows, reading EOF throws an // exception, but on other OSes, reading EOF returns 0. Uniformize @@ -880,7 +789,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } if (bytesRead > 0) { - result = buf.slice(0, bytesRead).toString('utf-8'); + result = buf.toString('utf-8', 0, bytesRead); } } else {} @@ -946,12 +855,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } catch (e) { throw new FS.ErrnoError(29); } - if (result === undefined && bytesRead === 0) { + if (result === undefined && !bytesRead) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; bytesRead++; buffer[offset+i] = result; + // We currently only support canonical mode (ICANON), where + // read(2) returns as soon as a line delimiter is read. + if (result === 10) break; } if (bytesRead) { stream.node.atime = Date.now(); @@ -1034,6 +946,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; + /** @type {!Uint8Array} */ + var HEAPU8; var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size); var alignMemory = (size, alignment) => { @@ -1045,12 +959,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 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) { + createNode(parent, name, mode, dev = undefined) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { // not supported throw new FS.ErrnoError(63); @@ -1143,7 +1058,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // For small filesizes (<1MB), perform size*2 geometric increase, but for // large sizes, do a much more conservative size*1.125 increase to avoid // overshooting the allocation cap by a very large margin. - var CAPACITY_DOUBLING_MAX = 1024 * 1024; + const CAPACITY_DOUBLING_MAX = 1024 * 1024; newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); if (prevCapacity) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. var oldContents = MEMFS.getFileDataAsTypedArray(node); @@ -1187,7 +1102,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return attr; }, setattr(node, attr) { - for (const key of ["mode", "atime", "mtime", "ctime"]) { + for (const key of ['mode', 'atime', 'mtime', 'ctime']) { if (attr[key] != null) { node[key] = attr[key]; } @@ -1206,7 +1121,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } throw MEMFS.doesNotExistError; }, - mknod(parent, name, mode, dev) { + mknod(parent, name, mode, dev = undefined) { return MEMFS.createNode(parent, name, mode, dev); }, rename(old_node, new_dir, new_name) { @@ -1214,14 +1129,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) {} - if (new_node) { - if (FS.isDir(old_node.mode)) { - // if we're overwriting a directory at new_name, make sure it's empty. - for (var i in new_node.contents) { - throw new FS.ErrnoError(55); - } + if (new_node && FS.isDir(old_node.mode)) { + // if we're overwriting a directory at new_name, make sure it's empty. + for (var i in new_node.contents) { + throw new FS.ErrnoError(55); } - FS.hashRemoveNode(new_node); } // do the internal rewiring delete old_node.parent.contents[old_node.name]; @@ -1280,7 +1192,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (canOwn) { node.contents = buffer.subarray(offset, offset + length); node.usedBytes = length; - } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + } else if (!node.usedBytes && !position) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. node.contents = buffer.slice(offset, offset + length); node.usedBytes = length; } else { @@ -1347,7 +1259,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; var FS_modeStringToFlags = (str) => { - if (typeof str != 'string') return str; + if (typeof str !== 'string') return str; var flagModes = { 'r': 0, 'r+': 2, @@ -1357,14 +1269,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 'a+': 1024 | 64 | 2, }; var flags = flagModes[str]; - if (typeof flags == 'undefined') { + if (typeof flags === 'undefined') { throw new Error(`Unknown file open mode: ${str}`); } return flags; }; var FS_fileDataToTypedArray = (data) => { - if (typeof data == 'string') { + if (typeof data === 'string') { data = intArrayFromString(data, true); } if (!data.subarray) { @@ -1393,24 +1305,27 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return id; }; + var dependenciesPromise = null; + var resolveRunDependencies = async () => dependenciesPromise; var runDependencies = 0; - var dependenciesFulfilled = null; + var dependenciesPromiseResolve = null; var removeRunDependency = (id) => { runDependencies--; Module['monitorRunDependencies']?.(runDependencies); - if (runDependencies == 0) { - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); // can add another dependenciesFulfilled - } + if (!runDependencies) { + dependenciesPromiseResolve(); } }; + + var addRunDependency = (id) => { + if (!runDependencies) { + dependenciesPromise = new Promise((resolve) => dependenciesPromiseResolve = resolve); + } runDependencies++; Module['monitorRunDependencies']?.(runDependencies); @@ -1421,7 +1336,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var preloadPlugins = []; var FS_handledByPreloadPlugin = async (byteArray, fullname) => { // Ensure plugins are ready. - if (typeof Browser != 'undefined') Browser.init(); + if (typeof Browser !== 'undefined') Browser.init(); for (var plugin of preloadPlugins) { if (plugin['canHandle'](fullname)) { @@ -1441,7 +1356,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { var byteArray = url; - if (typeof url == 'string') { + if (typeof url === 'string') { byteArray = await asyncLoad(url); } @@ -1457,6 +1372,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 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:[], @@ -1464,11 +1380,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }, streams:[], nextInode:1, - nameTable:null, + nameTable:[], currentPath:"/", initialized:false, ignorePermissions:true, - filesystems:null, syncFSRequests:0, ErrnoError:class { name = 'ErrnoError'; @@ -1548,6 +1463,48 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { get isDevice() { return FS.isChrdev(this.mode); } + // The per-inode readiness wait-queue. The node carries a Set of listener + // entries {cb}; producers (SOCKFS, PIPEFS) call notifyListeners on a + // readiness transition, and poll()/epoll consume it. It lives on the node + // (not the fd) so dup'd fds share one queue. Only nodes that derive real + // readiness (sockets, pipes, and an epoll's own node) ever use this - + // always-ready types (regular files, ttys) never register or notify. + addListener(cb, exclusive = false) { + var entry = {cb, exclusive}; + var listeners = (this.listeners ??= new Set()); + listeners.add(entry); + return {listeners, entry}; + } + notifyListeners(flags) { + // Iterates the set without copying, which is safe ONLY under a + // load-bearing contract that every internal listener must honour: + // 1. A listener must not run user code synchronously (a poll waiter only + // resolves a Promise; an epoll registration only re-lists + + // re-notifies; the epoll callback only schedules a tick). User code + // runs on a later tick, never inside this loop. + // 2. A listener may delete entries only from ITS OWN waiter, never from + // a sibling node's set that may be mid-iteration. (Deleting an entry + // of the set being iterated here is fine - a Set tolerates removal of + // a not-yet-visited entry mid-iteration; mutating a *different* node's + // set is fine because that set is not being iterated.) + // Violating either gives silently skipped wakeups that are near-impossible + // to reproduce. Any new producer/listener must preserve it. + if (!this.listeners) return; + // Fire every non-exclusive listener. Among EPOLLEXCLUSIVE registrations + // (one fd watched by several epolls) wake only one, rotating round-robin + // per node, to avoid a thundering herd. (Only epoll registrations are ever + // exclusive; poll waiters and a node's own consumers are not.) + var excl; + for (var entry of this.listeners) { + if (entry.exclusive) (excl ||= []).push(entry); + else entry.cb(flags); + } + if (excl) { + var i = (this.exclTurn || 0) % excl.length; + this.exclTurn = i + 1; + excl[i].cb(flags); + } + } }, lookupPath(path, opts = {}) { if (!path) { @@ -1559,7 +1516,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = FS.cwd() + '/' + path; } - // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + // limit max consecutive symlinks to SYMLOOP_MAX. linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { // split the absolute path var parts = path.split('/').filter((p) => !!p); @@ -1684,7 +1641,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // if we failed to find it in the cache, call into the VFS return FS.lookup(parent, name); }, - createNode(parent, name, mode, rdev) { + createNode(parent, name, mode, rdev = undefined) { var node = new FS.FSNode(parent, name, mode, rdev); FS.hashAddNode(node); @@ -1849,7 +1806,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var arg = setattr ? stream : node; setattr ??= node.node_ops.setattr; FS.checkOpExists(setattr, 63) - setattr(arg, attr); + try { + setattr(arg, attr); + } catch (e) { + if (e instanceof RangeError) { + throw new FS.ErrnoError(22); + } + throw e; + } }, chrdev_stream_ops:{ open(stream) { @@ -1885,7 +1849,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return mounts; }, syncfs(populate, callback) { - if (typeof populate == 'function') { + if (typeof populate === 'function') { callback = populate; populate = false; } @@ -2043,9 +2007,9 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var rtn = { bsize: 4096, frsize: 4096, - blocks: 1e6, - bfree: 5e5, - bavail: 5e5, + blocks: 1_000_000, + bfree: 500_000, + bavail: 500_000, files: FS.nextInode, ffree: FS.nextInode - 1, fsid: 42, @@ -2068,7 +2032,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { mode |= 16384; return FS.mknod(path, mode, 0); }, - mkdirTree(path, mode) { + mkdirTree(path, mode = 0o777) { var dirs = path.split('/'); var d = ''; for (var dir of dirs) { @@ -2082,8 +2046,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - mkdev(path, mode, dev) { - if (typeof dev == 'undefined') { + mkdev(path, mode, dev = undefined) { + if (typeof dev === 'undefined') { dev = mode; mode = 0o666; } @@ -2109,6 +2073,25 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return parent.node_ops.symlink(parent, newname, oldpath); }, + link(oldpath, newpath, flags) { + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.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); + } + // Hardlinks are only supported by filesystem backends that provide a + // `link` node op (e.g. NODERAWFS backed by the host). NODEFS omits it: + // a host hardlink cannot be confined to the mount root. + if (!parent.node_ops.link) { + throw new FS.ErrnoError(34); + } + return parent.node_ops.link(parent, newname, oldpath, flags); + }, rename(old_path, new_path) { var old_dirname = PATH.dirname(old_path); var new_dirname = PATH.dirname(new_path); @@ -2183,6 +2166,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // do the underlying fs rename try { old_dir.node_ops.rename(old_node, new_dir, new_name); + // The replaced node is stale now. Evict it only after the rename + // succeeded: backends like NODEFS report node.id as st_ino. + if (new_node) { + FS.hashRemoveNode(new_node); + } // update old node (we do this here to avoid each backend // needing to) old_node.parent = new_dir; @@ -2253,7 +2241,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return link.node_ops.readlink(link); }, - stat(path, dontFollow) { + stat(path, dontFollow = false) { var lookup = FS.lookupPath(path, { follow: !dontFollow }); var node = lookup.node; var getattr = FS.checkOpExists(node.node_ops.getattr, 63); @@ -2271,16 +2259,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { lstat(path) { return FS.stat(path, true); }, - doChmod(stream, node, mode, dontFollow) { + doChmod(stream, node, mode, dontFollow = false) { FS.doSetAttr(stream, node, { mode: (mode & 4095) | (node.mode & ~4095), ctime: Date.now(), dontFollow }); }, - chmod(path, mode, dontFollow) { + chmod(path, mode, dontFollow = false) { var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { @@ -2295,16 +2283,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var stream = FS.getStreamChecked(fd); FS.doChmod(stream, stream.node, mode, false); }, - doChown(stream, node, dontFollow) { + doChown(stream, node, dontFollow = false) { FS.doSetAttr(stream, node, { timestamp: Date.now(), dontFollow // we ignore the uid / gid for now }); }, - chown(path, uid, gid, dontFollow) { + chown(path, uid, gid, dontFollow = false) { var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { @@ -2340,7 +2328,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { throw new FS.ErrnoError(28); } var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: true }); node = lookup.node; } else { @@ -2355,17 +2343,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } FS.doTruncate(stream, stream.node, len); }, - utime(path, atime, mtime) { - var lookup = FS.lookupPath(path, { follow: true }); - var node = lookup.node; - var setattr = FS.checkOpExists(node.node_ops.setattr, 63); - setattr(node, { + utime(path, atime, mtime, dontFollow = false) { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + FS.doSetAttr(null, lookup.node, { atime: atime, - mtime: mtime + mtime: mtime, + dontFollow }); }, open(path, flags, mode = 0o666) { - if (path === "") { + if (path === '') { throw new FS.ErrnoError(44); } flags = FS_modeStringToFlags(flags); @@ -2376,10 +2363,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } var node; var isDirPath; - if (typeof path == 'object') { + if (typeof path === 'object') { node = path; } else { - isDirPath = path.endsWith("/"); + isDirPath = path.endsWith('/'); // noent_okay makes it so that if the final component of the path // doesn't exist, lookupPath returns `node: undefined`. `path` will be // updated to point to the target of all symlinks. @@ -2462,6 +2449,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { throw new FS.ErrnoError(8); } if (stream.getdents) stream.getdents = null; // free readdir state + // The fd is going away: wake anything waiting on it (poll/epoll) with + // POLLNVAL so a blocking wait unblocks and an epoll registration is evicted + // on its next derive. Only sockets/pipes/epoll ever carry a wait-queue, so + // for every other stream (incl. nodeless noderawfs stdio) this is a no-op. + stream.node?.notifyListeners(32); try { if (stream.stream_ops.close) { stream.stream_ops.close(stream); @@ -2490,7 +2482,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { stream.ungotten = []; return stream.position; }, - read(stream, buffer, offset, length, position) { + read(stream, buffer, offset, length, position = undefined) { if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } @@ -2506,7 +2498,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (!stream.stream_ops.read) { throw new FS.ErrnoError(28); } - var seeking = typeof position != 'undefined'; + var seeking = typeof position !== 'undefined'; if (!seeking) { position = stream.position; } else if (!stream.seekable) { @@ -2516,7 +2508,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (!seeking) stream.position += bytesRead; return bytesRead; }, - write(stream, buffer, offset, length, position, canOwn) { + write(stream, buffer, offset, length, position = undefined, canOwn = undefined) { if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } @@ -2536,7 +2528,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // seek to the end before writing in append mode FS.llseek(stream, 0, 2); } - var seeking = typeof position != 'undefined'; + var seeking = typeof position !== 'undefined'; if (!seeking) { position = stream.position; } else if (!stream.seekable) { @@ -2553,8 +2545,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // to write to file opened in read-only mode with MAP_PRIVATE flag, // as all modifications will be visible only in the memory of // the current process. - if ((prot & 2) !== 0 - && (flags & 2) === 0 + if ((prot & 2) + && !(flags & 2) && (stream.flags & 2097155) !== 2) { throw new FS.ErrnoError(2); } @@ -2582,8 +2574,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return stream.stream_ops.ioctl(stream, cmd, arg); }, readFile(path, opts = {}) { - opts.flags = opts.flags || 0; - opts.encoding = opts.encoding || 'binary'; + opts.flags ??= 0; + opts.encoding ??= 'binary'; if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { abort(`Invalid encoding type "${opts.encoding}"`); } @@ -2599,7 +2591,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return buf; }, writeFile(path, data, opts = {}) { - opts.flags = opts.flags || 577; + opts.flags ??= 577; var stream = FS.open(path, opts.flags, opts.mode); data = FS_fileDataToTypedArray(data); FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); @@ -2646,7 +2638,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // use a buffer to avoid overhead of individual crypto calls per byte var randomBuffer = new Uint8Array(1024), randomLeft = 0; var randomByte = () => { - if (randomLeft === 0) { + if (!randomLeft) { randomFill(randomBuffer); randomLeft = randomBuffer.byteLength; } @@ -2725,19 +2717,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var stderr = FS.open('/dev/stderr', 1); }, staticInit() { - FS.nameTable = new Array(4096); + FS.nameTable.length = 4096; FS.mount(MEMFS, {}, '/'); FS.createDefaultDirectories(); FS.createDefaultDevices(); FS.createSpecialDirectories(); - - FS.filesystems = { - 'MEMFS': MEMFS, - }; }, - init(input, output, error) { + init(input = undefined, output = undefined, error = undefined) { FS.initialized = true; // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here @@ -2757,14 +2745,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - findObject(path, dontResolveLastLink) { - var ret = FS.analyzePath(path, dontResolveLastLink); - if (!ret.exists) { - return null; - } - return ret.object; - }, - analyzePath(path, dontResolveLastLink) { + analyzePath(path, dontResolveLastLink = false) { // operate from within the context of the symlink's target try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); @@ -2792,8 +2773,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; return ret; }, - createPath(parent, path, canRead, canWrite) { - parent = typeof parent == 'string' ? parent : FS.getPath(parent); + createPath(parent, path, canRead = undefined, canWrite = undefined) { + parent = typeof parent === 'string' ? parent : FS.getPath(parent); var parts = path.split('/').reverse(); while (parts.length) { var part = parts.pop(); @@ -2808,15 +2789,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return current; }, - createFile(parent, name, properties, canRead, canWrite) { - var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name); + createFile(parent, name, properties, canRead = undefined, canWrite = undefined) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS_getMode(canRead, canWrite); return FS.create(path, mode); }, - createDataFile(parent, name, data, canRead, canWrite, canOwn) { + createDataFile(parent, name, data = undefined, canRead = undefined, canWrite = undefined, canOwn = undefined) { var path = name; if (parent) { - parent = typeof parent == 'string' ? parent : FS.getPath(parent); + parent = typeof parent === 'string' ? parent : FS.getPath(parent); path = name ? PATH.join2(parent, name) : parent; } var mode = FS_getMode(canRead, canWrite); @@ -2831,8 +2812,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.chmod(node, mode); } }, - createDevice(parent, name, input, output) { - var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name); + createDevice(parent, name, input = undefined, output = undefined) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS_getMode(!!input, !!output); FS.createDevice.major ??= 64; var dev = FS.makedev(FS.createDevice.major++, 0); @@ -2857,7 +2838,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } catch (e) { throw new FS.ErrnoError(29); } - if (result === undefined && bytesRead === 0) { + if (result === undefined && !bytesRead) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; @@ -2888,7 +2869,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { forceLoadFile(obj) { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; if (globalThis.XMLHttpRequest) { - abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + abort('Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.'); } else { // Command-line. try { obj.contents = readBinary(obj.url); @@ -2897,7 +2878,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - createLazyFile(parent, name, url, canRead, canWrite) { + createLazyFile(parent, name, url, canRead = undefined, canWrite = undefined) { // Lazy chunked Uint8Array (implements get and length from Uint8Array). // Actual getting is abstracted away for eventual reuse. class LazyUint8Array { @@ -2919,11 +2900,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var xhr = new XMLHttpRequest(); xhr.open('HEAD', url, false); xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort("Couldn't load " + url + ". Status: " + xhr.status); - var datalength = Number(xhr.getResponseHeader("Content-length")); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort(`Couldn't load ${url}. Status: ${xhr.status}`); + var datalength = Number(xhr.getResponseHeader('Content-length')); var header; - var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; - var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; + var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes'; + var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip'; var chunkSize = 1024*1024; // Chunk size in bytes @@ -2931,13 +2912,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // Function to get a range from the remote URL. var doXHR = (from, to) => { - if (from > to) abort("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength-1) abort("only " + datalength + " bytes available! programmer error!"); + if (from > to) abort(`invalid range (${from}, ${to}) or no bytes requested!`); + if (to > datalength-1) abort(`only ${datalength} bytes available! programmer error!`); // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); - if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + if (datalength !== chunkSize) xhr.setRequestHeader('Range', `bytes=${from}-${to}`); // Some hints to the browser that we want binary data. xhr.responseType = 'arraybuffer'; @@ -2946,21 +2927,21 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort("Couldn't load " + url + ". Status: " + xhr.status); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort(`Couldn't load ${url}. Status: ${xhr.status}`); if (xhr.response !== undefined) { return new Uint8Array(/** @type{Array} */(xhr.response || [])); } - return intArrayFromString(xhr.responseText || '', true); + return intArrayFromString(xhr.responseText ?? '', true); }; var lazyArray = this; lazyArray.setDataGetter((chunkNum) => { var start = chunkNum * chunkSize; var end = (chunkNum+1) * chunkSize - 1; // including this byte end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block - if (typeof lazyArray.chunks[chunkNum] == 'undefined') { + if (typeof lazyArray.chunks[chunkNum] === 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end); } - if (typeof lazyArray.chunks[chunkNum] == 'undefined') abort('doXHR failed!'); + if (typeof lazyArray.chunks[chunkNum] === 'undefined') abort('doXHR failed!'); return lazyArray.chunks[chunkNum]; }); @@ -2969,7 +2950,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file datalength = this.getter(0).length; chunkSize = datalength; - out("LazyFiles on gzip forces download of the whole file when length is accessed"); + out('LazyFiles on gzip forces download of the whole file when length is accessed'); } this._length = datalength; @@ -3060,6 +3041,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + /** * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the * emscripten HEAP, returns a copy of that string as a Javascript String object. @@ -3078,8 +3060,19 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul); return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); }; + + + /** @type {!Int32Array} */ + var HEAP32; + + /** @type {!Uint32Array} */ + var HEAPU32; + + /** not-@type {!BigInt64Array} */ + var HEAP64; var SYSCALLS = { - calculateAt(dirfd, path, allowEmpty) { + currentUmask:18, + calculateAt(dirfd, path, allowEmpty = false) { if (PATH.isAbs(path)) { return path; } @@ -3141,7 +3134,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // MAP_PRIVATE calls need not to be synced back to underlying fs return 0; } - var buffer = HEAPU8.slice(addr, addr + len); + var buffer = HEAPU8.subarray(addr, addr + len); FS.msync(stream, buffer, offset, len, flags); }, getStreamFromFD(fd) { @@ -3161,10 +3154,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.chmod(path, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_faccessat(dirfd, path, amode, flags) { try { @@ -3189,10 +3183,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fchmod(fd, mode) { try { @@ -3200,10 +3195,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.fchmod(fd, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fchown32(fd, owner, group) { try { @@ -3211,10 +3207,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.fchown(fd, owner, group); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var syscallGetVarargI = () => { // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. @@ -3225,6 +3222,9 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var syscallGetVarargP = syscallGetVarargI; + + /** @type {!Int16Array} */ + var HEAP16; function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs; try { @@ -3250,7 +3250,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return stream.flags; case 4: { var arg = syscallGetVarargI(); - stream.flags |= arg; + var mask = 289792; + stream.flags = (stream.flags & ~mask) | (arg & mask); return 0; } case 12: { @@ -3270,20 +3271,22 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return -28; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fstat64(fd, buf) { try { return SYSCALLS.writeStat(buf, FS.fstat(fd)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var INT53_MAX = 9007199254740992; @@ -3295,35 +3298,42 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { - if (isNaN(length)) return -61; + if (isNaN(length)) return -22; FS.ftruncate(fd, length); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; } + var stringToUTF8 = (str, outPtr, maxBytesToWrite) => { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); }; function ___syscall_getcwd(buf, size) { try { - if (size === 0) return -28; + if (!size) return -28; var cwd = FS.cwd(); var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1; if (size < cwdLengthInBytes) return -68; stringToUTF8(cwd, buf, size); return cwdLengthInBytes; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + var ___syscall_geteuid32 = () => 0; + + + + function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs; @@ -3416,10 +3426,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { default: return -28; // not supported } } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_lstat64(path, buf) { try { @@ -3427,23 +3438,26 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); return SYSCALLS.writeStat(buf, FS.lstat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_mkdirat(dirfd, path, mode) { try { path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path); - FS.mkdir(path, mode, 0); + mode &= ~SYSCALLS.currentUmask; + FS.mkdir(path, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_newfstatat(dirfd, path, buf, flags) { try { @@ -3455,10 +3469,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_openat(dirfd, path, flags, varargs) { @@ -3468,15 +3483,20 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path); var mode = varargs ? syscallGetVarargI() : 0; + if (flags & 64) { + mode &= ~SYSCALLS.currentUmask; + } return FS.open(path, flags, mode).fd; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { @@ -3493,10 +3513,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAP8[buf+len] = endChar; return len; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_rmdir(path) { try { @@ -3505,10 +3526,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.rmdir(path); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_stat64(path, buf) { try { @@ -3516,10 +3538,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); return SYSCALLS.writeStat(buf, FS.stat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_unlinkat(dirfd, path, flags) { try { @@ -3535,18 +3558,22 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + var readI53FromI64 = (ptr) => { return HEAPU32[((ptr)>>2)] + HEAP32[(((ptr)+(4))>>2)] * 4294967296; }; + function ___syscall_utimensat(dirfd, path, times, flags) { try { + var nofollow = flags & 256; path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path, true); var now = Date.now(), atime, mtime; @@ -3554,37 +3581,32 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { atime = now; mtime = now; } else { - var seconds = readI53FromI64(times); - var nanoseconds = HEAP32[(((times)+(8))>>2)]; - if (nanoseconds == 1073741823) { - atime = now; - } else if (nanoseconds == 1073741822) { - atime = null; - } else { - atime = (seconds*1000) + (nanoseconds/(1000*1000)); - } - times += 16; - seconds = readI53FromI64(times); - nanoseconds = HEAP32[(((times)+(8))>>2)]; - if (nanoseconds == 1073741823) { - mtime = now; - } else if (nanoseconds == 1073741822) { - mtime = null; - } else { - mtime = (seconds*1000) + (nanoseconds/(1000*1000)); + function readTimespec(ptr) { + var tv_nsec = HEAP32[(((ptr)+(8))>>2)]; + if (tv_nsec == 1073741823) { + return now; + } + if (tv_nsec == 1073741822) { + return null; + } + var tv_sec = readI53FromI64(ptr); + return (tv_sec*1000) + Math.floor(tv_nsec/(1000*1000)); } + atime = readTimespec(times); + mtime = readTimespec(times + 16); } // null here means UTIME_OMIT was passed. If both were set to UTIME_OMIT then // we can skip the call completely. if ((mtime ?? atime) !== null) { - FS.utime(path, atime, mtime); + FS.utime(path, atime, mtime, nofollow); } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var isLeapYear = (year) => year%4 === 0 && (year%100 !== 0 || year%400 === 0); @@ -3599,11 +3621,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return yday; }; + function __localtime_js(time, tmPtr) { time = bigintToI53Checked(time); var date = new Date(time*1000); + if (isNaN(date.getTime())) { + return 1; + } HEAP32[((tmPtr)>>2)] = date.getSeconds(); HEAP32[(((tmPtr)+(4))>>2)] = date.getMinutes(); HEAP32[(((tmPtr)+(8))>>2)] = date.getHours(); @@ -3622,6 +3648,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var winterOffset = start.getTimezoneOffset(); var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0; HEAP32[(((tmPtr)+(32))>>2)] = dst; + return 0; ; } @@ -3630,6 +3657,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + + function __mmap_js(len, prot, flags, fd, offset, allocated, addr) { offset = bigintToI53Checked(offset); @@ -3643,7 +3672,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((addr)>>2)] = ptr; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; @@ -3661,12 +3690,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { SYSCALLS.doMsync(addr, stream, len, flags, offset); } } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; } + + var __tzset_js = (timezone, daylight, std_name, dst_name) => { // TODO: Use (malleable) environment variables instead of system settings. var currentYear = new Date().getFullYear(); @@ -3695,11 +3726,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var extractZone = (timezoneOffset) => { // Why inverse sign? // Read here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset - var sign = timezoneOffset >= 0 ? "-" : "+"; + var sign = timezoneOffset >= 0 ? '-' : '+'; var absOffset = Math.abs(timezoneOffset) - var hours = String(Math.floor(absOffset / 60)).padStart(2, "0"); - var minutes = String(absOffset % 60).padStart(2, "0"); + var hours = String(Math.floor(absOffset / 60)).padStart(2, '0'); + var minutes = String(absOffset % 60).padStart(2, '0'); return `UTC${sign}${hours}${minutes}`; } @@ -3724,6 +3755,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; + function _clock_time_get(clk_id, ignored_precision, ptime) { ignored_precision = bigintToI53Checked(ignored_precision); @@ -3741,7 +3773,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return 52; } // "now" is in ms, and wasi times are in ns. - var nsec = Math.round(now * 1000 * 1000); + var nsec = Math.round(now * 1_000_000); HEAP64[((ptime)>>3)] = BigInt(nsec); return 0; ; @@ -3769,9 +3801,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return 1 /*success*/; } catch(e) { } - // implicit 0 return to save code size (caller will cast "undefined" into 0 + // implicit 0 return to save code size (caller will cast 'undefined' into 0 // anyhow) }; + var _emscripten_resize_heap = (requestedSize) => { var oldSize = HEAPU8.length; // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. @@ -3825,11 +3858,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var ENV = { }; - var getExecutableName = () => thisProgram || './this.program'; + var getExecutableName = () => thisProgram; var getEnvStrings = () => { if (!getEnvStrings.strings) { // Default values. - // Browser language detection #8751 var lang = (globalThis.navigator?.language ?? 'C').replace('-', '_') + '.UTF-8'; var env = { 'USER': 'web_user', @@ -3857,6 +3889,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return getEnvStrings.strings; }; + var _environ_get = (__environ, environ_buf) => { var bufSize = 0; var envp = 0; @@ -3870,6 +3903,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; + var _environ_sizes_get = (penviron_count, penviron_buf_size) => { var strings = getEnvStrings(); HEAPU32[((penviron_count)>>2)] = strings.length; @@ -3888,11 +3922,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.close(stream); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + + function _fd_fdstat_get(fd, pbuf) { try { @@ -3914,11 +3952,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAP64[(((pbuf)+(16))>>3)] = BigInt(rightsInheriting); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + /** @param {number=} offset */ var doReadv = (stream, iov, iovcnt, offset) => { var ret = 0; @@ -3926,17 +3966,29 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var ptr = HEAPU32[((iov)>>2)]; var len = HEAPU32[(((iov)+(4))>>2)]; iov += 8; - var curr = FS.read(stream, HEAP8, ptr, len, offset); + try { + var curr = FS.read(stream, HEAP8, ptr, len, offset); + } catch (e) { + // On a non-blocking stream a subsequent read may would-block after we + // already gathered data. POSIX readv is a single gather-read: return + // what we have rather than failing the whole call. + if (ret > 0 && e instanceof FS.ErrnoError && + (e.errno == 6 || e.errno == 6)) { + break; + } + throw e; + } if (curr < 0) return -1; ret += curr; if (curr < len) break; // nothing more to read - if (typeof offset != 'undefined') { + if (typeof offset !== 'undefined') { offset += curr; } } return ret; }; + function _fd_read(fd, iov, iovcnt, pnum) { try { @@ -3945,26 +3997,28 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((pnum)>>2)] = num; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + function _fd_seek(fd, offset, whence, newOffset) { offset = bigintToI53Checked(offset); try { - if (isNaN(offset)) return 61; + if (isNaN(offset)) return 22; var stream = SYSCALLS.getStreamFromFD(fd); FS.llseek(stream, offset, whence); HEAP64[((newOffset)>>3)] = BigInt(stream.position); - if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; // reset readdir state + if (stream.getdents && !offset && whence === 0) stream.getdents = null; // reset readdir state return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } ; @@ -3977,32 +4031,40 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var rtn = stream.stream_ops?.fsync?.(stream); return rtn; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + /** @param {number=} offset */ var doWritev = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { + // Gather all iovecs into one contiguous buffer and issue a single + // FS.write, matching POSIX writev's single gather-write semantics (as + // __syscall_sendmsg already does). Per-iovec writes fragment a stream + // socket send into multiple segments, breaking stream byte semantics. + if (iovcnt == 1) { + // Single iovec: write directly from HEAP8, no gather buffer needed. + return FS.write(stream, HEAP8, HEAPU32[((iov)>>2)], HEAPU32[(((iov)+(4))>>2)], offset); + } + var total = 0; + for (var i = 0, p = iov; i < iovcnt; i++, p += 8) { + total += HEAPU32[(((p)+(4))>>2)]; + } + var view = new Uint8Array(total); + var voff = 0; + for (var i = 0; i < iovcnt; i++, iov += 8) { var ptr = HEAPU32[((iov)>>2)]; var len = HEAPU32[(((iov)+(4))>>2)]; - iov += 8; - var curr = FS.write(stream, HEAP8, ptr, len, offset); - if (curr < 0) return -1; - ret += curr; - if (curr < len) { - // No more space to write. - break; - } - if (typeof offset != 'undefined') { - offset += curr; - } + view.set(HEAPU8.subarray(ptr, ptr + len), voff); + voff += len; } - return ret; + return FS.write(stream, view, 0, total, offset); }; + function _fd_write(fd, iov, iovcnt, pnum) { try { @@ -4011,10 +4073,72 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((pnum)>>2)] = num; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + + + + + /** @type {!Float32Array} */ + var HEAPF32; + + /** @type {!Float64Array} */ + var HEAPF64; + + + /** + * @param {number} ptr + * @param {string} type + */ + function getValue(ptr, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': return HEAP8[ptr]; + case 'i8': return HEAP8[ptr]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': return HEAP64[((ptr)>>3)]; + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + case '*': return HEAPU32[((ptr)>>2)]; + default: abort(`invalid type for getValue: ${type}`); + } + } + + + + + + + + + /** + * @param {number} ptr + * @param {number} value + * @param {string} type + */ + function setValue(ptr, value, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': HEAP8[ptr] = value; break; + case 'i8': HEAP8[ptr] = value; break; + case 'i16': HEAP16[((ptr)>>1)] = value; break; + case 'i32': HEAP32[((ptr)>>2)] = value; break; + case 'i64': HEAP64[((ptr)>>3)] = BigInt(value); break; + case 'float': HEAPF32[((ptr)>>2)] = value; break; + case 'double': HEAPF64[((ptr)>>3)] = value; break; + case '*': HEAPU32[((ptr)>>2)] = value; break; + default: abort(`invalid type for setValue: ${type}`); + } + } + + + + FS.createPreloadedFile = FS_createPreloadedFile; @@ -4033,19 +4157,21 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // Begin ATMODULES hooks if (Module['noExitRuntime']) noExitRuntime = Module['noExitRuntime']; -if (Module['preloadPlugins']) preloadPlugins = Module['preloadPlugins']; + if (Module['print']) out = Module['print']; if (Module['printErr']) err = Module['printErr']; -if (Module['wasmBinary']) wasmBinary = Module['wasmBinary']; // End ATMODULES hooks - if (Module['arguments']) arguments_ = Module['arguments']; + if (Module['arguments']) programArgs = Module['arguments']; if (Module['thisProgram']) thisProgram = Module['thisProgram']; - if (Module['preInit']) { - if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; - while (Module['preInit'].length > 0) { - Module['preInit'].shift()(); + var preInit = Module['preInit']; + if (preInit) { + if (typeof preInit === 'function') Module['preInit'] = preInit = [preInit]; + // Written as a loop so that preInit functions that themselves add more + // preInit functions. Is this actually needed? + while (preInit.length > 0) { + preInit.shift()(); } } } @@ -4322,6 +4448,7 @@ var _sqlite3_status64, _free, _realloc, _emscripten_builtin_memalign, + _setThrew, __emscripten_stack_restore, __emscripten_stack_alloc, _emscripten_stack_get_current, @@ -4591,6 +4718,7 @@ function assignWasmExports(wasmExports) { _free = Module['_free'] = wasmExports['free']; _realloc = Module['_realloc'] = wasmExports['realloc']; _emscripten_builtin_memalign = wasmExports['emscripten_builtin_memalign']; + _setThrew = wasmExports['setThrew']; __emscripten_stack_restore = wasmExports['_emscripten_stack_restore']; __emscripten_stack_alloc = wasmExports['_emscripten_stack_alloc']; _emscripten_stack_get_current = wasmExports['emscripten_stack_get_current']; @@ -4615,6 +4743,8 @@ var wasmImports = { /** @export */ __syscall_getcwd: ___syscall_getcwd, /** @export */ + __syscall_geteuid32: ___syscall_geteuid32, + /** @export */ __syscall_ioctl: ___syscall_ioctl, /** @export */ __syscall_lstat64: ___syscall_lstat64, @@ -4676,55 +4806,38 @@ var wasmImports = { // include: postamble.js // === Auto-generated postamble setup entry stuff === -function run() { - - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; - } +async function run() { preRun(); - // a preRun added a dependency, run will be called later - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; + if (runDependencies) { + await resolveRunDependencies(); } - function doRun() { - // run may have just been called through dependencies being fulfilled just in this very frame, - // or while the async setStatus time below was happening - Module['calledRun'] = true; - - if (ABORT) return; + var setStatus = Module['setStatus']; + if (setStatus) { + setStatus('Running...'); + // Yield to the event loop to allow the browser to paint "Running..." + await new Promise((resolve) => setTimeout(resolve, 1)); + // Then we want to clear the status text, but only after the rest of this function runs. + setTimeout(setStatus, 1, ''); + } - initRuntime(); + if (ABORT) return; - readyPromiseResolve?.(Module); - Module['onRuntimeInitialized']?.(); + initRuntime(); - postRun(); - } + Module['onRuntimeInitialized']?.(); - if (Module['setStatus']) { - Module['setStatus']('Running...'); - setTimeout(() => { - setTimeout(() => Module['setStatus'](''), 1); - doRun(); - }, 1); - } else - { - doRun(); - } + postRun(); } var wasmExports; // In modularize mode the generated code is within a factory function so we // can use await here (since it's not top-level-await). -wasmExports = await (createWasm()); - -run(); +wasmExports = await createWasm(); +await run(); // end include: postamble.js @@ -4804,7 +4917,7 @@ Module.runSQLite3PostLoadInit = async function( ** SQLITE_VERSION_NUMBER 3054001 ** SQLITE_SOURCE_ID "2026-10-16 14:45:20 555e07ef33f69a0957d24d8e555b702464a131d6f1607f4c3c1e4551e8e6ca76" ** -** Emscripten SDK: 5.0.5 +** Emscripten SDK: 6.0.13 */ /* 2022-05-22 @@ -16305,25 +16418,12 @@ try{ // include: postamble_modularize.js // In MODULARIZE mode we wrap the generated code in a factory function // and return either the Module itself, or a promise of the module. -// -// We assign to the `moduleRtn` global here and configure closure to see -// this as an extern so it won't get minified. - -if (runtimeInitialized) { - moduleRtn = Module; -} else { - // Set up the promise that indicates the Module is initialized - moduleRtn = new Promise((resolve, reject) => { - readyPromiseResolve = resolve; - readyPromiseReject = reject; - }); -} // end include: postamble_modularize.js - return moduleRtn; + return Module; } // Export using a UMD style export, or ES6 exports if selected diff --git a/src/bin/sqlite3.mjs b/src/bin/sqlite3.mjs index 8936330..09b4712 100644 --- a/src/bin/sqlite3.mjs +++ b/src/bin/sqlite3.mjs @@ -31,7 +31,7 @@ ** SQLITE_VERSION_NUMBER 3054001 ** SQLITE_SOURCE_ID "2026-10-16 14:45:20 555e07ef33f69a0957d24d8e555b702464a131d6f1607f4c3c1e4551e8e6ca76" ** -** Emscripten SDK: 5.0.5 +** Emscripten SDK: 6.0.13 */ // This code implements the `-sMODULARIZE` settings by taking the generated // JS program code (INNER_JS_CODE) and wrapping it in a factory function. @@ -39,8 +39,7 @@ // When targeting node and ES6 we use `await import ..` in the generated code // so the outer function needs to be marked as async. async function sqlite3InitModule(moduleArg = {}) { - var moduleRtn; - + var Module = moduleArg; // include: shell.js // include: minimum_runtime_check.js // end include: minimum_runtime_check.js @@ -57,7 +56,6 @@ async function sqlite3InitModule(moduleArg = {}) { // after the generated code, you will need to define var Module = {}; // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. -var Module = moduleArg; // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). @@ -189,7 +187,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR // end include: ./bld/pre-js.esm.js -var arguments_ = []; +var programArgs = []; var thisProgram = './this.program'; var quit_ = (status, toThrow) => { throw toThrow; @@ -299,8 +297,6 @@ function assert(condition, text) { var isFileURI = (filename) => filename.startsWith('file://'); // include: runtime_common.js -// include: runtime_stack_check.js -// end include: runtime_stack_check.js // include: runtime_exceptions.js // Base Emscripten EH error class class EmscriptenEH {} @@ -310,34 +306,39 @@ class EmscriptenSjLj extends EmscriptenEH {} // end include: runtime_exceptions.js // include: runtime_debug.js // end include: runtime_debug.js -var readyPromiseResolve, readyPromiseReject; - // Memory management var runtimeInitialized = false; +// When ALLOW_MEMORY_GROWTH is enabled, the conversion from Wasm +// memory to ArrayBuffer requires some additional logic. +function getMemoryBuffer() { + return wasmMemory.buffer; +} + function updateMemoryViews() { - var b = wasmMemory.buffer; + // If we already have a heap that is resizeable/growable buffer we don't + // need to do anything in updateMemoryViews. + if (HEAP8?.buffer?.resizable) return; + var b = getMemoryBuffer(); 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); + } // In non-standalone/normal mode, we create the memory here. // include: runtime_init_memory.js // Create the wasm memory. (Note: this only applies if IMPORTED_MEMORY is defined) -// check for full engine support (use string 'subarray' to avoid closure compiler confusion) - function initMemory() { @@ -369,11 +370,10 @@ function initMemory() { // end include: memoryprofiler.js // end include: runtime_common.js function preRun() { - if (Module['preRun']) { - if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; - while (Module['preRun'].length) { - addOnPreRun(Module['preRun'].shift()); - } + var preRun = Module['preRun']; + if (preRun) { + if (typeof preRun === 'function') preRun = [preRun]; + onPreRuns.push(...preRun); } // Begin ATPRERUNS hooks callRuntimeCallbacks(onPreRuns); @@ -393,16 +393,15 @@ TTY.init(); // Begin ATPOSTCTORS hooks FS.ignorePermissions = false; // End ATPOSTCTORS hooks + } function postRun() { - // PThreads reuse the runtime from the main thread. - if (Module['postRun']) { - if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; - while (Module['postRun'].length) { - addOnPostRun(Module['postRun'].shift()); - } + var postRun = Module['postRun']; + if (postRun) { + if (typeof postRun === 'function') postRun = [postRun]; + onPostRuns.push(...postRun); } // Begin ATPOSTRUNS hooks @@ -412,7 +411,6 @@ function postRun() { /** * @param {string|number=} what - * @noreturn */ function abort(what) { Module['onAbort']?.(what); @@ -434,19 +432,7 @@ function abort(what) { // a trap or not based on a hidden field within the object. So at the moment // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that // allows this in the wasm spec. - - // Suppress closure compiler warning here. Closure compiler's builtin extern - // definition for WebAssembly.RuntimeError claims it takes no arguments even - // though it can. - // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. - /** @suppress {checkTypes} */ - var e = new WebAssembly.RuntimeError(what); - - readyPromiseReject?.(e); - // Throw the error whether or not MODULARIZE is set because abort is used - // in code paths apart from instantiation where an exception is expected - // to be thrown when abort is called. - throw e; + throw new WebAssembly.RuntimeError(what); } var wasmBinaryFile; @@ -463,9 +449,6 @@ function findWasmBinary() { } function getBinarySync(file) { - if (file == wasmBinaryFile && wasmBinary) { - return new Uint8Array(wasmBinary); - } if (readBinary) { return readBinary(file); } @@ -535,8 +518,7 @@ async function createWasm() { // Load the wasm module and create an instance of using native support in the JS engine. // handle a generated wasm instance, receiving its exports and // performing other necessary setup - /** @param {WebAssembly.Module=} module*/ - function receiveInstance(instance, module) { + function receiveInstance(instance) { wasmExports = instance.exports; assignWasmExports(wasmExports); @@ -561,11 +543,10 @@ async function createWasm() { // performing. // Also pthreads and wasm workers initialize the wasm instance through this // path. - if (Module['instantiateWasm']) { - return new Promise((resolve, reject) => { - Module['instantiateWasm'](info, (inst, mod) => { - resolve(receiveInstance(inst, mod)); - }); + var instantiateWasm = Module['instantiateWasm']; + if (instantiateWasm) { + return new Promise((resolve) => { + instantiateWasm(info, (inst) => resolve(receiveInstance(inst))); }); } @@ -588,36 +569,9 @@ async function createWasm() { } } - /** @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) { // Pass the module as the first argument. @@ -631,49 +585,8 @@ async function createWasm() { var addOnPreRun = (cb) => onPreRuns.push(cb); - - /** - * @param {number} ptr - * @param {string} type - */ - function getValue(ptr, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': return HEAP8[ptr]; - case 'i8': return HEAP8[ptr]; - case 'i16': return HEAP16[((ptr)>>1)]; - case 'i32': return HEAP32[((ptr)>>2)]; - case 'i64': return HEAP64[((ptr)>>3)]; - case 'float': return HEAPF32[((ptr)>>2)]; - case 'double': return HEAPF64[((ptr)>>3)]; - case '*': return HEAPU32[((ptr)>>2)]; - default: abort(`invalid type for getValue: ${type}`); - } - } - var noExitRuntime = true; - - /** - * @param {number} ptr - * @param {number} value - * @param {string} type - */ - function setValue(ptr, value, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': HEAP8[ptr] = value; break; - case 'i8': HEAP8[ptr] = value; break; - case 'i16': HEAP16[((ptr)>>1)] = value; break; - case 'i32': HEAP32[((ptr)>>2)] = value; break; - case 'i64': HEAP64[((ptr)>>3)] = BigInt(value); break; - case 'float': HEAPF32[((ptr)>>2)] = value; break; - case 'double': HEAPF64[((ptr)>>3)] = value; break; - case '*': HEAPU32[((ptr)>>2)] = value; break; - default: abort(`invalid type for setValue: ${type}`); - } - } - var stackRestore = (val) => __emscripten_stack_restore(val); var stackSave = () => _emscripten_stack_get_current(); @@ -756,7 +669,7 @@ resolve:(...args) => { for (var i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = (i >= 0) ? args[i] : FS.cwd(); // Skip empty and invalid entries - if (typeof path != 'string') { + if (typeof path !== 'string') { throw new TypeError('Arguments to path.resolve must be strings'); } else if (!path) { return ''; // an invalid portion invalidates the whole thing @@ -806,18 +719,26 @@ relative:(from, to) => { var UTF8Decoder = new TextDecoder(); -var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { - var maxIdx = idx + maxBytesToRead; - if (ignoreNul) return maxIdx; - // TextDecoder needs to know the byte length in advance, it doesn't stop on - // null terminator by itself. - // As a tiny code save trick, compare idx against maxIdx using a negation, - // so that maxBytesToRead=undefined/NaN means Infinity. - while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; - return idx; - }; /** + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul + * @return {number} + */ + var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + var maxIdx = idx + maxBytesToRead; + if (ignoreNul) return maxIdx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. + // As a tiny code save trick, compare idx against maxIdx using a negation, + // so that maxBytesToRead=undefined/NaN means Infinity. + 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. @@ -978,12 +899,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } catch (e) { throw new FS.ErrnoError(29); } - if (result === undefined && bytesRead === 0) { + if (result === undefined && !bytesRead) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; bytesRead++; buffer[offset+i] = result; + // We currently only support canonical mode (ICANON), where + // read(2) returns as soon as a line delimiter is read. + if (result === 10) break; } if (bytesRead) { stream.node.atime = Date.now(); @@ -1066,6 +990,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; + /** @type {!Uint8Array} */ + var HEAPU8; var zeroMemory = (ptr, size) => HEAPU8.fill(0, ptr, ptr + size); var alignMemory = (size, alignment) => { @@ -1077,12 +1003,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 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) { + createNode(parent, name, mode, dev = undefined) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { // not supported throw new FS.ErrnoError(63); @@ -1175,7 +1102,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // For small filesizes (<1MB), perform size*2 geometric increase, but for // large sizes, do a much more conservative size*1.125 increase to avoid // overshooting the allocation cap by a very large margin. - var CAPACITY_DOUBLING_MAX = 1024 * 1024; + const CAPACITY_DOUBLING_MAX = 1024 * 1024; newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); if (prevCapacity) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. var oldContents = MEMFS.getFileDataAsTypedArray(node); @@ -1219,7 +1146,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return attr; }, setattr(node, attr) { - for (const key of ["mode", "atime", "mtime", "ctime"]) { + for (const key of ['mode', 'atime', 'mtime', 'ctime']) { if (attr[key] != null) { node[key] = attr[key]; } @@ -1238,7 +1165,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } throw MEMFS.doesNotExistError; }, - mknod(parent, name, mode, dev) { + mknod(parent, name, mode, dev = undefined) { return MEMFS.createNode(parent, name, mode, dev); }, rename(old_node, new_dir, new_name) { @@ -1246,14 +1173,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) {} - if (new_node) { - if (FS.isDir(old_node.mode)) { - // if we're overwriting a directory at new_name, make sure it's empty. - for (var i in new_node.contents) { - throw new FS.ErrnoError(55); - } + if (new_node && FS.isDir(old_node.mode)) { + // if we're overwriting a directory at new_name, make sure it's empty. + for (var i in new_node.contents) { + throw new FS.ErrnoError(55); } - FS.hashRemoveNode(new_node); } // do the internal rewiring delete old_node.parent.contents[old_node.name]; @@ -1312,7 +1236,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (canOwn) { node.contents = buffer.subarray(offset, offset + length); node.usedBytes = length; - } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + } else if (!node.usedBytes && !position) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. node.contents = buffer.slice(offset, offset + length); node.usedBytes = length; } else { @@ -1379,7 +1303,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; var FS_modeStringToFlags = (str) => { - if (typeof str != 'string') return str; + if (typeof str !== 'string') return str; var flagModes = { 'r': 0, 'r+': 2, @@ -1389,14 +1313,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 'a+': 1024 | 64 | 2, }; var flags = flagModes[str]; - if (typeof flags == 'undefined') { + if (typeof flags === 'undefined') { throw new Error(`Unknown file open mode: ${str}`); } return flags; }; var FS_fileDataToTypedArray = (data) => { - if (typeof data == 'string') { + if (typeof data === 'string') { data = intArrayFromString(data, true); } if (!data.subarray) { @@ -1425,24 +1349,27 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return id; }; + var dependenciesPromise = null; + var resolveRunDependencies = async () => dependenciesPromise; var runDependencies = 0; - var dependenciesFulfilled = null; + var dependenciesPromiseResolve = null; var removeRunDependency = (id) => { runDependencies--; Module['monitorRunDependencies']?.(runDependencies); - if (runDependencies == 0) { - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); // can add another dependenciesFulfilled - } + if (!runDependencies) { + dependenciesPromiseResolve(); } }; + + var addRunDependency = (id) => { + if (!runDependencies) { + dependenciesPromise = new Promise((resolve) => dependenciesPromiseResolve = resolve); + } runDependencies++; Module['monitorRunDependencies']?.(runDependencies); @@ -1453,7 +1380,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var preloadPlugins = []; var FS_handledByPreloadPlugin = async (byteArray, fullname) => { // Ensure plugins are ready. - if (typeof Browser != 'undefined') Browser.init(); + if (typeof Browser !== 'undefined') Browser.init(); for (var plugin of preloadPlugins) { if (plugin['canHandle'](fullname)) { @@ -1473,7 +1400,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { var byteArray = url; - if (typeof url == 'string') { + if (typeof url === 'string') { byteArray = await asyncLoad(url); } @@ -1489,6 +1416,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { 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:[], @@ -1496,11 +1424,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }, streams:[], nextInode:1, - nameTable:null, + nameTable:[], currentPath:"/", initialized:false, ignorePermissions:true, - filesystems:null, syncFSRequests:0, ErrnoError:class { name = 'ErrnoError'; @@ -1580,6 +1507,48 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { get isDevice() { return FS.isChrdev(this.mode); } + // The per-inode readiness wait-queue. The node carries a Set of listener + // entries {cb}; producers (SOCKFS, PIPEFS) call notifyListeners on a + // readiness transition, and poll()/epoll consume it. It lives on the node + // (not the fd) so dup'd fds share one queue. Only nodes that derive real + // readiness (sockets, pipes, and an epoll's own node) ever use this - + // always-ready types (regular files, ttys) never register or notify. + addListener(cb, exclusive = false) { + var entry = {cb, exclusive}; + var listeners = (this.listeners ??= new Set()); + listeners.add(entry); + return {listeners, entry}; + } + notifyListeners(flags) { + // Iterates the set without copying, which is safe ONLY under a + // load-bearing contract that every internal listener must honour: + // 1. A listener must not run user code synchronously (a poll waiter only + // resolves a Promise; an epoll registration only re-lists + + // re-notifies; the epoll callback only schedules a tick). User code + // runs on a later tick, never inside this loop. + // 2. A listener may delete entries only from ITS OWN waiter, never from + // a sibling node's set that may be mid-iteration. (Deleting an entry + // of the set being iterated here is fine - a Set tolerates removal of + // a not-yet-visited entry mid-iteration; mutating a *different* node's + // set is fine because that set is not being iterated.) + // Violating either gives silently skipped wakeups that are near-impossible + // to reproduce. Any new producer/listener must preserve it. + if (!this.listeners) return; + // Fire every non-exclusive listener. Among EPOLLEXCLUSIVE registrations + // (one fd watched by several epolls) wake only one, rotating round-robin + // per node, to avoid a thundering herd. (Only epoll registrations are ever + // exclusive; poll waiters and a node's own consumers are not.) + var excl; + for (var entry of this.listeners) { + if (entry.exclusive) (excl ||= []).push(entry); + else entry.cb(flags); + } + if (excl) { + var i = (this.exclTurn || 0) % excl.length; + this.exclTurn = i + 1; + excl[i].cb(flags); + } + } }, lookupPath(path, opts = {}) { if (!path) { @@ -1591,7 +1560,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = FS.cwd() + '/' + path; } - // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + // limit max consecutive symlinks to SYMLOOP_MAX. linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { // split the absolute path var parts = path.split('/').filter((p) => !!p); @@ -1716,7 +1685,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // if we failed to find it in the cache, call into the VFS return FS.lookup(parent, name); }, - createNode(parent, name, mode, rdev) { + createNode(parent, name, mode, rdev = undefined) { var node = new FS.FSNode(parent, name, mode, rdev); FS.hashAddNode(node); @@ -1881,7 +1850,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var arg = setattr ? stream : node; setattr ??= node.node_ops.setattr; FS.checkOpExists(setattr, 63) - setattr(arg, attr); + try { + setattr(arg, attr); + } catch (e) { + if (e instanceof RangeError) { + throw new FS.ErrnoError(22); + } + throw e; + } }, chrdev_stream_ops:{ open(stream) { @@ -1917,7 +1893,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return mounts; }, syncfs(populate, callback) { - if (typeof populate == 'function') { + if (typeof populate === 'function') { callback = populate; populate = false; } @@ -2075,9 +2051,9 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var rtn = { bsize: 4096, frsize: 4096, - blocks: 1e6, - bfree: 5e5, - bavail: 5e5, + blocks: 1_000_000, + bfree: 500_000, + bavail: 500_000, files: FS.nextInode, ffree: FS.nextInode - 1, fsid: 42, @@ -2100,7 +2076,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { mode |= 16384; return FS.mknod(path, mode, 0); }, - mkdirTree(path, mode) { + mkdirTree(path, mode = 0o777) { var dirs = path.split('/'); var d = ''; for (var dir of dirs) { @@ -2114,8 +2090,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - mkdev(path, mode, dev) { - if (typeof dev == 'undefined') { + mkdev(path, mode, dev = undefined) { + if (typeof dev === 'undefined') { dev = mode; mode = 0o666; } @@ -2141,6 +2117,25 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return parent.node_ops.symlink(parent, newname, oldpath); }, + link(oldpath, newpath, flags) { + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.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); + } + // Hardlinks are only supported by filesystem backends that provide a + // `link` node op (e.g. NODERAWFS backed by the host). NODEFS omits it: + // a host hardlink cannot be confined to the mount root. + if (!parent.node_ops.link) { + throw new FS.ErrnoError(34); + } + return parent.node_ops.link(parent, newname, oldpath, flags); + }, rename(old_path, new_path) { var old_dirname = PATH.dirname(old_path); var new_dirname = PATH.dirname(new_path); @@ -2215,6 +2210,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // do the underlying fs rename try { old_dir.node_ops.rename(old_node, new_dir, new_name); + // The replaced node is stale now. Evict it only after the rename + // succeeded: backends like NODEFS report node.id as st_ino. + if (new_node) { + FS.hashRemoveNode(new_node); + } // update old node (we do this here to avoid each backend // needing to) old_node.parent = new_dir; @@ -2285,7 +2285,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return link.node_ops.readlink(link); }, - stat(path, dontFollow) { + stat(path, dontFollow = false) { var lookup = FS.lookupPath(path, { follow: !dontFollow }); var node = lookup.node; var getattr = FS.checkOpExists(node.node_ops.getattr, 63); @@ -2303,16 +2303,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { lstat(path) { return FS.stat(path, true); }, - doChmod(stream, node, mode, dontFollow) { + doChmod(stream, node, mode, dontFollow = false) { FS.doSetAttr(stream, node, { mode: (mode & 4095) | (node.mode & ~4095), ctime: Date.now(), dontFollow }); }, - chmod(path, mode, dontFollow) { + chmod(path, mode, dontFollow = false) { var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { @@ -2327,16 +2327,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var stream = FS.getStreamChecked(fd); FS.doChmod(stream, stream.node, mode, false); }, - doChown(stream, node, dontFollow) { + doChown(stream, node, dontFollow = false) { FS.doSetAttr(stream, node, { timestamp: Date.now(), dontFollow // we ignore the uid / gid for now }); }, - chown(path, uid, gid, dontFollow) { + chown(path, uid, gid, dontFollow = false) { var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { @@ -2372,7 +2372,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { throw new FS.ErrnoError(28); } var node; - if (typeof path == 'string') { + if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: true }); node = lookup.node; } else { @@ -2387,17 +2387,16 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } FS.doTruncate(stream, stream.node, len); }, - utime(path, atime, mtime) { - var lookup = FS.lookupPath(path, { follow: true }); - var node = lookup.node; - var setattr = FS.checkOpExists(node.node_ops.setattr, 63); - setattr(node, { + utime(path, atime, mtime, dontFollow = false) { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + FS.doSetAttr(null, lookup.node, { atime: atime, - mtime: mtime + mtime: mtime, + dontFollow }); }, open(path, flags, mode = 0o666) { - if (path === "") { + if (path === '') { throw new FS.ErrnoError(44); } flags = FS_modeStringToFlags(flags); @@ -2408,10 +2407,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } var node; var isDirPath; - if (typeof path == 'object') { + if (typeof path === 'object') { node = path; } else { - isDirPath = path.endsWith("/"); + isDirPath = path.endsWith('/'); // noent_okay makes it so that if the final component of the path // doesn't exist, lookupPath returns `node: undefined`. `path` will be // updated to point to the target of all symlinks. @@ -2494,6 +2493,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { throw new FS.ErrnoError(8); } if (stream.getdents) stream.getdents = null; // free readdir state + // The fd is going away: wake anything waiting on it (poll/epoll) with + // POLLNVAL so a blocking wait unblocks and an epoll registration is evicted + // on its next derive. Only sockets/pipes/epoll ever carry a wait-queue, so + // for every other stream (incl. nodeless noderawfs stdio) this is a no-op. + stream.node?.notifyListeners(32); try { if (stream.stream_ops.close) { stream.stream_ops.close(stream); @@ -2522,7 +2526,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { stream.ungotten = []; return stream.position; }, - read(stream, buffer, offset, length, position) { + read(stream, buffer, offset, length, position = undefined) { if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } @@ -2538,7 +2542,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (!stream.stream_ops.read) { throw new FS.ErrnoError(28); } - var seeking = typeof position != 'undefined'; + var seeking = typeof position !== 'undefined'; if (!seeking) { position = stream.position; } else if (!stream.seekable) { @@ -2548,7 +2552,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { if (!seeking) stream.position += bytesRead; return bytesRead; }, - write(stream, buffer, offset, length, position, canOwn) { + write(stream, buffer, offset, length, position = undefined, canOwn = undefined) { if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } @@ -2568,7 +2572,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // seek to the end before writing in append mode FS.llseek(stream, 0, 2); } - var seeking = typeof position != 'undefined'; + var seeking = typeof position !== 'undefined'; if (!seeking) { position = stream.position; } else if (!stream.seekable) { @@ -2585,8 +2589,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // to write to file opened in read-only mode with MAP_PRIVATE flag, // as all modifications will be visible only in the memory of // the current process. - if ((prot & 2) !== 0 - && (flags & 2) === 0 + if ((prot & 2) + && !(flags & 2) && (stream.flags & 2097155) !== 2) { throw new FS.ErrnoError(2); } @@ -2614,8 +2618,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return stream.stream_ops.ioctl(stream, cmd, arg); }, readFile(path, opts = {}) { - opts.flags = opts.flags || 0; - opts.encoding = opts.encoding || 'binary'; + opts.flags ??= 0; + opts.encoding ??= 'binary'; if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { abort(`Invalid encoding type "${opts.encoding}"`); } @@ -2631,7 +2635,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return buf; }, writeFile(path, data, opts = {}) { - opts.flags = opts.flags || 577; + opts.flags ??= 577; var stream = FS.open(path, opts.flags, opts.mode); data = FS_fileDataToTypedArray(data); FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); @@ -2678,7 +2682,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // use a buffer to avoid overhead of individual crypto calls per byte var randomBuffer = new Uint8Array(1024), randomLeft = 0; var randomByte = () => { - if (randomLeft === 0) { + if (!randomLeft) { randomFill(randomBuffer); randomLeft = randomBuffer.byteLength; } @@ -2757,19 +2761,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var stderr = FS.open('/dev/stderr', 1); }, staticInit() { - FS.nameTable = new Array(4096); + FS.nameTable.length = 4096; FS.mount(MEMFS, {}, '/'); FS.createDefaultDirectories(); FS.createDefaultDevices(); FS.createSpecialDirectories(); - - FS.filesystems = { - 'MEMFS': MEMFS, - }; }, - init(input, output, error) { + init(input = undefined, output = undefined, error = undefined) { FS.initialized = true; // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here @@ -2789,14 +2789,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - findObject(path, dontResolveLastLink) { - var ret = FS.analyzePath(path, dontResolveLastLink); - if (!ret.exists) { - return null; - } - return ret.object; - }, - analyzePath(path, dontResolveLastLink) { + analyzePath(path, dontResolveLastLink = false) { // operate from within the context of the symlink's target try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); @@ -2824,8 +2817,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; return ret; }, - createPath(parent, path, canRead, canWrite) { - parent = typeof parent == 'string' ? parent : FS.getPath(parent); + createPath(parent, path, canRead = undefined, canWrite = undefined) { + parent = typeof parent === 'string' ? parent : FS.getPath(parent); var parts = path.split('/').reverse(); while (parts.length) { var part = parts.pop(); @@ -2840,15 +2833,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return current; }, - createFile(parent, name, properties, canRead, canWrite) { - var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name); + createFile(parent, name, properties, canRead = undefined, canWrite = undefined) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS_getMode(canRead, canWrite); return FS.create(path, mode); }, - createDataFile(parent, name, data, canRead, canWrite, canOwn) { + createDataFile(parent, name, data = undefined, canRead = undefined, canWrite = undefined, canOwn = undefined) { var path = name; if (parent) { - parent = typeof parent == 'string' ? parent : FS.getPath(parent); + parent = typeof parent === 'string' ? parent : FS.getPath(parent); path = name ? PATH.join2(parent, name) : parent; } var mode = FS_getMode(canRead, canWrite); @@ -2863,8 +2856,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.chmod(node, mode); } }, - createDevice(parent, name, input, output) { - var path = PATH.join2(typeof parent == 'string' ? parent : FS.getPath(parent), name); + createDevice(parent, name, input = undefined, output = undefined) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS_getMode(!!input, !!output); FS.createDevice.major ??= 64; var dev = FS.makedev(FS.createDevice.major++, 0); @@ -2889,7 +2882,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } catch (e) { throw new FS.ErrnoError(29); } - if (result === undefined && bytesRead === 0) { + if (result === undefined && !bytesRead) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; @@ -2920,7 +2913,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { forceLoadFile(obj) { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; if (globalThis.XMLHttpRequest) { - abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + abort('Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.'); } else { // Command-line. try { obj.contents = readBinary(obj.url); @@ -2929,7 +2922,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } } }, - createLazyFile(parent, name, url, canRead, canWrite) { + createLazyFile(parent, name, url, canRead = undefined, canWrite = undefined) { // Lazy chunked Uint8Array (implements get and length from Uint8Array). // Actual getting is abstracted away for eventual reuse. class LazyUint8Array { @@ -2951,11 +2944,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var xhr = new XMLHttpRequest(); xhr.open('HEAD', url, false); xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort("Couldn't load " + url + ". Status: " + xhr.status); - var datalength = Number(xhr.getResponseHeader("Content-length")); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort(`Couldn't load ${url}. Status: ${xhr.status}`); + var datalength = Number(xhr.getResponseHeader('Content-length')); var header; - var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; - var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; + var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes'; + var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip'; var chunkSize = 1024*1024; // Chunk size in bytes @@ -2963,13 +2956,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // Function to get a range from the remote URL. var doXHR = (from, to) => { - if (from > to) abort("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength-1) abort("only " + datalength + " bytes available! programmer error!"); + if (from > to) abort(`invalid range (${from}, ${to}) or no bytes requested!`); + if (to > datalength-1) abort(`only ${datalength} bytes available! programmer error!`); // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); - if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + if (datalength !== chunkSize) xhr.setRequestHeader('Range', `bytes=${from}-${to}`); // Some hints to the browser that we want binary data. xhr.responseType = 'arraybuffer'; @@ -2978,21 +2971,21 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort("Couldn't load " + url + ". Status: " + xhr.status); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) abort(`Couldn't load ${url}. Status: ${xhr.status}`); if (xhr.response !== undefined) { return new Uint8Array(/** @type{Array} */(xhr.response || [])); } - return intArrayFromString(xhr.responseText || '', true); + return intArrayFromString(xhr.responseText ?? '', true); }; var lazyArray = this; lazyArray.setDataGetter((chunkNum) => { var start = chunkNum * chunkSize; var end = (chunkNum+1) * chunkSize - 1; // including this byte end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block - if (typeof lazyArray.chunks[chunkNum] == 'undefined') { + if (typeof lazyArray.chunks[chunkNum] === 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end); } - if (typeof lazyArray.chunks[chunkNum] == 'undefined') abort('doXHR failed!'); + if (typeof lazyArray.chunks[chunkNum] === 'undefined') abort('doXHR failed!'); return lazyArray.chunks[chunkNum]; }); @@ -3001,7 +2994,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file datalength = this.getter(0).length; chunkSize = datalength; - out("LazyFiles on gzip forces download of the whole file when length is accessed"); + out('LazyFiles on gzip forces download of the whole file when length is accessed'); } this._length = datalength; @@ -3092,6 +3085,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + /** * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the * emscripten HEAP, returns a copy of that string as a Javascript String object. @@ -3110,8 +3104,19 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var end = findStringEnd(HEAPU8, ptr, maxBytesToRead, ignoreNul); return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); }; + + + /** @type {!Int32Array} */ + var HEAP32; + + /** @type {!Uint32Array} */ + var HEAPU32; + + /** not-@type {!BigInt64Array} */ + var HEAP64; var SYSCALLS = { - calculateAt(dirfd, path, allowEmpty) { + currentUmask:18, + calculateAt(dirfd, path, allowEmpty = false) { if (PATH.isAbs(path)) { return path; } @@ -3173,7 +3178,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // MAP_PRIVATE calls need not to be synced back to underlying fs return 0; } - var buffer = HEAPU8.slice(addr, addr + len); + var buffer = HEAPU8.subarray(addr, addr + len); FS.msync(stream, buffer, offset, len, flags); }, getStreamFromFD(fd) { @@ -3193,10 +3198,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.chmod(path, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_faccessat(dirfd, path, amode, flags) { try { @@ -3221,10 +3227,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fchmod(fd, mode) { try { @@ -3232,10 +3239,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.fchmod(fd, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fchown32(fd, owner, group) { try { @@ -3243,10 +3251,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.fchown(fd, owner, group); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var syscallGetVarargI = () => { // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. @@ -3257,6 +3266,9 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var syscallGetVarargP = syscallGetVarargI; + + /** @type {!Int16Array} */ + var HEAP16; function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs; try { @@ -3282,7 +3294,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return stream.flags; case 4: { var arg = syscallGetVarargI(); - stream.flags |= arg; + var mask = 289792; + stream.flags = (stream.flags & ~mask) | (arg & mask); return 0; } case 12: { @@ -3302,20 +3315,22 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return -28; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_fstat64(fd, buf) { try { return SYSCALLS.writeStat(buf, FS.fstat(fd)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var INT53_MAX = 9007199254740992; @@ -3327,36 +3342,43 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { try { - if (isNaN(length)) return -61; + if (isNaN(length)) return -22; FS.ftruncate(fd, length); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; } + var stringToUTF8 = (str, outPtr, maxBytesToWrite) => { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); }; function ___syscall_getcwd(buf, size) { try { - if (size === 0) return -28; + if (!size) return -28; var cwd = FS.cwd(); var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1; if (size < cwdLengthInBytes) return -68; stringToUTF8(cwd, buf, size); return cwdLengthInBytes; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + + var ___syscall_geteuid32 = () => 0; + + + function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs; try { @@ -3448,10 +3470,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { default: return -28; // not supported } } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_lstat64(path, buf) { try { @@ -3459,23 +3482,26 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); return SYSCALLS.writeStat(buf, FS.lstat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_mkdirat(dirfd, path, mode) { try { path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path); - FS.mkdir(path, mode, 0); + mode &= ~SYSCALLS.currentUmask; + FS.mkdir(path, mode); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_newfstatat(dirfd, path, buf, flags) { try { @@ -3487,10 +3513,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_openat(dirfd, path, flags, varargs) { @@ -3500,15 +3527,20 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path); var mode = varargs ? syscallGetVarargI() : 0; + if (flags & 64) { + mode &= ~SYSCALLS.currentUmask; + } return FS.open(path, flags, mode).fd; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { @@ -3525,10 +3557,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAP8[buf+len] = endChar; return len; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_rmdir(path) { try { @@ -3537,10 +3570,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.rmdir(path); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_stat64(path, buf) { try { @@ -3548,10 +3582,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { path = SYSCALLS.getStr(path); return SYSCALLS.writeStat(buf, FS.stat(path)); } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + function ___syscall_unlinkat(dirfd, path, flags) { try { @@ -3567,18 +3602,22 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + + var readI53FromI64 = (ptr) => { return HEAPU32[((ptr)>>2)] + HEAP32[(((ptr)+(4))>>2)] * 4294967296; }; + function ___syscall_utimensat(dirfd, path, times, flags) { try { + var nofollow = flags & 256; path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path, true); var now = Date.now(), atime, mtime; @@ -3586,37 +3625,32 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { atime = now; mtime = now; } else { - var seconds = readI53FromI64(times); - var nanoseconds = HEAP32[(((times)+(8))>>2)]; - if (nanoseconds == 1073741823) { - atime = now; - } else if (nanoseconds == 1073741822) { - atime = null; - } else { - atime = (seconds*1000) + (nanoseconds/(1000*1000)); - } - times += 16; - seconds = readI53FromI64(times); - nanoseconds = HEAP32[(((times)+(8))>>2)]; - if (nanoseconds == 1073741823) { - mtime = now; - } else if (nanoseconds == 1073741822) { - mtime = null; - } else { - mtime = (seconds*1000) + (nanoseconds/(1000*1000)); + function readTimespec(ptr) { + var tv_nsec = HEAP32[(((ptr)+(8))>>2)]; + if (tv_nsec == 1073741823) { + return now; + } + if (tv_nsec == 1073741822) { + return null; + } + var tv_sec = readI53FromI64(ptr); + return (tv_sec*1000) + Math.floor(tv_nsec/(1000*1000)); } + atime = readTimespec(times); + mtime = readTimespec(times + 16); } // null here means UTIME_OMIT was passed. If both were set to UTIME_OMIT then // we can skip the call completely. if ((mtime ?? atime) !== null) { - FS.utime(path, atime, mtime); + FS.utime(path, atime, mtime, nofollow); } return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } } + var isLeapYear = (year) => year%4 === 0 && (year%100 !== 0 || year%400 === 0); @@ -3631,11 +3665,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return yday; }; + function __localtime_js(time, tmPtr) { time = bigintToI53Checked(time); var date = new Date(time*1000); + if (isNaN(date.getTime())) { + return 1; + } HEAP32[((tmPtr)>>2)] = date.getSeconds(); HEAP32[(((tmPtr)+(4))>>2)] = date.getMinutes(); HEAP32[(((tmPtr)+(8))>>2)] = date.getHours(); @@ -3654,6 +3692,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var winterOffset = start.getTimezoneOffset(); var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0; HEAP32[(((tmPtr)+(32))>>2)] = dst; + return 0; ; } @@ -3662,6 +3701,8 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + + function __mmap_js(len, prot, flags, fd, offset, allocated, addr) { offset = bigintToI53Checked(offset); @@ -3675,7 +3716,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((addr)>>2)] = ptr; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; @@ -3693,12 +3734,14 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { SYSCALLS.doMsync(addr, stream, len, flags, offset); } } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return -e.errno; } ; } + + var __tzset_js = (timezone, daylight, std_name, dst_name) => { // TODO: Use (malleable) environment variables instead of system settings. var currentYear = new Date().getFullYear(); @@ -3727,11 +3770,11 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var extractZone = (timezoneOffset) => { // Why inverse sign? // Read here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset - var sign = timezoneOffset >= 0 ? "-" : "+"; + var sign = timezoneOffset >= 0 ? '-' : '+'; var absOffset = Math.abs(timezoneOffset) - var hours = String(Math.floor(absOffset / 60)).padStart(2, "0"); - var minutes = String(absOffset % 60).padStart(2, "0"); + var hours = String(Math.floor(absOffset / 60)).padStart(2, '0'); + var minutes = String(absOffset % 60).padStart(2, '0'); return `UTC${sign}${hours}${minutes}`; } @@ -3756,6 +3799,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; + function _clock_time_get(clk_id, ignored_precision, ptime) { ignored_precision = bigintToI53Checked(ignored_precision); @@ -3773,7 +3817,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return 52; } // "now" is in ms, and wasi times are in ns. - var nsec = Math.round(now * 1000 * 1000); + var nsec = Math.round(now * 1_000_000); HEAP64[((ptime)>>3)] = BigInt(nsec); return 0; ; @@ -3801,9 +3845,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return 1 /*success*/; } catch(e) { } - // implicit 0 return to save code size (caller will cast "undefined" into 0 + // implicit 0 return to save code size (caller will cast 'undefined' into 0 // anyhow) }; + var _emscripten_resize_heap = (requestedSize) => { var oldSize = HEAPU8.length; // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. @@ -3857,11 +3902,10 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var ENV = { }; - var getExecutableName = () => thisProgram || './this.program'; + var getExecutableName = () => thisProgram; var getEnvStrings = () => { if (!getEnvStrings.strings) { // Default values. - // Browser language detection #8751 var lang = (globalThis.navigator?.language ?? 'C').replace('-', '_') + '.UTF-8'; var env = { 'USER': 'web_user', @@ -3889,6 +3933,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { return getEnvStrings.strings; }; + var _environ_get = (__environ, environ_buf) => { var bufSize = 0; var envp = 0; @@ -3902,6 +3947,7 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { }; + var _environ_sizes_get = (penviron_count, penviron_buf_size) => { var strings = getEnvStrings(); HEAPU32[((penviron_count)>>2)] = strings.length; @@ -3920,11 +3966,15 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { FS.close(stream); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + + function _fd_fdstat_get(fd, pbuf) { try { @@ -3946,11 +3996,13 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAP64[(((pbuf)+(16))>>3)] = BigInt(rightsInheriting); return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + /** @param {number=} offset */ var doReadv = (stream, iov, iovcnt, offset) => { var ret = 0; @@ -3958,17 +4010,29 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var ptr = HEAPU32[((iov)>>2)]; var len = HEAPU32[(((iov)+(4))>>2)]; iov += 8; - var curr = FS.read(stream, HEAP8, ptr, len, offset); + try { + var curr = FS.read(stream, HEAP8, ptr, len, offset); + } catch (e) { + // On a non-blocking stream a subsequent read may would-block after we + // already gathered data. POSIX readv is a single gather-read: return + // what we have rather than failing the whole call. + if (ret > 0 && e instanceof FS.ErrnoError && + (e.errno == 6 || e.errno == 6)) { + break; + } + throw e; + } if (curr < 0) return -1; ret += curr; if (curr < len) break; // nothing more to read - if (typeof offset != 'undefined') { + if (typeof offset !== 'undefined') { offset += curr; } } return ret; }; + function _fd_read(fd, iov, iovcnt, pnum) { try { @@ -3977,26 +4041,28 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((pnum)>>2)] = num; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + function _fd_seek(fd, offset, whence, newOffset) { offset = bigintToI53Checked(offset); try { - if (isNaN(offset)) return 61; + if (isNaN(offset)) return 22; var stream = SYSCALLS.getStreamFromFD(fd); FS.llseek(stream, offset, whence); HEAP64[((newOffset)>>3)] = BigInt(stream.position); - if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; // reset readdir state + if (stream.getdents && !offset && whence === 0) stream.getdents = null; // reset readdir state return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } ; @@ -4009,32 +4075,40 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { var rtn = stream.stream_ops?.fsync?.(stream); return rtn; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + /** @param {number=} offset */ var doWritev = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { + // Gather all iovecs into one contiguous buffer and issue a single + // FS.write, matching POSIX writev's single gather-write semantics (as + // __syscall_sendmsg already does). Per-iovec writes fragment a stream + // socket send into multiple segments, breaking stream byte semantics. + if (iovcnt == 1) { + // Single iovec: write directly from HEAP8, no gather buffer needed. + return FS.write(stream, HEAP8, HEAPU32[((iov)>>2)], HEAPU32[(((iov)+(4))>>2)], offset); + } + var total = 0; + for (var i = 0, p = iov; i < iovcnt; i++, p += 8) { + total += HEAPU32[(((p)+(4))>>2)]; + } + var view = new Uint8Array(total); + var voff = 0; + for (var i = 0; i < iovcnt; i++, iov += 8) { var ptr = HEAPU32[((iov)>>2)]; var len = HEAPU32[(((iov)+(4))>>2)]; - iov += 8; - var curr = FS.write(stream, HEAP8, ptr, len, offset); - if (curr < 0) return -1; - ret += curr; - if (curr < len) { - // No more space to write. - break; - } - if (typeof offset != 'undefined') { - offset += curr; - } + view.set(HEAPU8.subarray(ptr, ptr + len), voff); + voff += len; } - return ret; + return FS.write(stream, view, 0, total, offset); }; + function _fd_write(fd, iov, iovcnt, pnum) { try { @@ -4043,10 +4117,72 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { HEAPU32[((pnum)>>2)] = num; return 0; } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + if (typeof FS === 'undefined' || !(e.name === 'ErrnoError')) throw e; return e.errno; } } + + + + + + + /** @type {!Float32Array} */ + var HEAPF32; + + /** @type {!Float64Array} */ + var HEAPF64; + + + /** + * @param {number} ptr + * @param {string} type + */ + function getValue(ptr, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': return HEAP8[ptr]; + case 'i8': return HEAP8[ptr]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': return HEAP64[((ptr)>>3)]; + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + case '*': return HEAPU32[((ptr)>>2)]; + default: abort(`invalid type for getValue: ${type}`); + } + } + + + + + + + + + /** + * @param {number} ptr + * @param {number} value + * @param {string} type + */ + function setValue(ptr, value, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': HEAP8[ptr] = value; break; + case 'i8': HEAP8[ptr] = value; break; + case 'i16': HEAP16[((ptr)>>1)] = value; break; + case 'i32': HEAP32[((ptr)>>2)] = value; break; + case 'i64': HEAP64[((ptr)>>3)] = BigInt(value); break; + case 'float': HEAPF32[((ptr)>>2)] = value; break; + case 'double': HEAPF64[((ptr)>>3)] = value; break; + case '*': HEAPU32[((ptr)>>2)] = value; break; + default: abort(`invalid type for setValue: ${type}`); + } + } + + + + FS.createPreloadedFile = FS_createPreloadedFile; @@ -4065,19 +4201,21 @@ var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { // Begin ATMODULES hooks if (Module['noExitRuntime']) noExitRuntime = Module['noExitRuntime']; -if (Module['preloadPlugins']) preloadPlugins = Module['preloadPlugins']; + if (Module['print']) out = Module['print']; if (Module['printErr']) err = Module['printErr']; -if (Module['wasmBinary']) wasmBinary = Module['wasmBinary']; // End ATMODULES hooks - if (Module['arguments']) arguments_ = Module['arguments']; + if (Module['arguments']) programArgs = Module['arguments']; if (Module['thisProgram']) thisProgram = Module['thisProgram']; - if (Module['preInit']) { - if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; - while (Module['preInit'].length > 0) { - Module['preInit'].shift()(); + var preInit = Module['preInit']; + if (preInit) { + if (typeof preInit === 'function') Module['preInit'] = preInit = [preInit]; + // Written as a loop so that preInit functions that themselves add more + // preInit functions. Is this actually needed? + while (preInit.length > 0) { + preInit.shift()(); } } } @@ -4354,6 +4492,7 @@ var _sqlite3_status64, _free, _realloc, _emscripten_builtin_memalign, + _setThrew, __emscripten_stack_restore, __emscripten_stack_alloc, _emscripten_stack_get_current, @@ -4623,6 +4762,7 @@ function assignWasmExports(wasmExports) { _free = Module['_free'] = wasmExports['free']; _realloc = Module['_realloc'] = wasmExports['realloc']; _emscripten_builtin_memalign = wasmExports['emscripten_builtin_memalign']; + _setThrew = wasmExports['setThrew']; __emscripten_stack_restore = wasmExports['_emscripten_stack_restore']; __emscripten_stack_alloc = wasmExports['_emscripten_stack_alloc']; _emscripten_stack_get_current = wasmExports['emscripten_stack_get_current']; @@ -4647,6 +4787,8 @@ var wasmImports = { /** @export */ __syscall_getcwd: ___syscall_getcwd, /** @export */ + __syscall_geteuid32: ___syscall_geteuid32, + /** @export */ __syscall_ioctl: ___syscall_ioctl, /** @export */ __syscall_lstat64: ___syscall_lstat64, @@ -4708,55 +4850,38 @@ var wasmImports = { // include: postamble.js // === Auto-generated postamble setup entry stuff === -function run() { - - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; - } +async function run() { preRun(); - // a preRun added a dependency, run will be called later - if (runDependencies > 0) { - dependenciesFulfilled = run; - return; + if (runDependencies) { + await resolveRunDependencies(); } - function doRun() { - // run may have just been called through dependencies being fulfilled just in this very frame, - // or while the async setStatus time below was happening - Module['calledRun'] = true; - - if (ABORT) return; + var setStatus = Module['setStatus']; + if (setStatus) { + setStatus('Running...'); + // Yield to the event loop to allow the browser to paint "Running..." + await new Promise((resolve) => setTimeout(resolve, 1)); + // Then we want to clear the status text, but only after the rest of this function runs. + setTimeout(setStatus, 1, ''); + } - initRuntime(); + if (ABORT) return; - readyPromiseResolve?.(Module); - Module['onRuntimeInitialized']?.(); + initRuntime(); - postRun(); - } + Module['onRuntimeInitialized']?.(); - if (Module['setStatus']) { - Module['setStatus']('Running...'); - setTimeout(() => { - setTimeout(() => Module['setStatus'](''), 1); - doRun(); - }, 1); - } else - { - doRun(); - } + postRun(); } var wasmExports; // In modularize mode the generated code is within a factory function so we // can use await here (since it's not top-level-await). -wasmExports = await (createWasm()); - -run(); +wasmExports = await createWasm(); +await run(); // end include: postamble.js @@ -4836,7 +4961,7 @@ Module.runSQLite3PostLoadInit = async function( ** SQLITE_VERSION_NUMBER 3054001 ** SQLITE_SOURCE_ID "2026-10-16 14:45:20 555e07ef33f69a0957d24d8e555b702464a131d6f1607f4c3c1e4551e8e6ca76" ** -** Emscripten SDK: 5.0.5 +** Emscripten SDK: 6.0.13 */ /* 2022-05-22 @@ -21670,25 +21795,12 @@ try{ // include: postamble_modularize.js // In MODULARIZE mode we wrap the generated code in a factory function // and return either the Module itself, or a promise of the module. -// -// We assign to the `moduleRtn` global here and configure closure to see -// this as an extern so it won't get minified. - -if (runtimeInitialized) { - moduleRtn = Module; -} else { - // Set up the promise that indicates the Module is initialized - moduleRtn = new Promise((resolve, reject) => { - readyPromiseResolve = resolve; - readyPromiseReject = reject; - }); -} // end include: postamble_modularize.js - return moduleRtn; + return Module; } // Export using a UMD style export, or ES6 exports if selected diff --git a/src/bin/sqlite3.wasm b/src/bin/sqlite3.wasm index 2be0556..e7f8579 100644 Binary files a/src/bin/sqlite3.wasm and b/src/bin/sqlite3.wasm differ diff --git a/tsdown.config.ts b/tsdown.config.ts index fc8596a..83ea2e5 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -7,7 +7,7 @@ const tsdownConfig: UserConfig[] = [ entry: { index: 'src/browser.ts', node: 'src/node.ts', - 'bin/sqlite3-worker1': 'src/bin/sqlite3-worker1.mjs', + 'sqlite3-worker1': 'src/bin/sqlite3-worker1.mjs', }, format: ['esm'], dts: false, @@ -19,7 +19,6 @@ const tsdownConfig: UserConfig[] = [ }, onSuccess: () => { copyFileSync('./src/bin/sqlite3.wasm', './dist/sqlite3.wasm'); - copyFileSync('./dist/bin/sqlite3-worker1.mjs', './dist/sqlite3-worker1.mjs'); }, }), defineConfig({ @@ -44,7 +43,6 @@ const tsdownConfig: UserConfig[] = [ onSuccess: () => { copyFileSync('./dist/sqlite3-opfs-async-proxy.iife.js', './dist/sqlite3-opfs-async-proxy.js'); rmSync('./dist/sqlite3-opfs-async-proxy.iife.js'); - rmSync('./dist/bin', { recursive: true, force: true }); }, }), ];