var WasmModule = (() => { var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; return ( async function(moduleArg = {}) { var moduleRtn; // include: shell.js // The Module object: Our interface to the outside world. We import // and export values on it. There are various ways Module can be used: // 1. Not defined. We create it here // 2. A function parameter, function(moduleArg) => Promise // 3. pre-run appended it, var Module = {}; ..generated code.. // 4. External script tag defines var Module. // We need to check if Module already exists (e.g. case 3 above). // Substitution will be replaced with actual code on later stage of the build, // this way Closure Compiler will not mangle it (e.g. case 4. above). // Note that if you want to run closure, and also to use Module // 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; // Set up the promise that indicates the Module is initialized var readyPromiseResolve, readyPromiseReject; var readyPromise = new Promise((resolve, reject) => { readyPromiseResolve = resolve; readyPromiseReject = reject; }); // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). // Attempt to auto-detect the environment var ENVIRONMENT_IS_WEB = typeof window == "object"; var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != "undefined"; // N.b. Electron.js environment is simultaneously a NODE-environment, but // also a web environment. var ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string" && process.type != "renderer"; var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; // Three configurations we can be running in: // 1) We could be the application main() thread running in the main JS UI thread. (ENVIRONMENT_IS_WORKER == false and ENVIRONMENT_IS_PTHREAD == false) // 2) We could be the application main() thread proxied to worker. (with Emscripten -sPROXY_TO_WORKER) (ENVIRONMENT_IS_WORKER == true, ENVIRONMENT_IS_PTHREAD == false) // 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true) // The way we signal to a worker that it is hosting a pthread is to construct // it with a specific name. var ENVIRONMENT_IS_PTHREAD = ENVIRONMENT_IS_WORKER && self.name?.startsWith("em-pthread"); if (ENVIRONMENT_IS_PTHREAD) { assert(!globalThis.moduleLoaded, "module should only be loaded once on each pthread worker"); globalThis.moduleLoaded = true; } // --pre-jses are emitted after the Module integration code, so that they can // refer to Module (if they choose; they can also define Module) // Sometimes an existing Module object exists with properties // meant to overwrite the default module functionality. Here // we collect those properties and reapply _after_ we configure // the current environment's defaults to avoid having to be so // defensive during initialization. var moduleOverrides = { ...Module }; var arguments_ = []; var thisProgram = "./this.program"; var quit_ = (status, toThrow) => { throw toThrow; }; // `/` should be present at the end if `scriptDirectory` is not empty var scriptDirectory = ""; function locateFile(path) { if (Module["locateFile"]) { return Module["locateFile"](path, scriptDirectory); } return scriptDirectory + path; } // Hooks that are implemented differently in different runtime environments. var readAsync, readBinary; if (ENVIRONMENT_IS_SHELL) { if ((typeof process == "object" && typeof require === "function") || typeof window == "object" || typeof WorkerGlobalScope != "undefined") throw new Error("not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)"); } else // Note that this includes Node.js workers when relevant (pthreads is enabled). // Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and // ENVIRONMENT_IS_NODE. if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled scriptDirectory = self.location.href; } else if (typeof document != "undefined" && document.currentScript) { // web scriptDirectory = document.currentScript.src; } // When MODULARIZE, this JS may be executed later, after document.currentScript // is gone, so we saved it, and we use it here instead of any other info. if (_scriptName) { scriptDirectory = _scriptName; } // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. // otherwise, slice off the final part of the url to find the script directory. // if scriptDirectory does not contain a slash, lastIndexOf will return -1, // and scriptDirectory will correctly be replaced with an empty string. // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #), // they are removed because they could contain a slash. if (scriptDirectory.startsWith("blob:")) { scriptDirectory = ""; } else { scriptDirectory = scriptDirectory.slice(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1); } if (!(typeof window == "object" || typeof WorkerGlobalScope != "undefined")) throw new Error("not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)"); { // include: web_or_worker_shell_read.js if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest; xhr.open("GET", url, false); xhr.responseType = "arraybuffer"; xhr.send(null); return new Uint8Array(/** @type{!ArrayBuffer} */ (xhr.response)); }; } readAsync = async url => { assert(!isFileURI(url), "readAsync does not work with file:// URLs"); var response = await fetch(url, { credentials: "same-origin" }); if (response.ok) { return response.arrayBuffer(); } throw new Error(response.status + " : " + response.url); }; } } else { throw new Error("environment detection error"); } var out = Module["print"] || console.log.bind(console); var err = Module["printErr"] || console.error.bind(console); // Merge back in the overrides Object.assign(Module, moduleOverrides); // Free the object hierarchy contained in the overrides, this lets the GC // reclaim data used. moduleOverrides = null; checkIncomingModuleAPI(); // Emit code to handle expected values on the Module object. This applies Module.x // to the proper local x. This has two benefits: first, we only emit it if it is // expected to arrive, and second, by using a local everywhere else that can be // minified. if (Module["arguments"]) arguments_ = Module["arguments"]; legacyModuleProp("arguments", "arguments_"); if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; legacyModuleProp("thisProgram", "thisProgram"); // perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message // Assertions on removed incoming Module JS APIs. assert(typeof Module["memoryInitializerPrefixURL"] == "undefined", "Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead"); assert(typeof Module["pthreadMainPrefixURL"] == "undefined", "Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead"); assert(typeof Module["cdInitializerPrefixURL"] == "undefined", "Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead"); assert(typeof Module["filePackagePrefixURL"] == "undefined", "Module.filePackagePrefixURL option was removed, use Module.locateFile instead"); assert(typeof Module["read"] == "undefined", "Module.read option was removed"); assert(typeof Module["readAsync"] == "undefined", "Module.readAsync option was removed (modify readAsync in JS)"); assert(typeof Module["readBinary"] == "undefined", "Module.readBinary option was removed (modify readBinary in JS)"); assert(typeof Module["setWindowTitle"] == "undefined", "Module.setWindowTitle option was removed (modify emscripten_set_window_title in JS)"); assert(typeof Module["TOTAL_MEMORY"] == "undefined", "Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY"); legacyModuleProp("asm", "wasmExports"); legacyModuleProp("readAsync", "readAsync"); legacyModuleProp("readBinary", "readBinary"); legacyModuleProp("setWindowTitle", "setWindowTitle"); var IDBFS = "IDBFS is no longer included by default; build with -lidbfs.js"; var PROXYFS = "PROXYFS is no longer included by default; build with -lproxyfs.js"; var WORKERFS = "WORKERFS is no longer included by default; build with -lworkerfs.js"; var FETCHFS = "FETCHFS is no longer included by default; build with -lfetchfs.js"; var ICASEFS = "ICASEFS is no longer included by default; build with -licasefs.js"; var JSFILEFS = "JSFILEFS is no longer included by default; build with -ljsfilefs.js"; var OPFS = "OPFS is no longer included by default; build with -lopfs.js"; var NODEFS = "NODEFS is no longer included by default; build with -lnodefs.js"; assert(ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER || ENVIRONMENT_IS_NODE, "Pthreads do not work in this environment yet (need Web Workers, or an alternative to them)"); assert(!ENVIRONMENT_IS_NODE, "node environment detected but not enabled at build time. Add `node` to `-sENVIRONMENT` to enable."); assert(!ENVIRONMENT_IS_SHELL, "shell environment detected but not enabled at build time. Add `shell` to `-sENVIRONMENT` to enable."); // end include: shell.js // include: preamble.js // === Preamble library stuff === // Documentation for the public APIs defined in this file must be updated in: // site/source/docs/api_reference/preamble.js.rst // A prebuilt local version of the documentation is available at: // site/build/text/docs/api_reference/preamble.js.txt // You can also build docs locally as HTML or other formats in site/ // An online HTML version (which may be of a different version of Emscripten) // is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html var wasmBinary = Module["wasmBinary"]; legacyModuleProp("wasmBinary", "wasmBinary"); if (typeof WebAssembly != "object") { err("no native wasm support detected"); } // Wasm globals var wasmMemory; // For sending to workers. var wasmModule; //======================================== // Runtime essentials //======================================== // whether we are quitting the application. no code should run after this. // set in exit() and abort() var ABORT = false; // set by exit() and abort(). Passed to 'onExit' handler. // NOTE: This is also used as the process return code code in shell environments // but only when noExitRuntime is false. var EXITSTATUS; // In STRICT mode, we only define assert() when ASSERTIONS is set. i.e. we // don't define it at all in release modes. This matches the behaviour of // MINIMAL_RUNTIME. // TODO(sbc): Make this the default even without STRICT enabled. /** @type {function(*, string=)} */ function assert(condition, text) { if (!condition) { abort("Assertion failed" + (text ? ": " + text : "")); } } // We used to include malloc/free by default in the past. Show a helpful error in // builds with assertions. // Memory management var HEAP, /** @type {!Int8Array} */ HEAP8, /** @type {!Uint8Array} */ HEAPU8, /** @type {!Int16Array} */ HEAP16, /** @type {!Uint16Array} */ HEAPU16, /** @type {!Int32Array} */ HEAP32, /** @type {!Uint32Array} */ HEAPU32, /** @type {!Float32Array} */ HEAPF32, /* BigInt64Array type is not correctly defined in closure /** not-@type {!BigInt64Array} */ HEAP64, /* BigUint64Array type is not correctly defined in closure /** not-t@type {!BigUint64Array} */ HEAPU64, /** @type {!Float64Array} */ HEAPF64; var runtimeInitialized = false; /** * Indicates whether filename is delivered via file protocol (as opposed to http/https) * @noinline */ var isFileURI = filename => filename.startsWith("file://"); // include: runtime_shared.js // include: runtime_stack_check.js // Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. function writeStackCookie() { var max = _emscripten_stack_get_end(); assert((max & 3) == 0); // If the stack ends at address zero we write our cookies 4 bytes into the // stack. This prevents interference with SAFE_HEAP and ASAN which also // monitor writes to address zero. if (max == 0) { max += 4; } // The stack grow downwards towards _emscripten_stack_get_end. // We write cookies to the final two words in the stack and detect if they are // ever overwritten. GROWABLE_HEAP_U32()[((max) >> 2)] = 34821223; GROWABLE_HEAP_U32()[(((max) + (4)) >> 2)] = 2310721022; // Also test the global address 0 for integrity. GROWABLE_HEAP_U32()[((0) >> 2)] = 1668509029; } function checkStackCookie() { if (ABORT) return; var max = _emscripten_stack_get_end(); // See writeStackCookie(). if (max == 0) { max += 4; } var cookie1 = GROWABLE_HEAP_U32()[((max) >> 2)]; var cookie2 = GROWABLE_HEAP_U32()[(((max) + (4)) >> 2)]; if (cookie1 != 34821223 || cookie2 != 2310721022) { abort(`Stack overflow! Stack cookie has been overwritten at ${ptrToString(max)}, expected hex dwords 0x89BACDFE and 0x2135467, but received ${ptrToString(cookie2)} ${ptrToString(cookie1)}`); } // Also test the global address 0 for integrity. if (GROWABLE_HEAP_U32()[((0) >> 2)] != 1668509029) { abort("Runtime error: The application has corrupted its heap memory area (address zero)!"); } } // end include: runtime_stack_check.js // include: runtime_exceptions.js // end include: runtime_exceptions.js // include: runtime_debug.js // Endianness check (() => { var h16 = new Int16Array(1); var h8 = new Int8Array(h16.buffer); h16[0] = 25459; if (h8[0] !== 115 || h8[1] !== 99) throw "Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)"; })(); if (Module["ENVIRONMENT"]) { throw new Error("Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)"); } function legacyModuleProp(prop, newName, incoming = true) { if (!Object.getOwnPropertyDescriptor(Module, prop)) { Object.defineProperty(Module, prop, { configurable: true, get() { let extra = incoming ? " (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)" : ""; abort(`\`Module.${prop}\` has been replaced by \`${newName}\`` + extra); } }); } } function consumedModuleProp(prop) { if (!Object.getOwnPropertyDescriptor(Module, prop)) { Object.defineProperty(Module, prop, { configurable: true, set() { abort(`Attempt to set \`Module.${prop}\` after it has already been processed. This can happen, for example, when code is injected via '--post-js' rather than '--pre-js'`); } }); } } function ignoredModuleProp(prop) { if (Object.getOwnPropertyDescriptor(Module, prop)) { abort(`\`Module.${prop}\` was supplied but \`${prop}\` not included in INCOMING_MODULE_JS_API`); } } // forcing the filesystem exports a few things by default function isExportedByForceFilesystem(name) { return name === "FS_createPath" || name === "FS_createDataFile" || name === "FS_createPreloadedFile" || name === "FS_unlink" || name === "addRunDependency" || // The old FS has some functionality that WasmFS lacks. name === "FS_createLazyFile" || name === "FS_createDevice" || name === "removeRunDependency"; } /** * Intercept access to a global symbol. This enables us to give informative * warnings/errors when folks attempt to use symbols they did not include in * their build, or no symbols that no longer exist. */ function hookGlobalSymbolAccess(sym, func) {} function missingGlobal(sym, msg) { hookGlobalSymbolAccess(sym, () => { warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`); }); } missingGlobal("buffer", "Please use HEAP8.buffer or wasmMemory.buffer"); missingGlobal("asm", "Please use wasmExports instead"); function missingLibrarySymbol(sym) { hookGlobalSymbolAccess(sym, () => { // Can't `abort()` here because it would break code that does runtime // checks. e.g. `if (typeof SDL === 'undefined')`. var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`; // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in // library.js, which means $name for a JS name with no prefix, or name // for a JS name like _name. var librarySymbol = sym; if (!librarySymbol.startsWith("_")) { librarySymbol = "$" + sym; } msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`; if (isExportedByForceFilesystem(sym)) { msg += ". Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you"; } warnOnce(msg); }); // Any symbol that is not included from the JS library is also (by definition) // not exported on the Module object. unexportedRuntimeSymbol(sym); } function unexportedRuntimeSymbol(sym) { if (ENVIRONMENT_IS_PTHREAD) { return; } if (!Object.getOwnPropertyDescriptor(Module, sym)) { Object.defineProperty(Module, sym, { configurable: true, get() { var msg = `'${sym}' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the Emscripten FAQ)`; if (isExportedByForceFilesystem(sym)) { msg += ". Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you"; } abort(msg); } }); } } var runtimeDebug = true; // Switch to false at runtime to disable logging at the right times // Used by XXXXX_DEBUG settings to output debug messages. function dbg(...args) { if (!runtimeDebug && typeof runtimeDebug != "undefined") return; // TODO(sbc): Make this configurable somehow. Its not always convenient for // logging to show up as warnings. console.warn(...args); } // end include: runtime_debug.js // include: memoryprofiler.js // end include: memoryprofiler.js // include: growableHeap.js // Support for growable heap + pthreads, where the buffer may change, so JS views // must be updated. function GROWABLE_HEAP_I8() { if (wasmMemory.buffer != HEAP8.buffer) { updateMemoryViews(); } return HEAP8; } function GROWABLE_HEAP_U8() { if (wasmMemory.buffer != HEAP8.buffer) { updateMemoryViews(); } return HEAPU8; } function GROWABLE_HEAP_I16() { if (wasmMemory.buffer != HEAP8.buffer) { updateMemoryViews(); } return HEAP16; } function GROWABLE_HEAP_U16() { if (wasmMemory.buffer != HEAP8.buffer) { updateMemoryViews(); } return HEAPU16; } function GROWABLE_HEAP_I32() { if (wasmMemory.buffer != HEAP8.buffer) { updateMemoryViews(); } return HEAP32; } function GROWABLE_HEAP_U32() { if (wasmMemory.buffer != HEAP8.buffer) { updateMemoryViews(); } return HEAPU32; } function GROWABLE_HEAP_F32() { if (wasmMemory.buffer != HEAP8.buffer) { updateMemoryViews(); } return HEAPF32; } function GROWABLE_HEAP_F64() { if (wasmMemory.buffer != HEAP8.buffer) { updateMemoryViews(); } return HEAPF64; } // end include: growableHeap.js // include: runtime_pthread.js // Pthread Web Worker handling code. // This code runs only on pthread web workers and handles pthread setup // and communication with the main thread via postMessage. // Unique ID of the current pthread worker (zero on non-pthread-workers // including the main thread). var workerID = 0; if (ENVIRONMENT_IS_PTHREAD) { var wasmModuleReceived; // Thread-local guard variable for one-time init of the JS state var initializedJS = false; function threadPrintErr(...args) { console.error(...args); } if (!Module["printErr"]) err = threadPrintErr; // Turn unhandled rejected promises into errors so that the main thread will be // notified about them. self.onunhandledrejection = e => { throw e.reason || e; }; function handleMessage(e) { try { var msgData = e["data"]; //dbg('msgData: ' + Object.keys(msgData)); var cmd = msgData.cmd; if (cmd === "load") { // Preload command that is called once per worker to parse and load the Emscripten code. workerID = msgData.workerID; // Until we initialize the runtime, queue up any further incoming messages. let messageQueue = []; self.onmessage = e => messageQueue.push(e); // And add a callback for when the runtime is initialized. self.startWorker = instance => { // Notify the main thread that this thread has loaded. postMessage({ cmd: "loaded" }); // Process any messages that were queued before the thread was ready. for (let msg of messageQueue) { handleMessage(msg); } // Restore the real message handler. self.onmessage = handleMessage; }; // Use `const` here to ensure that the variable is scoped only to // that iteration, allowing safe reference from a closure. for (const handler of msgData.handlers) { // The the main module has a handler for a certain even, but no // handler exists on the pthread worker, then proxy that handler // back to the main thread. if (!Module[handler] || Module[handler].proxy) { Module[handler] = (...args) => { postMessage({ cmd: "callHandler", handler, args }); }; // Rebind the out / err handlers if needed if (handler == "print") out = Module[handler]; if (handler == "printErr") err = Module[handler]; } } wasmMemory = msgData.wasmMemory; updateMemoryViews(); wasmModuleReceived(msgData.wasmModule); } else if (cmd === "run") { assert(msgData.pthread_ptr); // Call inside JS module to set up the stack frame for this pthread in JS module scope. // This needs to be the first thing that we do, as we cannot call to any C/C++ functions // until the thread stack is initialized. establishStackSpace(msgData.pthread_ptr); // Pass the thread address to wasm to store it for fast access. __emscripten_thread_init(msgData.pthread_ptr, /*is_main=*/ 0, /*is_runtime=*/ 0, /*can_block=*/ 1, 0, 0); PThread.receiveOffscreenCanvases(msgData); PThread.threadInitTLS(); // Await mailbox notifications with `Atomics.waitAsync` so we can start // using the fast `Atomics.notify` notification path. __emscripten_thread_mailbox_await(msgData.pthread_ptr); if (!initializedJS) { // Embind must initialize itself on all threads, as it generates support JS. // We only do this once per worker since they get reused __embind_initialize_bindings(); initializedJS = true; } try { invokeEntryPoint(msgData.start_routine, msgData.arg); } catch (ex) { if (ex != "unwind") { // The pthread "crashed". Do not call `_emscripten_thread_exit` (which // would make this thread joinable). Instead, re-throw the exception // and let the top level handler propagate it back to the main thread. throw ex; } } } else if (msgData.target === "setimmediate") {} else if (cmd === "checkMailbox") { if (initializedJS) { checkMailbox(); } } else if (cmd) { // The received message looks like something that should be handled by this message // handler, (since there is a cmd field present), but is not one of the // recognized commands: err(`worker: received unknown command ${cmd}`); err(msgData); } } catch (ex) { err(`worker: onmessage() captured an uncaught exception: ${ex}`); if (ex?.stack) err(ex.stack); __emscripten_thread_crashed(); throw ex; } } self.onmessage = handleMessage; } // ENVIRONMENT_IS_PTHREAD // end include: runtime_pthread.js function updateMemoryViews() { var b = wasmMemory.buffer; Module["HEAP8"] = HEAP8 = new Int8Array(b); Module["HEAP16"] = HEAP16 = new Int16Array(b); Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); Module["HEAP32"] = HEAP32 = new Int32Array(b); Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); Module["HEAPF32"] = HEAPF32 = new Float32Array(b); Module["HEAPF64"] = HEAPF64 = new Float64Array(b); Module["HEAP64"] = HEAP64 = new BigInt64Array(b); Module["HEAPU64"] = HEAPU64 = new BigUint64Array(b); } // end include: runtime_shared.js assert(!Module["STACK_SIZE"], "STACK_SIZE can no longer be set at runtime. Use -sSTACK_SIZE at link time"); assert(typeof Int32Array != "undefined" && typeof Float64Array !== "undefined" && Int32Array.prototype.subarray != undefined && Int32Array.prototype.set != undefined, "JS engine does not provide full typed array support"); // 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) if (!ENVIRONMENT_IS_PTHREAD) { if (Module["wasmMemory"]) { wasmMemory = Module["wasmMemory"]; } else { var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 67108864; legacyModuleProp("INITIAL_MEMORY", "INITIAL_MEMORY"); assert(INITIAL_MEMORY >= 16777216, "INITIAL_MEMORY should be larger than STACK_SIZE, was " + INITIAL_MEMORY + "! (STACK_SIZE=" + 16777216 + ")"); /** @suppress {checkTypes} */ wasmMemory = new WebAssembly.Memory({ "initial": INITIAL_MEMORY / 65536, // In theory we should not need to emit the maximum if we want "unlimited" // or 4GB of memory, but VMs error on that atm, see // https://github.com/emscripten-core/emscripten/issues/14130 // And in the pthreads case we definitely need to emit a maximum. So // always emit one. "maximum": 32768, "shared": true }); } updateMemoryViews(); } // end include: runtime_init_memory.js function preRun() { assert(!ENVIRONMENT_IS_PTHREAD); // PThreads reuse the runtime from the main thread. if (Module["preRun"]) { if (typeof Module["preRun"] == "function") Module["preRun"] = [ Module["preRun"] ]; while (Module["preRun"].length) { addOnPreRun(Module["preRun"].shift()); } } consumedModuleProp("preRun"); callRuntimeCallbacks(onPreRuns); } function initRuntime() { assert(!runtimeInitialized); runtimeInitialized = true; if (ENVIRONMENT_IS_PTHREAD) return startWorker(Module); checkStackCookie(); setStackLimits(); if (!Module["noFSInit"] && !FS.initialized) FS.init(); TTY.init(); wasmExports["__wasm_call_ctors"](); FS.ignorePermissions = false; } function preMain() { checkStackCookie(); } function postRun() { checkStackCookie(); if (ENVIRONMENT_IS_PTHREAD) return; // 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()); } } consumedModuleProp("postRun"); callRuntimeCallbacks(onPostRuns); } // A counter of dependencies for calling run(). If we need to // do asynchronous work before running, increment this and // decrement it. Incrementing must happen in a place like // Module.preRun (used by emcc to add file preloading). // Note that you can add dependencies in preRun, even though // it happens right before run - run will be postponed until // the dependencies are met. var runDependencies = 0; var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled var runDependencyTracking = {}; var runDependencyWatcher = null; function getUniqueRunDependency(id) { var orig = id; while (1) { if (!runDependencyTracking[id]) return id; id = orig + Math.random(); } } function addRunDependency(id) { runDependencies++; Module["monitorRunDependencies"]?.(runDependencies); if (id) { assert(!runDependencyTracking[id]); runDependencyTracking[id] = 1; if (runDependencyWatcher === null && typeof setInterval != "undefined") { // Check for missing dependencies every few seconds runDependencyWatcher = setInterval(() => { if (ABORT) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; return; } var shown = false; for (var dep in runDependencyTracking) { if (!shown) { shown = true; err("still waiting on run dependencies:"); } err(`dependency: ${dep}`); } if (shown) { err("(end of list)"); } }, 1e4); } } else { err("warning: run dependency added without ID"); } } function removeRunDependency(id) { runDependencies--; Module["monitorRunDependencies"]?.(runDependencies); if (id) { assert(runDependencyTracking[id]); delete runDependencyTracking[id]; } else { err("warning: run dependency removed without ID"); } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; } if (dependenciesFulfilled) { var callback = dependenciesFulfilled; dependenciesFulfilled = null; callback(); } } } /** @param {string|number=} what */ function abort(what) { Module["onAbort"]?.(what); what = "Aborted(" + what + ")"; // TODO(sbc): Should we remove printing and leave it up to whoever // catches the exception? err(what); ABORT = true; if (what.indexOf("RuntimeError: unreachable") >= 0) { what += '. "unreachable" may be due to ASYNCIFY_STACK_SIZE not being large enough (try increasing it)'; } // Use a wasm runtime error, because a JS error might be seen as a foreign // exception, which means we'd run destructors on it. We need the error to // simply make the program stop. // FIXME This approach does not work in Wasm EH because it currently does not assume // all RuntimeErrors are from traps; it decides whether a RuntimeError is from // 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; } function createExportWrapper(name, nargs) { return (...args) => { assert(runtimeInitialized, `native function \`${name}\` called before runtime initialization`); var f = wasmExports[name]; assert(f, `exported native function \`${name}\` not found`); // Only assert for too many arguments. Too few can be valid since the missing arguments will be zero filled. assert(args.length <= nargs, `native function \`${name}\` called with ${args.length} args but expects ${nargs}`); return f(...args); }; } var wasmBinaryFile; function findWasmBinary() { return locateFile("h265web_wasm.wasm"); } function getBinarySync(file) { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary); } if (readBinary) { return readBinary(file); } throw "both async and sync fetching of the wasm failed"; } async function getWasmBinary(binaryFile) { // If we don't have the binary yet, load it asynchronously using readAsync. if (!wasmBinary) { // Fetch the binary using readAsync try { var response = await readAsync(binaryFile); return new Uint8Array(response); } catch {} } // Otherwise, getBinarySync should be able to get it synchronously return getBinarySync(binaryFile); } async function instantiateArrayBuffer(binaryFile, imports) { try { var binary = await getWasmBinary(binaryFile); var instance = await WebAssembly.instantiate(binary, imports); return instance; } catch (reason) { err(`failed to asynchronously prepare wasm: ${reason}`); // Warn on some common problems. if (isFileURI(wasmBinaryFile)) { err(`warning: Loading from a file URI (${wasmBinaryFile}) is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing`); } abort(reason); } } async function instantiateAsync(binary, binaryFile, imports) { if (!binary && typeof WebAssembly.instantiateStreaming == "function") { try { var response = fetch(binaryFile, { credentials: "same-origin" }); var instantiationResult = await WebAssembly.instantiateStreaming(response, imports); return instantiationResult; } catch (reason) { // We expect the most common failure cause to be a bad MIME type for the binary, // in which case falling back to ArrayBuffer instantiation should work. err(`wasm streaming compile failed: ${reason}`); err("falling back to ArrayBuffer instantiation"); } } return instantiateArrayBuffer(binaryFile, imports); } function getWasmImports() { assignWasmImports(); // instrumenting imports is used in asyncify in two ways: to add assertions // that check for proper import use, and for ASYNCIFY=2 we use them to set up // the Promise API on the import side. // In pthreads builds getWasmImports is called more than once but we only // and the instrument the imports once. if (!wasmImports.__instrumented) { wasmImports.__instrumented = true; Asyncify.instrumentWasmImports(wasmImports); } // prepare imports return { "env": wasmImports, "wasi_snapshot_preview1": wasmImports }; } // Create the wasm instance. // Receives the wasm imports, returns the exports. 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) { wasmExports = instance.exports; wasmExports = Asyncify.instrumentWasmExports(wasmExports); registerTLSInit(wasmExports["_emscripten_tls_init"]); wasmTable = wasmExports["__indirect_function_table"]; assert(wasmTable, "table not found in wasm exports"); // We now have the Wasm module loaded up, keep a reference to the compiled module so we can post it to the workers. wasmModule = module; removeRunDependency("wasm-instantiate"); return wasmExports; } // wait for the pthread pool (if any) addRunDependency("wasm-instantiate"); // Prefer streaming instantiation if available. // Async compilation can be confusing when an error on the page overwrites Module // (for example, if the order of elements is wrong, and the one defining Module is // later), so we save Module and check it later. var trueModule = Module; function receiveInstantiationResult(result) { // 'result' is a ResultObject object which has both the module and instance. // receiveInstance() will swap in the exports (to Module.asm) so they can be called assert(Module === trueModule, "the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?"); trueModule = null; return receiveInstance(result["instance"], result["module"]); } var info = getWasmImports(); // 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) => { try { Module["instantiateWasm"](info, (mod, inst) => { receiveInstance(mod, inst); resolve(mod.exports); }); } catch (e) { err(`Module.instantiateWasm callback failed with error: ${e}`); reject(e); } }); } if (ENVIRONMENT_IS_PTHREAD) { return new Promise(resolve => { wasmModuleReceived = module => { // Instantiate from the module posted from the main thread. // We can just use sync instantiation in the worker. var instance = new WebAssembly.Instance(module, getWasmImports()); resolve(receiveInstance(instance, module)); }; }); } wasmBinaryFile ??= findWasmBinary(); try { var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info); var exports = receiveInstantiationResult(result); return exports; } catch (e) { // If instantiation fails, reject the module ready promise. readyPromiseReject(e); return Promise.reject(e); } } // end include: preamble.js // Begin JS library code class ExitStatus { name="ExitStatus"; constructor(status) { this.message = `Program terminated with exit(${status})`; this.status = status; } } var terminateWorker = worker => { worker.terminate(); // terminate() can be asynchronous, so in theory the worker can continue // to run for some amount of time after termination. However from our POV // the worker now dead and we don't want to hear from it again, so we stub // out its message handler here. This avoids having to check in each of // the onmessage handlers if the message was coming from valid worker. worker.onmessage = e => { var cmd = e["data"].cmd; err(`received "${cmd}" command from terminated worker: ${worker.workerID}`); }; }; var cleanupThread = pthread_ptr => { assert(!ENVIRONMENT_IS_PTHREAD, "Internal Error! cleanupThread() can only ever be called from main application thread!"); assert(pthread_ptr, "Internal Error! Null pthread_ptr in cleanupThread!"); var worker = PThread.pthreads[pthread_ptr]; assert(worker); PThread.returnWorkerToPool(worker); }; var callRuntimeCallbacks = callbacks => { while (callbacks.length > 0) { // Pass the module as the first argument. callbacks.shift()(Module); } }; var onPreRuns = []; var addOnPreRun = cb => onPreRuns.unshift(cb); var spawnThread = threadParams => { assert(!ENVIRONMENT_IS_PTHREAD, "Internal Error! spawnThread() can only ever be called from main application thread!"); assert(threadParams.pthread_ptr, "Internal error, no pthread ptr!"); var worker = PThread.getNewWorker(); if (!worker) { // No available workers in the PThread pool. return 6; } assert(!worker.pthread_ptr, "Internal error!"); PThread.runningWorkers.push(worker); // Add to pthreads map PThread.pthreads[threadParams.pthread_ptr] = worker; worker.pthread_ptr = threadParams.pthread_ptr; var msg = { cmd: "run", start_routine: threadParams.startRoutine, arg: threadParams.arg, pthread_ptr: threadParams.pthread_ptr }; // Note that we do not need to quote these names because they are only used // in this file, and not from the external worker.js. msg.moduleCanvasId = threadParams.moduleCanvasId; msg.offscreenCanvases = threadParams.offscreenCanvases; // Ask the worker to start executing its pthread entry point function. worker.postMessage(msg, threadParams.transferList); return 0; }; var runtimeKeepaliveCounter = 0; var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; var stackSave = () => _emscripten_stack_get_current(); var stackRestore = val => __emscripten_stack_restore(val); var stackAlloc = sz => __emscripten_stack_alloc(sz); /** @type{function(number, (number|boolean), ...number)} */ var proxyToMainThread = (funcIndex, emAsmAddr, sync, ...callArgs) => { // EM_ASM proxying is done by passing a pointer to the address of the EM_ASM // content as `emAsmAddr`. JS library proxying is done by passing an index // into `proxiedJSCallArgs` as `funcIndex`. If `emAsmAddr` is non-zero then // `funcIndex` will be ignored. // Additional arguments are passed after the first three are the actual // function arguments. // The serialization buffer contains the number of call params, and then // all the args here. // We also pass 'sync' to C separately, since C needs to look at it. // Allocate a buffer, which will be copied by the C code. // First passed parameter specifies the number of arguments to the function. // When BigInt support is enabled, we must handle types in a more complex // way, detecting at runtime if a value is a BigInt or not (as we have no // type info here). To do that, add a "prefix" before each value that // indicates if it is a BigInt, which effectively doubles the number of // values we serialize for proxying. TODO: pack this? var serializedNumCallArgs = callArgs.length * 2; var sp = stackSave(); var args = stackAlloc(serializedNumCallArgs * 8); var b = ((args) >> 3); for (var i = 0; i < callArgs.length; i++) { var arg = callArgs[i]; if (typeof arg == "bigint") { // The prefix is non-zero to indicate a bigint. HEAP64[b + 2 * i] = 1n; HEAP64[b + 2 * i + 1] = arg; } else { // The prefix is zero to indicate a JS Number. HEAP64[b + 2 * i] = 0n; GROWABLE_HEAP_F64()[b + 2 * i + 1] = arg; } } var rtn = __emscripten_run_on_main_thread_js(funcIndex, emAsmAddr, serializedNumCallArgs, args, sync); stackRestore(sp); return rtn; }; function _proc_exit(code) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(0, 0, 1, code); EXITSTATUS = code; if (!keepRuntimeAlive()) { PThread.terminateAllThreads(); Module["onExit"]?.(code); ABORT = true; } quit_(code, new ExitStatus(code)); } var runtimeKeepalivePop = () => { assert(runtimeKeepaliveCounter > 0); runtimeKeepaliveCounter -= 1; }; function exitOnMainThread(returnCode) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(1, 0, 0, returnCode); runtimeKeepalivePop(); _exit(returnCode); } /** @suppress {duplicate } */ /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => { EXITSTATUS = status; checkUnflushedContent(); if (ENVIRONMENT_IS_PTHREAD) { // implicit exit can never happen on a pthread assert(!implicit); // When running in a pthread we propagate the exit back to the main thread // where it can decide if the whole process should be shut down or not. // The pthread may have decided not to exit its own runtime, for example // because it runs a main loop, but that doesn't affect the main thread. exitOnMainThread(status); throw "unwind"; } // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down if (keepRuntimeAlive() && !implicit) { var msg = `program exited (with status: ${status}), but keepRuntimeAlive() is set (counter=${runtimeKeepaliveCounter}) due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)`; readyPromiseReject(msg); err(msg); } _proc_exit(status); }; var _exit = exitJS; var ptrToString = ptr => { assert(typeof ptr === "number"); // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. ptr >>>= 0; return "0x" + ptr.toString(16).padStart(8, "0"); }; var PThread = { unusedWorkers: [], runningWorkers: [], tlsInitFunctions: [], pthreads: {}, nextWorkerID: 1, debugInit() { function pthreadLogPrefix() { var t = 0; if (runtimeInitialized && typeof _pthread_self != "undefined") { t = _pthread_self(); } return `w:${workerID},t:${ptrToString(t)}: `; } // Prefix all err()/dbg() messages with the calling thread ID. var origDbg = dbg; dbg = (...args) => origDbg(pthreadLogPrefix() + args.join(" ")); }, init() { PThread.debugInit(); if ((!(ENVIRONMENT_IS_PTHREAD))) { PThread.initMainThread(); } }, initMainThread() { // MINIMAL_RUNTIME takes care of calling loadWasmModuleToAllWorkers // in postamble_minimal.js addOnPreRun(() => { addRunDependency("loading-workers"); PThread.loadWasmModuleToAllWorkers(() => removeRunDependency("loading-workers")); }); }, terminateAllThreads: () => { assert(!ENVIRONMENT_IS_PTHREAD, "Internal Error! terminateAllThreads() can only ever be called from main application thread!"); // Attempt to kill all workers. Sadly (at least on the web) there is no // way to terminate a worker synchronously, or to be notified when a // worker in actually terminated. This means there is some risk that // pthreads will continue to be executing after `worker.terminate` has // returned. For this reason, we don't call `returnWorkerToPool` here or // free the underlying pthread data structures. for (var worker of PThread.runningWorkers) { terminateWorker(worker); } for (var worker of PThread.unusedWorkers) { terminateWorker(worker); } PThread.unusedWorkers = []; PThread.runningWorkers = []; PThread.pthreads = {}; }, returnWorkerToPool: worker => { // We don't want to run main thread queued calls here, since we are doing // some operations that leave the worker queue in an invalid state until // we are completely done (it would be bad if free() ends up calling a // queued pthread_create which looks at the global data structures we are // modifying). To achieve that, defer the free() til the very end, when // we are all done. var pthread_ptr = worker.pthread_ptr; delete PThread.pthreads[pthread_ptr]; // Note: worker is intentionally not terminated so the pool can // dynamically grow. PThread.unusedWorkers.push(worker); PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(worker), 1); // Not a running Worker anymore // Detach the worker from the pthread object, and return it to the // worker pool as an unused worker. worker.pthread_ptr = 0; // Finally, free the underlying (and now-unused) pthread structure in // linear memory. __emscripten_thread_free_data(pthread_ptr); }, receiveOffscreenCanvases(data) { if (typeof GL != "undefined") { Object.assign(GL.offscreenCanvases, data.offscreenCanvases); if (!Module["canvas"] && data.moduleCanvasId && GL.offscreenCanvases[data.moduleCanvasId]) { Module["canvas"] = GL.offscreenCanvases[data.moduleCanvasId].offscreenCanvas; Module["canvas"].id = data.moduleCanvasId; } } }, threadInitTLS() { // Call thread init functions (these are the _emscripten_tls_init for each // module loaded. PThread.tlsInitFunctions.forEach(f => f()); }, loadWasmModuleToWorker: worker => new Promise(onFinishedLoading => { worker.onmessage = e => { var d = e["data"]; var cmd = d.cmd; // If this message is intended to a recipient that is not the main // thread, forward it to the target thread. if (d.targetThread && d.targetThread != _pthread_self()) { var targetWorker = PThread.pthreads[d.targetThread]; if (targetWorker) { targetWorker.postMessage(d, d.transferList); } else { err(`Internal error! Worker sent a message "${cmd}" to target pthread ${d.targetThread}, but that thread no longer exists!`); } return; } if (cmd === "checkMailbox") { checkMailbox(); } else if (cmd === "spawnThread") { spawnThread(d); } else if (cmd === "cleanupThread") { cleanupThread(d.thread); } else if (cmd === "loaded") { worker.loaded = true; onFinishedLoading(worker); } else if (d.target === "setimmediate") { // Worker wants to postMessage() to itself to implement setImmediate() // emulation. worker.postMessage(d); } else if (cmd === "callHandler") { Module[d.handler](...d.args); } else if (cmd) { // The received message looks like something that should be handled by this message // handler, (since there is a e.data.cmd field present), but is not one of the // recognized commands: err(`worker sent an unknown command ${cmd}`); } }; worker.onerror = e => { var message = "worker sent an error!"; if (worker.pthread_ptr) { message = `Pthread ${ptrToString(worker.pthread_ptr)} sent an error!`; } err(`${message} ${e.filename}:${e.lineno}: ${e.message}`); throw e; }; assert(wasmMemory instanceof WebAssembly.Memory, "WebAssembly memory should have been loaded by now!"); assert(wasmModule instanceof WebAssembly.Module, "WebAssembly Module should have been loaded by now!"); // When running on a pthread, none of the incoming parameters on the module // object are present. Proxy known handlers back to the main thread if specified. var handlers = []; var knownHandlers = [ "onExit", "onAbort", "print", "printErr" ]; for (var handler of knownHandlers) { if (Module.propertyIsEnumerable(handler)) { handlers.push(handler); } } // Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation. worker.postMessage({ cmd: "load", handlers, wasmMemory, wasmModule, "workerID": worker.workerID }); }), loadWasmModuleToAllWorkers(onMaybeReady) { onMaybeReady(); }, allocateUnusedWorker() { var worker; var pthreadMainJs = _scriptName; // We can't use makeModuleReceiveWithVar here since we want to also // call URL.createObjectURL on the mainScriptUrlOrBlob. if (Module["mainScriptUrlOrBlob"]) { pthreadMainJs = Module["mainScriptUrlOrBlob"]; if (typeof pthreadMainJs != "string") { pthreadMainJs = URL.createObjectURL(pthreadMainJs); } } worker = new Worker(pthreadMainJs, { // This is the way that we signal to the Web Worker that it is hosting // a pthread. "name": "em-pthread-" + PThread.nextWorkerID }); worker.workerID = PThread.nextWorkerID++; PThread.unusedWorkers.push(worker); }, getNewWorker() { if (PThread.unusedWorkers.length == 0) { // PTHREAD_POOL_SIZE_STRICT should show a warning and, if set to level `2`, return from the function. PThread.allocateUnusedWorker(); PThread.loadWasmModuleToWorker(PThread.unusedWorkers[0]); } return PThread.unusedWorkers.pop(); } }; var onPostRuns = []; var addOnPostRun = cb => onPostRuns.unshift(cb); var establishStackSpace = pthread_ptr => { // If memory growth is enabled, the memory views may have gotten out of date, // so resync them before accessing the pthread ptr below. updateMemoryViews(); var stackHigh = GROWABLE_HEAP_U32()[(((pthread_ptr) + (52)) >> 2)]; var stackSize = GROWABLE_HEAP_U32()[(((pthread_ptr) + (56)) >> 2)]; var stackLow = stackHigh - stackSize; assert(stackHigh != 0); assert(stackLow != 0); assert(stackHigh > stackLow, "stackHigh must be higher then stackLow"); // Set stack limits used by `emscripten/stack.h` function. These limits are // cached in wasm-side globals to make checks as fast as possible. _emscripten_stack_set_limits(stackHigh, stackLow); setStackLimits(); // Call inside wasm module to set up the stack frame for this pthread in wasm module scope stackRestore(stackHigh); // Write the stack cookie last, after we have set up the proper bounds and // current position of the stack. writeStackCookie(); }; /** * @param {number} ptr * @param {string} type */ function getValue(ptr, type = "i8") { if (type.endsWith("*")) type = "*"; switch (type) { case "i1": return GROWABLE_HEAP_I8()[ptr]; case "i8": return GROWABLE_HEAP_I8()[ptr]; case "i16": return GROWABLE_HEAP_I16()[((ptr) >> 1)]; case "i32": return GROWABLE_HEAP_I32()[((ptr) >> 2)]; case "i64": return HEAP64[((ptr) >> 3)]; case "float": return GROWABLE_HEAP_F32()[((ptr) >> 2)]; case "double": return GROWABLE_HEAP_F64()[((ptr) >> 3)]; case "*": return GROWABLE_HEAP_U32()[((ptr) >> 2)]; default: abort(`invalid type for getValue: ${type}`); } } var invokeEntryPoint = (ptr, arg) => { // An old thread on this worker may have been canceled without returning the // `runtimeKeepaliveCounter` to zero. Reset it now so the new thread won't // be affected. runtimeKeepaliveCounter = 0; // Same for noExitRuntime. The default for pthreads should always be false // otherwise pthreads would never complete and attempts to pthread_join to // them would block forever. // pthreads can still choose to set `noExitRuntime` explicitly, or // call emscripten_unwind_to_js_event_loop to extend their lifetime beyond // their main function. See comment in src/runtime_pthread.js for more. noExitRuntime = 0; // pthread entry points are always of signature 'void *ThreadMain(void *arg)' // Native codebases sometimes spawn threads with other thread entry point // signatures, such as void ThreadMain(void *arg), void *ThreadMain(), or // void ThreadMain(). That is not acceptable per C/C++ specification, but // x86 compiler ABI extensions enable that to work. If you find the // following line to crash, either change the signature to "proper" void // *ThreadMain(void *arg) form, or try linking with the Emscripten linker // flag -sEMULATE_FUNCTION_POINTER_CASTS to add in emulation for this x86 // ABI extension. var result = (a1 => dynCall_ii(ptr, a1))(arg); checkStackCookie(); function finish(result) { if (keepRuntimeAlive()) { EXITSTATUS = result; } else { __emscripten_thread_exit(result); } } finish(result); }; var noExitRuntime = Module["noExitRuntime"] || true; var registerTLSInit = tlsInitFunc => PThread.tlsInitFunctions.push(tlsInitFunc); var runtimeKeepalivePush = () => { runtimeKeepaliveCounter += 1; }; var setStackLimits = () => { var stackLow = _emscripten_stack_get_base(); var stackHigh = _emscripten_stack_get_end(); ___set_stack_limits(stackLow, stackHigh); }; /** * @param {number} ptr * @param {number} value * @param {string} type */ function setValue(ptr, value, type = "i8") { if (type.endsWith("*")) type = "*"; switch (type) { case "i1": GROWABLE_HEAP_I8()[ptr] = value; break; case "i8": GROWABLE_HEAP_I8()[ptr] = value; break; case "i16": GROWABLE_HEAP_I16()[((ptr) >> 1)] = value; break; case "i32": GROWABLE_HEAP_I32()[((ptr) >> 2)] = value; break; case "i64": HEAP64[((ptr) >> 3)] = BigInt(value); break; case "float": GROWABLE_HEAP_F32()[((ptr) >> 2)] = value; break; case "double": GROWABLE_HEAP_F64()[((ptr) >> 3)] = value; break; case "*": GROWABLE_HEAP_U32()[((ptr) >> 2)] = value; break; default: abort(`invalid type for setValue: ${type}`); } } var warnOnce = text => { warnOnce.shown ||= {}; if (!warnOnce.shown[text]) { warnOnce.shown[text] = 1; err(text); } }; var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder : undefined; /** * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given * array that contains uint8 values, returns a copy of that string as a * Javascript String object. * heapOrArray is either a regular array, or a JavaScript typed array view. * @param {number=} idx * @param {number=} maxBytesToRead * @return {string} */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => { var endIdx = idx + maxBytesToRead; var endPtr = idx; // TextDecoder needs to know the byte length in advance, it doesn't stop on // null terminator by itself. Also, use the length info to avoid running tiny // strings through TextDecoder, since .subarray() allocates garbage. // (As a tiny code save trick, compare endPtr against endIdx using a negation, // so that undefined/NaN means Infinity) while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.buffer instanceof ArrayBuffer ? heapOrArray.subarray(idx, endPtr) : heapOrArray.slice(idx, endPtr)); } var str = ""; // If building with TextDecoder, we have already computed the string length // above, so test loop end condition against that while (idx < endPtr) { // For UTF8 byte structure, see: // http://en.wikipedia.org/wiki/UTF-8#Description // https://www.ietf.org/rfc/rfc2279.txt // https://tools.ietf.org/html/rfc3629 var u0 = heapOrArray[idx++]; if (!(u0 & 128)) { str += String.fromCharCode(u0); continue; } var u1 = heapOrArray[idx++] & 63; if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } var u2 = heapOrArray[idx++] & 63; if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; } else { if ((u0 & 248) != 240) warnOnce("Invalid UTF-8 leading byte " + ptrToString(u0) + " encountered when deserializing a UTF-8 string in wasm memory to a JS string!"); u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); } if (u0 < 65536) { str += String.fromCharCode(u0); } else { var ch = u0 - 65536; str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)); } } return str; }; /** * 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. * * @param {number} ptr * @param {number=} maxBytesToRead - An optional length that specifies the * maximum number of bytes to read. You can omit this parameter to scan the * string until the first 0 byte. If maxBytesToRead is passed, and the string * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the * string will cut short at that byte index (i.e. maxBytesToRead will not * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing * frequent uses of UTF8ToString() with and without maxBytesToRead may throw * JS JIT optimizations off, so it is worth to consider consistently using one * @return {string} */ var UTF8ToString = (ptr, maxBytesToRead) => { assert(typeof ptr == "number", `UTF8ToString expects a number (got ${typeof ptr})`); return ptr ? UTF8ArrayToString(GROWABLE_HEAP_U8(), ptr, maxBytesToRead) : ""; }; var ___assert_fail = (condition, filename, line, func) => abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]); class ExceptionInfo { // excPtr - Thrown object pointer to wrap. Metadata pointer is calculated from it. constructor(excPtr) { this.excPtr = excPtr; this.ptr = excPtr - 24; } set_type(type) { GROWABLE_HEAP_U32()[(((this.ptr) + (4)) >> 2)] = type; } get_type() { return GROWABLE_HEAP_U32()[(((this.ptr) + (4)) >> 2)]; } set_destructor(destructor) { GROWABLE_HEAP_U32()[(((this.ptr) + (8)) >> 2)] = destructor; } get_destructor() { return GROWABLE_HEAP_U32()[(((this.ptr) + (8)) >> 2)]; } set_caught(caught) { caught = caught ? 1 : 0; GROWABLE_HEAP_I8()[(this.ptr) + (12)] = caught; } get_caught() { return GROWABLE_HEAP_I8()[(this.ptr) + (12)] != 0; } set_rethrown(rethrown) { rethrown = rethrown ? 1 : 0; GROWABLE_HEAP_I8()[(this.ptr) + (13)] = rethrown; } get_rethrown() { return GROWABLE_HEAP_I8()[(this.ptr) + (13)] != 0; } // Initialize native structure fields. Should be called once after allocated. init(type, destructor) { this.set_adjusted_ptr(0); this.set_type(type); this.set_destructor(destructor); } set_adjusted_ptr(adjustedPtr) { GROWABLE_HEAP_U32()[(((this.ptr) + (16)) >> 2)] = adjustedPtr; } get_adjusted_ptr() { return GROWABLE_HEAP_U32()[(((this.ptr) + (16)) >> 2)]; } } var exceptionLast = 0; var uncaughtExceptionCount = 0; var ___cxa_throw = (ptr, type, destructor) => { var info = new ExceptionInfo(ptr); // Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception. info.init(type, destructor); exceptionLast = ptr; uncaughtExceptionCount++; assert(false, "Exception thrown, but exception catching is not enabled. Compile with -sNO_DISABLE_EXCEPTION_CATCHING or -sEXCEPTION_CATCHING_ALLOWED=[..] to catch."); }; var ___handle_stack_overflow = requested => { var base = _emscripten_stack_get_base(); var end = _emscripten_stack_get_end(); abort(`stack overflow (Attempt to set SP to ${ptrToString(requested)}` + `, with stack limits [${ptrToString(end)} - ${ptrToString(base)}` + "]). If you require more stack space build with -sSTACK_SIZE="); }; function pthreadCreateProxied(pthread_ptr, attr, startRoutine, arg) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(2, 0, 1, pthread_ptr, attr, startRoutine, arg); return ___pthread_create_js(pthread_ptr, attr, startRoutine, arg); } var _emscripten_has_threading_support = () => typeof SharedArrayBuffer != "undefined"; var ___pthread_create_js = (pthread_ptr, attr, startRoutine, arg) => { if (!_emscripten_has_threading_support()) { dbg("pthread_create: environment does not support SharedArrayBuffer, pthreads are not available"); return 6; } // List of JS objects that will transfer ownership to the Worker hosting the thread var transferList = []; var error = 0; // Deduce which WebGL canvases (HTMLCanvasElements or OffscreenCanvases) should be passed over to the // Worker that hosts the spawned pthread. // Comma-delimited list of CSS selectors that must identify canvases by IDs: "#canvas1, #canvas2, ..." var transferredCanvasNames = attr ? GROWABLE_HEAP_U32()[(((attr) + (40)) >> 2)] : 0; // Proxied canvases string pointer -1/MAX_PTR is used as a special token to // fetch whatever canvases were passed to build in // -sOFFSCREENCANVASES_TO_PTHREAD= command line. if (transferredCanvasNames == 4294967295) { transferredCanvasNames = "#canvas"; } else { transferredCanvasNames = UTF8ToString(transferredCanvasNames).trim(); } transferredCanvasNames = transferredCanvasNames ? transferredCanvasNames.split(",") : []; var offscreenCanvases = {}; // Dictionary of OffscreenCanvas objects we'll transfer to the created thread to own var moduleCanvasId = Module["canvas"]?.id || ""; // Note that transferredCanvasNames might be null (so we cannot do a for-of loop). for (var name of transferredCanvasNames) { name = name.trim(); var offscreenCanvasInfo; try { if (name == "#canvas") { if (!Module["canvas"]) { err(`pthread_create: could not find canvas with ID "${name}" to transfer to thread!`); error = 28; break; } name = Module["canvas"].id; } assert(typeof GL == "object", "OFFSCREENCANVAS_SUPPORT assumes GL is in use (you can force-include it with '-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$GL')"); if (GL.offscreenCanvases[name]) { offscreenCanvasInfo = GL.offscreenCanvases[name]; GL.offscreenCanvases[name] = null; // This thread no longer owns this canvas. if (Module["canvas"] instanceof OffscreenCanvas && name === Module["canvas"].id) Module["canvas"] = null; } else if (!ENVIRONMENT_IS_PTHREAD) { var canvas = (Module["canvas"] && Module["canvas"].id === name) ? Module["canvas"] : document.querySelector(name); if (!canvas) { err(`pthread_create: could not find canvas with ID "${name}" to transfer to thread!`); error = 28; break; } if (canvas.controlTransferredOffscreen) { err(`pthread_create: cannot transfer canvas with ID "${name}" to thread, since the current thread does not have control over it!`); error = 63; // Operation not permitted, some other thread is accessing the canvas. break; } if (canvas.transferControlToOffscreen) { // Create a shared information block in heap so that we can control // the canvas size from any thread. if (!canvas.canvasSharedPtr) { canvas.canvasSharedPtr = _malloc(12); GROWABLE_HEAP_I32()[((canvas.canvasSharedPtr) >> 2)] = canvas.width; GROWABLE_HEAP_I32()[(((canvas.canvasSharedPtr) + (4)) >> 2)] = canvas.height; GROWABLE_HEAP_U32()[(((canvas.canvasSharedPtr) + (8)) >> 2)] = 0; } offscreenCanvasInfo = { offscreenCanvas: canvas.transferControlToOffscreen(), canvasSharedPtr: canvas.canvasSharedPtr, id: canvas.id }; // After calling canvas.transferControlToOffscreen(), it is no // longer possible to access certain operations on the canvas, such // as resizing it or obtaining GL contexts via it. // Use this field to remember that we have permanently converted // this Canvas to be controlled via an OffscreenCanvas (there is no // way to undo this in the spec) canvas.controlTransferredOffscreen = true; } else { err(`pthread_create: cannot transfer control of canvas "${name}" to pthread, because current browser does not support OffscreenCanvas!`); // If building with OFFSCREEN_FRAMEBUFFER=1 mode, we don't need to // be able to transfer control to offscreen, but WebGL can be // proxied from worker to main thread. err("pthread_create: Build with -sOFFSCREEN_FRAMEBUFFER to enable fallback proxying of GL commands from pthread to main thread."); return 52; } } if (offscreenCanvasInfo) { transferList.push(offscreenCanvasInfo.offscreenCanvas); offscreenCanvases[offscreenCanvasInfo.id] = offscreenCanvasInfo; } } catch (e) { err(`pthread_create: failed to transfer control of canvas "${name}" to OffscreenCanvas! Error: ${e}`); return 28; } } // Synchronously proxy the thread creation to main thread if possible. If we // need to transfer ownership of objects, then proxy asynchronously via // postMessage. if (ENVIRONMENT_IS_PTHREAD && (transferList.length === 0 || error)) { return pthreadCreateProxied(pthread_ptr, attr, startRoutine, arg); } // If on the main thread, and accessing Canvas/OffscreenCanvas failed, abort // with the detected error. if (error) return error; // Register for each of the transferred canvases that the new thread now // owns the OffscreenCanvas. for (var canvas of Object.values(offscreenCanvases)) { // pthread ptr to the thread that owns this canvas. GROWABLE_HEAP_U32()[(((canvas.canvasSharedPtr) + (8)) >> 2)] = pthread_ptr; } var threadParams = { startRoutine, pthread_ptr, arg, moduleCanvasId, offscreenCanvases, transferList }; if (ENVIRONMENT_IS_PTHREAD) { // The prepopulated pool of web workers that can host pthreads is stored // in the main JS thread. Therefore if a pthread is attempting to spawn a // new thread, the thread creation must be deferred to the main JS thread. threadParams.cmd = "spawnThread"; postMessage(threadParams, transferList); // When we defer thread creation this way, we have no way to detect thread // creation synchronously today, so we have to assume success and return 0. return 0; } // We are the main thread, so we have the pthread warmup pool in this // thread and can fire off JS thread creation directly ourselves. return spawnThread(threadParams); }; /** @suppress {duplicate } */ var syscallGetVarargI = () => { assert(SYSCALLS.varargs != undefined); // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. var ret = GROWABLE_HEAP_I32()[((+SYSCALLS.varargs) >> 2)]; SYSCALLS.varargs += 4; return ret; }; var syscallGetVarargP = syscallGetVarargI; var PATH = { isAbs: path => path.charAt(0) === "/", splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; return splitPathRe.exec(filename).slice(1); }, normalizeArray: (parts, allowAboveRoot) => { // if the path tries to go above the root, `up` ends up > 0 var up = 0; for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i]; if (last === ".") { parts.splice(i, 1); } else if (last === "..") { parts.splice(i, 1); up++; } else if (up) { parts.splice(i, 1); up--; } } // if the path is allowed to go above the root, restore leading ..s if (allowAboveRoot) { for (;up; up--) { parts.unshift(".."); } } return parts; }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.slice(-1) === "/"; // Normalize the path path = PATH.normalizeArray(path.split("/").filter(p => !!p), !isAbsolute).join("/"); if (!path && !isAbsolute) { path = "."; } if (path && trailingSlash) { path += "/"; } return (isAbsolute ? "/" : "") + path; }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1]; if (!root && !dir) { // No dirname whatsoever return "."; } if (dir) { // It has a dirname, strip trailing slash dir = dir.slice(0, -1); } return root + dir; }, basename: path => path && path.match(/([^\/]+|\/)\/*$/)[1], join: (...paths) => PATH.normalize(paths.join("/")), join2: (l, r) => PATH.normalize(l + "/" + r) }; var initRandomFill = () => view => view.set(crypto.getRandomValues(new Uint8Array(view.byteLength))); var randomFill = view => { // Lazily init on the first invocation. (randomFill = initRandomFill())(view); }; var PATH_FS = { resolve: (...args) => { var resolvedPath = "", resolvedAbsolute = false; for (var i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = (i >= 0) ? args[i] : FS.cwd(); // Skip empty and invalid entries if (typeof path != "string") { throw new TypeError("Arguments to path.resolve must be strings"); } else if (!path) { return ""; } resolvedPath = path + "/" + resolvedPath; resolvedAbsolute = PATH.isAbs(path); } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) resolvedPath = PATH.normalizeArray(resolvedPath.split("/").filter(p => !!p), !resolvedAbsolute).join("/"); return ((resolvedAbsolute ? "/" : "") + resolvedPath) || "."; }, relative: (from, to) => { from = PATH_FS.resolve(from).slice(1); to = PATH_FS.resolve(to).slice(1); function trim(arr) { var start = 0; for (;start < arr.length; start++) { if (arr[start] !== "") break; } var end = arr.length - 1; for (;end >= 0; end--) { if (arr[end] !== "") break; } if (start > end) return []; return arr.slice(start, end - start + 1); } var fromParts = trim(from.split("/")); var toParts = trim(to.split("/")); var length = Math.min(fromParts.length, toParts.length); var samePartsLength = length; for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i; break; } } var outputParts = []; for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push(".."); } outputParts = outputParts.concat(toParts.slice(samePartsLength)); return outputParts.join("/"); } }; var FS_stdin_getChar_buffer = []; var lengthBytesUTF8 = str => { var len = 0; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code // unit, not a Unicode code point of the character! So decode // UTF16->UTF32->UTF8. // See http://unicode.org/faq/utf_bom.html#utf16-3 var c = str.charCodeAt(i); // possibly a lead surrogate if (c <= 127) { len++; } else if (c <= 2047) { len += 2; } else if (c >= 55296 && c <= 57343) { len += 4; ++i; } else { len += 3; } } return len; }; var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { assert(typeof str === "string", `stringToUTF8Array expects a string (got ${typeof str})`); // Parameter maxBytesToWrite is not optional. Negative values, 0, null, // undefined and false each don't write out any bytes. if (!(maxBytesToWrite > 0)) return 0; var startIdx = outIdx; var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code // unit, not a Unicode code point of the character! So decode // UTF16->UTF32->UTF8. // See http://unicode.org/faq/utf_bom.html#utf16-3 // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description // and https://www.ietf.org/rfc/rfc2279.txt // and https://tools.ietf.org/html/rfc3629 var u = str.charCodeAt(i); // possibly a lead surrogate if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i); u = 65536 + ((u & 1023) << 10) | (u1 & 1023); } if (u <= 127) { if (outIdx >= endIdx) break; heap[outIdx++] = u; } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break; heap[outIdx++] = 192 | (u >> 6); heap[outIdx++] = 128 | (u & 63); } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break; heap[outIdx++] = 224 | (u >> 12); heap[outIdx++] = 128 | ((u >> 6) & 63); heap[outIdx++] = 128 | (u & 63); } else { if (outIdx + 3 >= endIdx) break; if (u > 1114111) warnOnce("Invalid Unicode code point " + ptrToString(u) + " encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF)."); heap[outIdx++] = 240 | (u >> 18); heap[outIdx++] = 128 | ((u >> 12) & 63); heap[outIdx++] = 128 | ((u >> 6) & 63); heap[outIdx++] = 128 | (u & 63); } } // Null-terminate the pointer to the buffer. heap[outIdx] = 0; return outIdx - startIdx; }; /** @type {function(string, boolean=, number=)} */ var intArrayFromString = (stringy, dontAddNull, length) => { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1; var u8array = new Array(len); var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); if (dontAddNull) u8array.length = numBytesWritten; return u8array; }; var FS_stdin_getChar = () => { if (!FS_stdin_getChar_buffer.length) { var result = null; if (typeof window != "undefined" && typeof window.prompt == "function") { // Browser. result = window.prompt("Input: "); // returns null on cancel if (result !== null) { result += "\n"; } } else {} if (!result) { return null; } FS_stdin_getChar_buffer = intArrayFromString(result, true); } return FS_stdin_getChar_buffer.shift(); }; var TTY = { ttys: [], init() {}, shutdown() {}, register(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops }; FS.registerDevice(dev, TTY.stream_ops); }, stream_ops: { open(stream) { var tty = TTY.ttys[stream.node.rdev]; if (!tty) { throw new FS.ErrnoError(43); } stream.tty = tty; stream.seekable = false; }, close(stream) { // flush any pending line data stream.tty.ops.fsync(stream.tty); }, fsync(stream) { stream.tty.ops.fsync(stream.tty); }, read(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60); } var bytesRead = 0; for (var i = 0; i < length; i++) { var result; try { result = stream.tty.ops.get_char(stream.tty); } catch (e) { throw new FS.ErrnoError(29); } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; bytesRead++; buffer[offset + i] = result; } if (bytesRead) { stream.node.atime = Date.now(); } return bytesRead; }, write(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60); } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]); } } catch (e) { throw new FS.ErrnoError(29); } if (length) { stream.node.mtime = stream.node.ctime = Date.now(); } return i; } }, default_tty_ops: { get_char(tty) { return FS_stdin_getChar(); }, put_char(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output)); tty.output = []; } else { if (val != 0) tty.output.push(val); } }, fsync(tty) { if (tty.output?.length > 0) { out(UTF8ArrayToString(tty.output)); tty.output = []; } }, ioctl_tcgets(tty) { // typical setting return { c_iflag: 25856, c_oflag: 5, c_cflag: 191, c_lflag: 35387, c_cc: [ 3, 28, 127, 21, 4, 0, 1, 0, 17, 19, 26, 0, 18, 15, 23, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }; }, ioctl_tcsets(tty, optional_actions, data) { // currently just ignore return 0; }, ioctl_tiocgwinsz(tty) { return [ 24, 80 ]; } }, default_tty1_ops: { put_char(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output)); tty.output = []; } else { if (val != 0) tty.output.push(val); } }, fsync(tty) { if (tty.output?.length > 0) { err(UTF8ArrayToString(tty.output)); tty.output = []; } } } }; var zeroMemory = (ptr, size) => GROWABLE_HEAP_U8().fill(0, ptr, ptr + size); var alignMemory = (size, alignment) => { assert(alignment, "alignment argument is required"); return Math.ceil(size / alignment) * alignment; }; var mmapAlloc = size => { size = alignMemory(size, 65536); var ptr = _emscripten_builtin_memalign(65536, size); if (ptr) zeroMemory(ptr, size); return ptr; }; var MEMFS = { ops_table: null, mount(mount) { return MEMFS.createNode(null, "/", 16895, 0); }, createNode(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { // no supported throw new FS.ErrnoError(63); } MEMFS.ops_table ||= { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink }, stream: { llseek: MEMFS.stream_ops.llseek } }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync } }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink }, stream: {} }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr }, stream: FS.chrdev_stream_ops } }; var node = FS.createNode(parent, name, mode, dev); if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node; node.stream_ops = MEMFS.ops_table.dir.stream; node.contents = {}; } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node; node.stream_ops = MEMFS.ops_table.file.stream; node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. node.contents = null; } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node; node.stream_ops = MEMFS.ops_table.link.stream; } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node; node.stream_ops = MEMFS.ops_table.chrdev.stream; } node.atime = node.mtime = node.ctime = Date.now(); // add the new node to the parent if (parent) { parent.contents[name] = node; parent.atime = parent.mtime = parent.ctime = node.atime; } return node; }, getFileDataAsTypedArray(node) { if (!node.contents) return new Uint8Array(0); if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. return new Uint8Array(node.contents); }, expandFileStorage(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0; if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough. // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. // 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; newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0); if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. var oldContents = node.contents; node.contents = new Uint8Array(newCapacity); // Allocate new storage. if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); }, resizeFileStorage(node, newSize) { if (node.usedBytes == newSize) return; if (newSize == 0) { node.contents = null; // Fully decommit when requesting a resize to zero. node.usedBytes = 0; } else { var oldContents = node.contents; node.contents = new Uint8Array(newSize); // Allocate new storage. if (oldContents) { node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); } node.usedBytes = newSize; } }, node_ops: { getattr(node) { var attr = {}; // device numbers reuse inode numbers. attr.dev = FS.isChrdev(node.mode) ? node.id : 1; attr.ino = node.id; attr.mode = node.mode; attr.nlink = 1; attr.uid = 0; attr.gid = 0; attr.rdev = node.rdev; if (FS.isDir(node.mode)) { attr.size = 4096; } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes; } else if (FS.isLink(node.mode)) { attr.size = node.link.length; } else { attr.size = 0; } attr.atime = new Date(node.atime); attr.mtime = new Date(node.mtime); attr.ctime = new Date(node.ctime); // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), // but this is not required by the standard. attr.blksize = 4096; attr.blocks = Math.ceil(attr.size / attr.blksize); return attr; }, setattr(node, attr) { for (const key of [ "mode", "atime", "mtime", "ctime" ]) { if (attr[key] != null) { node[key] = attr[key]; } } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size); } }, lookup(parent, name) { throw new FS.ErrnoError(44); }, mknod(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev); }, rename(old_node, new_dir, new_name) { var new_node; try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) {} if (new_node) { if (FS.isDir(old_node.mode)) { // 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]; new_dir.contents[new_name] = old_node; old_node.name = new_name; new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now(); }, unlink(parent, name) { delete parent.contents[name]; parent.ctime = parent.mtime = Date.now(); }, rmdir(parent, name) { var node = FS.lookupNode(parent, name); for (var i in node.contents) { throw new FS.ErrnoError(55); } delete parent.contents[name]; parent.ctime = parent.mtime = Date.now(); }, readdir(node) { return [ ".", "..", ...Object.keys(node.contents) ]; }, symlink(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0); node.link = oldpath; return node; }, readlink(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28); } return node.link; } }, stream_ops: { read(stream, buffer, offset, length, position) { var contents = stream.node.contents; if (position >= stream.node.usedBytes) return 0; var size = Math.min(stream.node.usedBytes - position, length); assert(size >= 0); if (size > 8 && contents.subarray) { // non-trivial, and typed array buffer.set(contents.subarray(position, position + size), offset); } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; } return size; }, write(stream, buffer, offset, length, position, canOwn) { // The data buffer should be a typed array view assert(!(buffer instanceof ArrayBuffer)); // If the buffer is located in main memory (HEAP), and if // memory can grow, we can't hold on to references of the // memory buffer, as they may get invalidated. That means we // need to do copy its contents. if (buffer.buffer === GROWABLE_HEAP_I8().buffer) { canOwn = false; } if (!length) return 0; var node = stream.node; node.mtime = node.ctime = Date.now(); if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array? if (canOwn) { assert(position === 0, "canOwn must imply no weird position inside the file"); node.contents = buffer.subarray(offset, offset + length); node.usedBytes = length; return 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. node.contents = buffer.slice(offset, offset + length); node.usedBytes = length; return length; } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file? node.contents.set(buffer.subarray(offset, offset + length), position); return length; } } // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. MEMFS.expandFileStorage(node, position + length); if (node.contents.subarray && buffer.subarray) { // Use typed array write which is available. node.contents.set(buffer.subarray(offset, offset + length), position); } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i]; } } node.usedBytes = Math.max(node.usedBytes, position + length); return length; }, llseek(stream, offset, whence) { var position = offset; if (whence === 1) { position += stream.position; } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes; } } if (position < 0) { throw new FS.ErrnoError(28); } return position; }, mmap(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43); } var ptr; var allocated; var contents = stream.node.contents; // Only make a new copy when MAP_PRIVATE is specified. if (!(flags & 2) && contents && contents.buffer === GROWABLE_HEAP_I8().buffer) { // We can't emulate MAP_SHARED when the file is not backed by the // buffer we're mapping to (e.g. the HEAP buffer). allocated = false; ptr = contents.byteOffset; } else { allocated = true; ptr = mmapAlloc(length); if (!ptr) { throw new FS.ErrnoError(48); } if (contents) { // Try to avoid unnecessary slices. if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length); } else { contents = Array.prototype.slice.call(contents, position, position + length); } } GROWABLE_HEAP_I8().set(contents, ptr); } } return { ptr, allocated }; }, msync(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); // should we check if bytesWritten and length are the same? return 0; } } }; var asyncLoad = async url => { var arrayBuffer = await readAsync(url); assert(arrayBuffer, `Loading data file "${url}" failed (no arrayBuffer).`); return new Uint8Array(arrayBuffer); }; asyncLoad.isAsync = true; var FS_createDataFile = (parent, name, fileData, canRead, canWrite, canOwn) => { FS.createDataFile(parent, name, fileData, canRead, canWrite, canOwn); }; var preloadPlugins = Module["preloadPlugins"] || []; var FS_handledByPreloadPlugin = (byteArray, fullname, finish, onerror) => { // Ensure plugins are ready. if (typeof Browser != "undefined") Browser.init(); var handled = false; preloadPlugins.forEach(plugin => { if (handled) return; if (plugin["canHandle"](fullname)) { plugin["handle"](byteArray, fullname, finish, onerror); handled = true; } }); return handled; }; var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => { // TODO we should allow people to just pass in a complete filename instead // of parent and name being that we just join them anyways var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; var dep = getUniqueRunDependency(`cp ${fullname}`); // might have several active requests for the same fullname function processData(byteArray) { function finish(byteArray) { preFinish?.(); if (!dontCreateFile) { FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); } onload?.(); removeRunDependency(dep); } if (FS_handledByPreloadPlugin(byteArray, fullname, finish, () => { onerror?.(); removeRunDependency(dep); })) { return; } finish(byteArray); } addRunDependency(dep); if (typeof url == "string") { asyncLoad(url).then(processData, onerror); } else { processData(url); } }; var FS_modeStringToFlags = str => { var flagModes = { "r": 0, "r+": 2, "w": 512 | 64 | 1, "w+": 512 | 64 | 2, "a": 1024 | 64 | 1, "a+": 1024 | 64 | 2 }; var flags = flagModes[str]; if (typeof flags == "undefined") { throw new Error(`Unknown file open mode: ${str}`); } return flags; }; var FS_getMode = (canRead, canWrite) => { var mode = 0; if (canRead) mode |= 292 | 73; if (canWrite) mode |= 146; return mode; }; var strError = errno => UTF8ToString(_strerror(errno)); var ERRNO_CODES = { "EPERM": 63, "ENOENT": 44, "ESRCH": 71, "EINTR": 27, "EIO": 29, "ENXIO": 60, "E2BIG": 1, "ENOEXEC": 45, "EBADF": 8, "ECHILD": 12, "EAGAIN": 6, "EWOULDBLOCK": 6, "ENOMEM": 48, "EACCES": 2, "EFAULT": 21, "ENOTBLK": 105, "EBUSY": 10, "EEXIST": 20, "EXDEV": 75, "ENODEV": 43, "ENOTDIR": 54, "EISDIR": 31, "EINVAL": 28, "ENFILE": 41, "EMFILE": 33, "ENOTTY": 59, "ETXTBSY": 74, "EFBIG": 22, "ENOSPC": 51, "ESPIPE": 70, "EROFS": 69, "EMLINK": 34, "EPIPE": 64, "EDOM": 18, "ERANGE": 68, "ENOMSG": 49, "EIDRM": 24, "ECHRNG": 106, "EL2NSYNC": 156, "EL3HLT": 107, "EL3RST": 108, "ELNRNG": 109, "EUNATCH": 110, "ENOCSI": 111, "EL2HLT": 112, "EDEADLK": 16, "ENOLCK": 46, "EBADE": 113, "EBADR": 114, "EXFULL": 115, "ENOANO": 104, "EBADRQC": 103, "EBADSLT": 102, "EDEADLOCK": 16, "EBFONT": 101, "ENOSTR": 100, "ENODATA": 116, "ETIME": 117, "ENOSR": 118, "ENONET": 119, "ENOPKG": 120, "EREMOTE": 121, "ENOLINK": 47, "EADV": 122, "ESRMNT": 123, "ECOMM": 124, "EPROTO": 65, "EMULTIHOP": 36, "EDOTDOT": 125, "EBADMSG": 9, "ENOTUNIQ": 126, "EBADFD": 127, "EREMCHG": 128, "ELIBACC": 129, "ELIBBAD": 130, "ELIBSCN": 131, "ELIBMAX": 132, "ELIBEXEC": 133, "ENOSYS": 52, "ENOTEMPTY": 55, "ENAMETOOLONG": 37, "ELOOP": 32, "EOPNOTSUPP": 138, "EPFNOSUPPORT": 139, "ECONNRESET": 15, "ENOBUFS": 42, "EAFNOSUPPORT": 5, "EPROTOTYPE": 67, "ENOTSOCK": 57, "ENOPROTOOPT": 50, "ESHUTDOWN": 140, "ECONNREFUSED": 14, "EADDRINUSE": 3, "ECONNABORTED": 13, "ENETUNREACH": 40, "ENETDOWN": 38, "ETIMEDOUT": 73, "EHOSTDOWN": 142, "EHOSTUNREACH": 23, "EINPROGRESS": 26, "EALREADY": 7, "EDESTADDRREQ": 17, "EMSGSIZE": 35, "EPROTONOSUPPORT": 66, "ESOCKTNOSUPPORT": 137, "EADDRNOTAVAIL": 4, "ENETRESET": 39, "EISCONN": 30, "ENOTCONN": 53, "ETOOMANYREFS": 141, "EUSERS": 136, "EDQUOT": 19, "ESTALE": 72, "ENOTSUP": 138, "ENOMEDIUM": 148, "EILSEQ": 25, "EOVERFLOW": 61, "ECANCELED": 11, "ENOTRECOVERABLE": 56, "EOWNERDEAD": 62, "ESTRPIPE": 135 }; var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: "/", initialized: false, ignorePermissions: true, filesystems: null, syncFSRequests: 0, readFiles: {}, ErrnoError: class extends Error { name="ErrnoError"; // We set the `name` property to be able to identify `FS.ErrnoError` // - the `name` is a standard ECMA-262 property of error objects. Kind of good to have it anyway. // - when using PROXYFS, an error can come from an underlying FS // as different FS objects have their own FS.ErrnoError each, // the test `err instanceof FS.ErrnoError` won't detect an error coming from another filesystem, causing bugs. // we'll use the reliable test `err.name == "ErrnoError"` instead constructor(errno) { super(runtimeInitialized ? strError(errno) : ""); this.errno = errno; for (var key in ERRNO_CODES) { if (ERRNO_CODES[key] === errno) { this.code = key; break; } } } }, FSStream: class { shared={}; get object() { return this.node; } set object(val) { this.node = val; } get isRead() { return (this.flags & 2097155) !== 1; } get isWrite() { return (this.flags & 2097155) !== 0; } get isAppend() { return (this.flags & 1024); } get flags() { return this.shared.flags; } set flags(val) { this.shared.flags = val; } get position() { return this.shared.position; } set position(val) { this.shared.position = val; } }, FSNode: class { node_ops={}; stream_ops={}; readMode=292 | 73; writeMode=146; mounted=null; constructor(parent, name, mode, rdev) { if (!parent) { parent = this; } this.parent = parent; this.mount = parent.mount; this.id = FS.nextInode++; this.name = name; this.mode = mode; this.rdev = rdev; this.atime = this.mtime = this.ctime = Date.now(); } get read() { return (this.mode & this.readMode) === this.readMode; } set read(val) { val ? this.mode |= this.readMode : this.mode &= ~this.readMode; } get write() { return (this.mode & this.writeMode) === this.writeMode; } set write(val) { val ? this.mode |= this.writeMode : this.mode &= ~this.writeMode; } get isFolder() { return FS.isDir(this.mode); } get isDevice() { return FS.isChrdev(this.mode); } }, lookupPath(path, opts = {}) { if (!path) { throw new FS.ErrnoError(44); } opts.follow_mount ??= true; if (!PATH.isAbs(path)) { path = FS.cwd() + "/" + path; } // limit max consecutive symlinks to 40 (SYMLOOP_MAX). linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { // split the absolute path var parts = path.split("/").filter(p => !!p); // start at the root var current = FS.root; var current_path = "/"; for (var i = 0; i < parts.length; i++) { var islast = (i === parts.length - 1); if (islast && opts.parent) { // stop resolving break; } if (parts[i] === ".") { continue; } if (parts[i] === "..") { current_path = PATH.dirname(current_path); current = current.parent; continue; } current_path = PATH.join2(current_path, parts[i]); try { current = FS.lookupNode(current, parts[i]); } catch (e) { // if noent_okay is true, suppress a ENOENT in the last component // and return an object with an undefined node. This is needed for // resolving symlinks in the path when creating a file. if ((e?.errno === 44) && islast && opts.noent_okay) { return { path: current_path }; } throw e; } // jump to the mount's root node if this is a mountpoint if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) { current = current.mounted.root; } // by default, lookupPath will not follow a symlink if it is the final path component. // setting opts.follow = true will override this behavior. if (FS.isLink(current.mode) && (!islast || opts.follow)) { if (!current.node_ops.readlink) { throw new FS.ErrnoError(52); } var link = current.node_ops.readlink(current); if (!PATH.isAbs(link)) { link = PATH.dirname(current_path) + "/" + link; } path = link + "/" + parts.slice(i + 1).join("/"); continue linkloop; } } return { path: current_path, node: current }; } throw new FS.ErrnoError(32); }, getPath(node) { var path; while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint; if (!path) return mount; return mount[mount.length - 1] !== "/" ? `${mount}/${path}` : mount + path; } path = path ? `${node.name}/${path}` : node.name; node = node.parent; } }, hashName(parentid, name) { var hash = 0; for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; } return ((parentid + hash) >>> 0) % FS.nameTable.length; }, hashAddNode(node) { var hash = FS.hashName(node.parent.id, node.name); node.name_next = FS.nameTable[hash]; FS.nameTable[hash] = node; }, hashRemoveNode(node) { var hash = FS.hashName(node.parent.id, node.name); if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next; } else { var current = FS.nameTable[hash]; while (current) { if (current.name_next === node) { current.name_next = node.name_next; break; } current = current.name_next; } } }, lookupNode(parent, name) { var errCode = FS.mayLookup(parent); if (errCode) { throw new FS.ErrnoError(errCode); } var hash = FS.hashName(parent.id, name); for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name; if (node.parent.id === parent.id && nodeName === name) { return node; } } // if we failed to find it in the cache, call into the VFS return FS.lookup(parent, name); }, createNode(parent, name, mode, rdev) { assert(typeof parent == "object"); var node = new FS.FSNode(parent, name, mode, rdev); FS.hashAddNode(node); return node; }, destroyNode(node) { FS.hashRemoveNode(node); }, isRoot(node) { return node === node.parent; }, isMountpoint(node) { return !!node.mounted; }, isFile(mode) { return (mode & 61440) === 32768; }, isDir(mode) { return (mode & 61440) === 16384; }, isLink(mode) { return (mode & 61440) === 40960; }, isChrdev(mode) { return (mode & 61440) === 8192; }, isBlkdev(mode) { return (mode & 61440) === 24576; }, isFIFO(mode) { return (mode & 61440) === 4096; }, isSocket(mode) { return (mode & 49152) === 49152; }, flagsToPermissionString(flag) { var perms = [ "r", "w", "rw" ][flag & 3]; if ((flag & 512)) { perms += "w"; } return perms; }, nodePermissions(node, perms) { if (FS.ignorePermissions) { return 0; } // return 0 if any user, group or owner bits are set. if (perms.includes("r") && !(node.mode & 292)) { return 2; } else if (perms.includes("w") && !(node.mode & 146)) { return 2; } else if (perms.includes("x") && !(node.mode & 73)) { return 2; } return 0; }, mayLookup(dir) { if (!FS.isDir(dir.mode)) return 54; var errCode = FS.nodePermissions(dir, "x"); if (errCode) return errCode; if (!dir.node_ops.lookup) return 2; return 0; }, mayCreate(dir, name) { if (!FS.isDir(dir.mode)) { return 54; } try { var node = FS.lookupNode(dir, name); return 20; } catch (e) {} return FS.nodePermissions(dir, "wx"); }, mayDelete(dir, name, isdir) { var node; try { node = FS.lookupNode(dir, name); } catch (e) { return e.errno; } var errCode = FS.nodePermissions(dir, "wx"); if (errCode) { return errCode; } if (isdir) { if (!FS.isDir(node.mode)) { return 54; } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10; } } else { if (FS.isDir(node.mode)) { return 31; } } return 0; }, mayOpen(node, flags) { if (!node) { return 44; } if (FS.isLink(node.mode)) { return 32; } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== "r" || (flags & (512 | 64))) { // TODO: check for O_SEARCH? (== search for dir only) return 31; } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); }, checkOpExists(op, err) { if (!op) { throw new FS.ErrnoError(err); } return op; }, MAX_OPEN_FDS: 4096, nextfd() { for (var fd = 0; fd <= FS.MAX_OPEN_FDS; fd++) { if (!FS.streams[fd]) { return fd; } } throw new FS.ErrnoError(33); }, getStreamChecked(fd) { var stream = FS.getStream(fd); if (!stream) { throw new FS.ErrnoError(8); } return stream; }, getStream: fd => FS.streams[fd], createStream(stream, fd = -1) { assert(fd >= -1); // clone it, so we can return an instance of FSStream stream = Object.assign(new FS.FSStream, stream); if (fd == -1) { fd = FS.nextfd(); } stream.fd = fd; FS.streams[fd] = stream; return stream; }, closeStream(fd) { FS.streams[fd] = null; }, dupStream(origStream, fd = -1) { var stream = FS.createStream(origStream, fd); stream.stream_ops?.dup?.(stream); return stream; }, doSetAttr(stream, node, attr) { var setattr = stream?.stream_ops.setattr; var arg = setattr ? stream : node; setattr ??= node.node_ops.setattr; FS.checkOpExists(setattr, 63); setattr(arg, attr); }, chrdev_stream_ops: { open(stream) { var device = FS.getDevice(stream.node.rdev); // override node's stream ops with the device's stream.stream_ops = device.stream_ops; // forward the open call stream.stream_ops.open?.(stream); }, llseek() { throw new FS.ErrnoError(70); } }, major: dev => ((dev) >> 8), minor: dev => ((dev) & 255), makedev: (ma, mi) => ((ma) << 8 | (mi)), registerDevice(dev, ops) { FS.devices[dev] = { stream_ops: ops }; }, getDevice: dev => FS.devices[dev], getMounts(mount) { var mounts = []; var check = [ mount ]; while (check.length) { var m = check.pop(); mounts.push(m); check.push(...m.mounts); } return mounts; }, syncfs(populate, callback) { if (typeof populate == "function") { callback = populate; populate = false; } FS.syncFSRequests++; if (FS.syncFSRequests > 1) { err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`); } var mounts = FS.getMounts(FS.root.mount); var completed = 0; function doCallback(errCode) { assert(FS.syncFSRequests > 0); FS.syncFSRequests--; return callback(errCode); } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true; return doCallback(errCode); } return; } if (++completed >= mounts.length) { doCallback(null); } } // sync all mounts mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null); } mount.type.syncfs(mount, populate, done); }); }, mount(type, opts, mountpoint) { if (typeof type == "string") { // The filesystem was not included, and instead we have an error // message stored in the variable. throw type; } var root = mountpoint === "/"; var pseudo = !mountpoint; var node; if (root && FS.root) { throw new FS.ErrnoError(10); } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); mountpoint = lookup.path; // use the absolute path node = lookup.node; if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10); } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54); } } var mount = { type, opts, mountpoint, mounts: [] }; // create a root node for the fs var mountRoot = type.mount(mount); mountRoot.mount = mount; mount.root = mountRoot; if (root) { FS.root = mountRoot; } else if (node) { // set as a mountpoint node.mounted = mount; // add the new mount to the current mount's children if (node.mount) { node.mount.mounts.push(mount); } } return mountRoot; }, unmount(mountpoint) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28); } // destroy the nodes for this mount, and all its child mounts var node = lookup.node; var mount = node.mounted; var mounts = FS.getMounts(mount); Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash]; while (current) { var next = current.name_next; if (mounts.includes(current.mount)) { FS.destroyNode(current); } current = next; } }); // no longer a mountpoint node.mounted = null; // remove this mount from the child mounts var idx = node.mount.mounts.indexOf(mount); assert(idx !== -1); node.mount.mounts.splice(idx, 1); }, lookup(parent, name) { return parent.node_ops.lookup(parent, name); }, mknod(path, mode, dev) { var lookup = FS.lookupPath(path, { parent: true }); var parent = lookup.node; var name = PATH.basename(path); if (!name) { throw new FS.ErrnoError(28); } if (name === "." || name === "..") { throw new FS.ErrnoError(20); } var errCode = FS.mayCreate(parent, name); if (errCode) { throw new FS.ErrnoError(errCode); } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63); } return parent.node_ops.mknod(parent, name, mode, dev); }, statfs(path) { return FS.statfsNode(FS.lookupPath(path, { follow: true }).node); }, statfsStream(stream) { // We keep a separate statfsStream function because noderawfs overrides // it. In noderawfs, stream.node is sometimes null. Instead, we need to // look at stream.path. return FS.statfsNode(stream.node); }, statfsNode(node) { // NOTE: None of the defaults here are true. We're just returning safe and // sane values. Currently nodefs and rawfs replace these defaults, // other file systems leave them alone. var rtn = { bsize: 4096, frsize: 4096, blocks: 1e6, bfree: 5e5, bavail: 5e5, files: FS.nextInode, ffree: FS.nextInode - 1, fsid: 42, flags: 2, namelen: 255 }; if (node.node_ops.statfs) { Object.assign(rtn, node.node_ops.statfs(node.mount.opts.root)); } return rtn; }, create(path, mode = 438) { mode &= 4095; mode |= 32768; return FS.mknod(path, mode, 0); }, mkdir(path, mode = 511) { mode &= 511 | 512; mode |= 16384; return FS.mknod(path, mode, 0); }, mkdirTree(path, mode) { var dirs = path.split("/"); var d = ""; for (var dir of dirs) { if (!dir) continue; if (d || PATH.isAbs(path)) d += "/"; d += dir; try { FS.mkdir(d, mode); } catch (e) { if (e.errno != 20) throw e; } } }, mkdev(path, mode, dev) { if (typeof dev == "undefined") { dev = mode; mode = 438; } mode |= 8192; return FS.mknod(path, mode, dev); }, symlink(oldpath, newpath) { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44); } var 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); } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63); } return parent.node_ops.symlink(parent, newname, oldpath); }, rename(old_path, new_path) { var old_dirname = PATH.dirname(old_path); var new_dirname = PATH.dirname(new_path); var old_name = PATH.basename(old_path); var new_name = PATH.basename(new_path); // parents must exist var lookup, old_dir, new_dir; // let the errors from non existent directories percolate up lookup = FS.lookupPath(old_path, { parent: true }); old_dir = lookup.node; lookup = FS.lookupPath(new_path, { parent: true }); new_dir = lookup.node; if (!old_dir || !new_dir) throw new FS.ErrnoError(44); // need to be part of the same mount if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75); } // source must exist var old_node = FS.lookupNode(old_dir, old_name); // old path should not be an ancestor of the new path var relative = PATH_FS.relative(old_path, new_dirname); if (relative.charAt(0) !== ".") { throw new FS.ErrnoError(28); } // new path should not be an ancestor of the old path relative = PATH_FS.relative(new_path, old_dirname); if (relative.charAt(0) !== ".") { throw new FS.ErrnoError(55); } // see if the new path already exists var new_node; try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) {} // early out if nothing needs to change if (old_node === new_node) { return; } // we'll need to delete the old entry var isdir = FS.isDir(old_node.mode); var errCode = FS.mayDelete(old_dir, old_name, isdir); if (errCode) { throw new FS.ErrnoError(errCode); } // need delete permissions if we'll be overwriting. // need create permissions if new doesn't already exist. errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name); if (errCode) { throw new FS.ErrnoError(errCode); } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63); } if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { throw new FS.ErrnoError(10); } // if we are going to change the parent, check write permissions if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, "w"); if (errCode) { throw new FS.ErrnoError(errCode); } } // remove the node from the lookup hash FS.hashRemoveNode(old_node); // do the underlying fs rename try { old_dir.node_ops.rename(old_node, new_dir, new_name); // update old node (we do this here to avoid each backend // needing to) old_node.parent = new_dir; } catch (e) { throw e; } finally { // add the node back to the hash (in case node_ops.rename // changed its name) FS.hashAddNode(old_node); } }, rmdir(path) { var lookup = FS.lookupPath(path, { parent: true }); var parent = lookup.node; var name = PATH.basename(path); var node = FS.lookupNode(parent, name); var errCode = FS.mayDelete(parent, name, true); if (errCode) { throw new FS.ErrnoError(errCode); } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63); } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10); } parent.node_ops.rmdir(parent, name); FS.destroyNode(node); }, readdir(path) { var lookup = FS.lookupPath(path, { follow: true }); var node = lookup.node; var readdir = FS.checkOpExists(node.node_ops.readdir, 54); return readdir(node); }, unlink(path) { var lookup = FS.lookupPath(path, { parent: true }); var parent = lookup.node; if (!parent) { throw new FS.ErrnoError(44); } var name = PATH.basename(path); var node = FS.lookupNode(parent, name); var errCode = FS.mayDelete(parent, name, false); if (errCode) { // According to POSIX, we should map EISDIR to EPERM, but // we instead do what Linux does (and we must, as we use // the musl linux libc). throw new FS.ErrnoError(errCode); } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63); } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10); } parent.node_ops.unlink(parent, name); FS.destroyNode(node); }, readlink(path) { var lookup = FS.lookupPath(path); var link = lookup.node; if (!link) { throw new FS.ErrnoError(44); } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28); } return link.node_ops.readlink(link); }, stat(path, dontFollow) { var lookup = FS.lookupPath(path, { follow: !dontFollow }); var node = lookup.node; var getattr = FS.checkOpExists(node.node_ops.getattr, 63); return getattr(node); }, fstat(fd) { var stream = FS.getStreamChecked(fd); var node = stream.node; var getattr = stream.stream_ops.getattr; var arg = getattr ? stream : node; getattr ??= node.node_ops.getattr; FS.checkOpExists(getattr, 63); return getattr(arg); }, lstat(path) { return FS.stat(path, true); }, doChmod(stream, node, mode, dontFollow) { FS.doSetAttr(stream, node, { mode: (mode & 4095) | (node.mode & ~4095), ctime: Date.now(), dontFollow }); }, chmod(path, mode, dontFollow) { var node; if (typeof path == "string") { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { node = path; } FS.doChmod(null, node, mode, dontFollow); }, lchmod(path, mode) { FS.chmod(path, mode, true); }, fchmod(fd, mode) { var stream = FS.getStreamChecked(fd); FS.doChmod(stream, stream.node, mode, false); }, doChown(stream, node, dontFollow) { FS.doSetAttr(stream, node, { timestamp: Date.now(), dontFollow }); }, chown(path, uid, gid, dontFollow) { var node; if (typeof path == "string") { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { node = path; } FS.doChown(null, node, dontFollow); }, lchown(path, uid, gid) { FS.chown(path, uid, gid, true); }, fchown(fd, uid, gid) { var stream = FS.getStreamChecked(fd); FS.doChown(stream, stream.node, false); }, doTruncate(stream, node, len) { if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31); } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28); } var errCode = FS.nodePermissions(node, "w"); if (errCode) { throw new FS.ErrnoError(errCode); } FS.doSetAttr(stream, node, { size: len, timestamp: Date.now() }); }, truncate(path, len) { if (len < 0) { throw new FS.ErrnoError(28); } var node; if (typeof path == "string") { var lookup = FS.lookupPath(path, { follow: true }); node = lookup.node; } else { node = path; } FS.doTruncate(null, node, len); }, ftruncate(fd, len) { var stream = FS.getStreamChecked(fd); if (len < 0 || (stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28); } FS.doTruncate(stream, stream.node, len); }, utime(path, atime, mtime) { var lookup = FS.lookupPath(path, { follow: true }); var node = lookup.node; var setattr = FS.checkOpExists(node.node_ops.setattr, 63); setattr(node, { atime, mtime }); }, open(path, flags, mode = 438) { if (path === "") { throw new FS.ErrnoError(44); } flags = typeof flags == "string" ? FS_modeStringToFlags(flags) : flags; if ((flags & 64)) { mode = (mode & 4095) | 32768; } else { mode = 0; } var node; var isDirPath; if (typeof path == "object") { node = path; } else { isDirPath = path.endsWith("/"); // 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. var lookup = FS.lookupPath(path, { follow: !(flags & 131072), noent_okay: true }); node = lookup.node; path = lookup.path; } // perhaps we need to create the node var created = false; if ((flags & 64)) { if (node) { // if O_CREAT and O_EXCL are set, error out if the node already exists if ((flags & 128)) { throw new FS.ErrnoError(20); } } else if (isDirPath) { throw new FS.ErrnoError(31); } else { // node doesn't exist, try to create it // Ignore the permission bits here to ensure we can `open` this new // file below. We use chmod below the apply the permissions once the // file is open. node = FS.mknod(path, mode | 511, 0); created = true; } } if (!node) { throw new FS.ErrnoError(44); } // can't truncate a device if (FS.isChrdev(node.mode)) { flags &= ~512; } // if asked only for a directory, then this must be one if ((flags & 65536) && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54); } // check permissions, if this is not a file we just created now (it is ok to // create and write to a file with read-only permissions; it is read-only // for later use) if (!created) { var errCode = FS.mayOpen(node, flags); if (errCode) { throw new FS.ErrnoError(errCode); } } // do truncation if necessary if ((flags & 512) && !created) { FS.truncate(node, 0); } // we've already handled these, don't pass down to the underlying vfs flags &= ~(128 | 512 | 131072); // register the stream with the filesystem var stream = FS.createStream({ node, path: FS.getPath(node), // we want the absolute path to the node flags, seekable: true, position: 0, stream_ops: node.stream_ops, // used by the file family libc calls (fopen, fwrite, ferror, etc.) ungotten: [], error: false }); // call the new stream's open function if (stream.stream_ops.open) { stream.stream_ops.open(stream); } if (created) { FS.chmod(node, mode & 511); } if (Module["logReadFiles"] && !(flags & 1)) { if (!(path in FS.readFiles)) { FS.readFiles[path] = 1; } } return stream; }, close(stream) { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if (stream.getdents) stream.getdents = null; // free readdir state try { if (stream.stream_ops.close) { stream.stream_ops.close(stream); } } catch (e) { throw e; } finally { FS.closeStream(stream.fd); } stream.fd = null; }, isClosed(stream) { return stream.fd === null; }, llseek(stream, offset, whence) { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70); } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28); } stream.position = stream.stream_ops.llseek(stream, offset, whence); stream.ungotten = []; return stream.position; }, read(stream, buffer, offset, length, position) { assert(offset >= 0); if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8); } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31); } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28); } var seeking = typeof position != "undefined"; if (!seeking) { position = stream.position; } else if (!stream.seekable) { throw new FS.ErrnoError(70); } var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); if (!seeking) stream.position += bytesRead; return bytesRead; }, write(stream, buffer, offset, length, position, canOwn) { assert(offset >= 0); if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8); } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31); } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28); } if (stream.seekable && stream.flags & 1024) { // seek to the end before writing in append mode FS.llseek(stream, 0, 2); } var seeking = typeof position != "undefined"; if (!seeking) { position = stream.position; } else if (!stream.seekable) { throw new FS.ErrnoError(70); } var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); if (!seeking) stream.position += bytesWritten; return bytesWritten; }, mmap(stream, length, position, prot, flags) { // User requests writing to file (prot & PROT_WRITE != 0). // Checking if we have permissions to write to the file unless // MAP_PRIVATE flag is set. According to POSIX spec it is possible // 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 && (stream.flags & 2097155) !== 2) { throw new FS.ErrnoError(2); } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2); } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43); } if (!length) { throw new FS.ErrnoError(28); } return stream.stream_ops.mmap(stream, length, position, prot, flags); }, msync(stream, buffer, offset, length, mmapFlags) { assert(offset >= 0); if (!stream.stream_ops.msync) { return 0; } return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); }, ioctl(stream, cmd, arg) { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59); } return stream.stream_ops.ioctl(stream, cmd, arg); }, readFile(path, opts = {}) { opts.flags = opts.flags || 0; opts.encoding = opts.encoding || "binary"; if (opts.encoding !== "utf8" && opts.encoding !== "binary") { throw new Error(`Invalid encoding type "${opts.encoding}"`); } var ret; var stream = FS.open(path, opts.flags); var stat = FS.stat(path); var length = stat.size; var buf = new Uint8Array(length); FS.read(stream, buf, 0, length, 0); if (opts.encoding === "utf8") { ret = UTF8ArrayToString(buf); } else if (opts.encoding === "binary") { ret = buf; } FS.close(stream); return ret; }, writeFile(path, data, opts = {}) { opts.flags = opts.flags || 577; var stream = FS.open(path, opts.flags, opts.mode); if (typeof data == "string") { var buf = new Uint8Array(lengthBytesUTF8(data) + 1); var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length); FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn); } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); } else { throw new Error("Unsupported data type"); } FS.close(stream); }, cwd: () => FS.currentPath, chdir(path) { var lookup = FS.lookupPath(path, { follow: true }); if (lookup.node === null) { throw new FS.ErrnoError(44); } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54); } var errCode = FS.nodePermissions(lookup.node, "x"); if (errCode) { throw new FS.ErrnoError(errCode); } FS.currentPath = lookup.path; }, createDefaultDirectories() { FS.mkdir("/tmp"); FS.mkdir("/home"); FS.mkdir("/home/web_user"); }, createDefaultDevices() { // create /dev FS.mkdir("/dev"); // setup /dev/null FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, llseek: () => 0 }); FS.mkdev("/dev/null", FS.makedev(1, 3)); // setup /dev/tty and /dev/tty1 // stderr needs to print output using err() rather than out() // so we register a second tty just for it. TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); FS.mkdev("/dev/tty", FS.makedev(5, 0)); FS.mkdev("/dev/tty1", FS.makedev(6, 0)); // setup /dev/[u]random // use a buffer to avoid overhead of individual crypto calls per byte var randomBuffer = new Uint8Array(1024), randomLeft = 0; var randomByte = () => { if (randomLeft === 0) { randomFill(randomBuffer); randomLeft = randomBuffer.byteLength; } return randomBuffer[--randomLeft]; }; FS.createDevice("/dev", "random", randomByte); FS.createDevice("/dev", "urandom", randomByte); // we're not going to emulate the actual shm device, // just create the tmp dirs that reside in it commonly FS.mkdir("/dev/shm"); FS.mkdir("/dev/shm/tmp"); }, createSpecialDirectories() { // create /proc/self/fd which allows /proc/self/fd/6 => readlink gives the // name of the stream for fd 6 (see test_unistd_ttyname) FS.mkdir("/proc"); var proc_self = FS.mkdir("/proc/self"); FS.mkdir("/proc/self/fd"); FS.mount({ mount() { var node = FS.createNode(proc_self, "fd", 16895, 73); node.stream_ops = { llseek: MEMFS.stream_ops.llseek }; node.node_ops = { lookup(parent, name) { var fd = +name; var stream = FS.getStreamChecked(fd); var ret = { parent: null, mount: { mountpoint: "fake" }, node_ops: { readlink: () => stream.path }, id: fd + 1 }; ret.parent = ret; // make it look like a simple root node return ret; }, readdir() { return Array.from(FS.streams.entries()).filter(([k, v]) => v).map(([k, v]) => k.toString()); } }; return node; } }, {}, "/proc/self/fd"); }, createStandardStreams(input, output, error) { // TODO deprecate the old functionality of a single // input / output callback and that utilizes FS.createDevice // and instead require a unique set of stream ops // by default, we symlink the standard streams to the // default tty devices. however, if the standard streams // have been overwritten we create a unique device for // them instead. if (input) { FS.createDevice("/dev", "stdin", input); } else { FS.symlink("/dev/tty", "/dev/stdin"); } if (output) { FS.createDevice("/dev", "stdout", null, output); } else { FS.symlink("/dev/tty", "/dev/stdout"); } if (error) { FS.createDevice("/dev", "stderr", null, error); } else { FS.symlink("/dev/tty1", "/dev/stderr"); } // open default streams for the stdin, stdout and stderr devices var stdin = FS.open("/dev/stdin", 0); var stdout = FS.open("/dev/stdout", 1); var stderr = FS.open("/dev/stderr", 1); assert(stdin.fd === 0, `invalid handle for stdin (${stdin.fd})`); assert(stdout.fd === 1, `invalid handle for stdout (${stdout.fd})`); assert(stderr.fd === 2, `invalid handle for stderr (${stderr.fd})`); }, staticInit() { FS.nameTable = new Array(4096); FS.mount(MEMFS, {}, "/"); FS.createDefaultDirectories(); FS.createDefaultDevices(); FS.createSpecialDirectories(); FS.filesystems = { "MEMFS": MEMFS }; }, init(input, output, error) { assert(!FS.initialized, "FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)"); FS.initialized = true; // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here input ??= Module["stdin"]; output ??= Module["stdout"]; error ??= Module["stderr"]; FS.createStandardStreams(input, output, error); }, quit() { FS.initialized = false; // force-flush all streams, so we get musl std streams printed out _fflush(0); // close all of our streams for (var stream of FS.streams) { if (stream) { FS.close(stream); } } }, findObject(path, dontResolveLastLink) { var ret = FS.analyzePath(path, dontResolveLastLink); if (!ret.exists) { return null; } return ret.object; }, analyzePath(path, dontResolveLastLink) { // operate from within the context of the symlink's target try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); path = lookup.path; } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null }; try { var lookup = FS.lookupPath(path, { parent: true }); ret.parentExists = true; ret.parentPath = lookup.path; ret.parentObject = lookup.node; ret.name = PATH.basename(path); lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); ret.exists = true; ret.path = lookup.path; ret.object = lookup.node; ret.name = lookup.node.name; ret.isRoot = lookup.path === "/"; } catch (e) { ret.error = e.errno; } return ret; }, createPath(parent, path, canRead, canWrite) { parent = typeof parent == "string" ? parent : FS.getPath(parent); var parts = path.split("/").reverse(); while (parts.length) { var part = parts.pop(); if (!part) continue; var current = PATH.join2(parent, part); try { FS.mkdir(current); } catch (e) { if (e.errno != 20) throw e; } parent = current; } return current; }, createFile(parent, name, properties, canRead, canWrite) { 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) { var path = name; if (parent) { parent = typeof parent == "string" ? parent : FS.getPath(parent); path = name ? PATH.join2(parent, name) : parent; } var mode = FS_getMode(canRead, canWrite); var node = FS.create(path, mode); if (data) { if (typeof data == "string") { var arr = new Array(data.length); for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); data = arr; } // make sure we can write to the file FS.chmod(node, mode | 146); var stream = FS.open(node, 577); FS.write(stream, data, 0, data.length, 0, canOwn); FS.close(stream); FS.chmod(node, mode); } }, createDevice(parent, name, input, output) { 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); // Create a fake device that a set of stream ops to emulate // the old behavior. FS.registerDevice(dev, { open(stream) { stream.seekable = false; }, close(stream) { // flush any pending line data if (output?.buffer?.length) { output(10); } }, read(stream, buffer, offset, length, pos) { var bytesRead = 0; for (var i = 0; i < length; i++) { var result; try { result = input(); } catch (e) { throw new FS.ErrnoError(29); } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; bytesRead++; buffer[offset + i] = result; } if (bytesRead) { stream.node.atime = Date.now(); } return bytesRead; }, write(stream, buffer, offset, length, pos) { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]); } catch (e) { throw new FS.ErrnoError(29); } } if (length) { stream.node.mtime = stream.node.ctime = Date.now(); } return i; } }); return FS.mkdev(path, mode, dev); }, forceLoadFile(obj) { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; if (typeof XMLHttpRequest != "undefined") { throw new Error("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); obj.usedBytes = obj.contents.length; } catch (e) { throw new FS.ErrnoError(29); } } }, createLazyFile(parent, name, url, canRead, canWrite) { // Lazy chunked Uint8Array (implements get and length from Uint8Array). // Actual getting is abstracted away for eventual reuse. class LazyUint8Array { lengthKnown=false; chunks=[]; // Loaded chunks. Index is the chunk number get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined; } var chunkOffset = idx % this.chunkSize; var chunkNum = (idx / this.chunkSize) | 0; return this.getter(chunkNum)[chunkOffset]; } setDataGetter(getter) { this.getter = getter; } cacheLength() { // Find length var xhr = new XMLHttpRequest; xhr.open("HEAD", url, false); xhr.send(null); if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("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 chunkSize = 1024 * 1024; // Chunk size in bytes if (!hasByteServing) chunkSize = datalength; // Function to get a range from the remote URL. var doXHR = (from, to) => { if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); if (to > datalength - 1) throw new Error("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); // Some hints to the browser that we want binary data. xhr.responseType = "arraybuffer"; if (xhr.overrideMimeType) { xhr.overrideMimeType("text/plain; charset=x-user-defined"); } xhr.send(null); if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); if (xhr.response !== undefined) { return new Uint8Array(/** @type{Array} */ (xhr.response || [])); } 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") { lazyArray.chunks[chunkNum] = doXHR(start, end); } if (typeof lazyArray.chunks[chunkNum] == "undefined") throw new Error("doXHR failed!"); return lazyArray.chunks[chunkNum]; }); if (usesGzip || !datalength) { // if the server uses gzip or doesn't supply the length, we have to download the whole file to get the (uncompressed) length 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"); } this._length = datalength; this._chunkSize = chunkSize; this.lengthKnown = true; } get length() { if (!this.lengthKnown) { this.cacheLength(); } return this._length; } get chunkSize() { if (!this.lengthKnown) { this.cacheLength(); } return this._chunkSize; } } if (typeof XMLHttpRequest != "undefined") { if (!ENVIRONMENT_IS_WORKER) throw "Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc"; var lazyArray = new LazyUint8Array; var properties = { isDevice: false, contents: lazyArray }; } else { var properties = { isDevice: false, url }; } var node = FS.createFile(parent, name, properties, canRead, canWrite); // This is a total hack, but I want to get this lazy file code out of the // core of MEMFS. If we want to keep this lazy file concept I feel it should // be its own thin LAZYFS proxying calls to MEMFS. if (properties.contents) { node.contents = properties.contents; } else if (properties.url) { node.contents = null; node.url = properties.url; } // Add a function that defers querying the file size until it is asked the first time. Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length; } } }); // override each stream op with one that tries to force load the lazy file first var stream_ops = {}; var keys = Object.keys(node.stream_ops); keys.forEach(key => { var fn = node.stream_ops[key]; stream_ops[key] = (...args) => { FS.forceLoadFile(node); return fn(...args); }; }); function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents; if (position >= contents.length) return 0; var size = Math.min(contents.length - position, length); assert(size >= 0); if (contents.slice) { // normal array for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i]; } } else { for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR buffer[offset + i] = contents.get(position + i); } } return size; } // use a custom read function stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node); return writeChunks(stream, buffer, offset, length, position); }; // use a custom mmap function stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node); var ptr = mmapAlloc(length); if (!ptr) { throw new FS.ErrnoError(48); } writeChunks(stream, GROWABLE_HEAP_I8(), ptr, length, position); return { ptr, allocated: true }; }; node.stream_ops = stream_ops; return node; }, absolutePath() { abort("FS.absolutePath has been removed; use PATH_FS.resolve instead"); }, createFolder() { abort("FS.createFolder has been removed; use FS.mkdir instead"); }, createLink() { abort("FS.createLink has been removed; use FS.symlink instead"); }, joinPath() { abort("FS.joinPath has been removed; use PATH.join instead"); }, mmapAlloc() { abort("FS.mmapAlloc has been replaced by the top level function mmapAlloc"); }, standardizePath() { abort("FS.standardizePath has been removed; use PATH.normalize instead"); } }; var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path; } // relative path var dir; if (dirfd === -100) { dir = FS.cwd(); } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd); dir = dirstream.path; } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44); } return dir; } return dir + "/" + path; }, writeStat(buf, stat) { GROWABLE_HEAP_I32()[((buf) >> 2)] = stat.dev; GROWABLE_HEAP_I32()[(((buf) + (4)) >> 2)] = stat.mode; GROWABLE_HEAP_U32()[(((buf) + (8)) >> 2)] = stat.nlink; GROWABLE_HEAP_I32()[(((buf) + (12)) >> 2)] = stat.uid; GROWABLE_HEAP_I32()[(((buf) + (16)) >> 2)] = stat.gid; GROWABLE_HEAP_I32()[(((buf) + (20)) >> 2)] = stat.rdev; HEAP64[(((buf) + (24)) >> 3)] = BigInt(stat.size); GROWABLE_HEAP_I32()[(((buf) + (32)) >> 2)] = 4096; GROWABLE_HEAP_I32()[(((buf) + (36)) >> 2)] = stat.blocks; var atime = stat.atime.getTime(); var mtime = stat.mtime.getTime(); var ctime = stat.ctime.getTime(); HEAP64[(((buf) + (40)) >> 3)] = BigInt(Math.floor(atime / 1e3)); GROWABLE_HEAP_U32()[(((buf) + (48)) >> 2)] = (atime % 1e3) * 1e3 * 1e3; HEAP64[(((buf) + (56)) >> 3)] = BigInt(Math.floor(mtime / 1e3)); GROWABLE_HEAP_U32()[(((buf) + (64)) >> 2)] = (mtime % 1e3) * 1e3 * 1e3; HEAP64[(((buf) + (72)) >> 3)] = BigInt(Math.floor(ctime / 1e3)); GROWABLE_HEAP_U32()[(((buf) + (80)) >> 2)] = (ctime % 1e3) * 1e3 * 1e3; HEAP64[(((buf) + (88)) >> 3)] = BigInt(stat.ino); return 0; }, writeStatFs(buf, stats) { GROWABLE_HEAP_I32()[(((buf) + (4)) >> 2)] = stats.bsize; GROWABLE_HEAP_I32()[(((buf) + (40)) >> 2)] = stats.bsize; GROWABLE_HEAP_I32()[(((buf) + (8)) >> 2)] = stats.blocks; GROWABLE_HEAP_I32()[(((buf) + (12)) >> 2)] = stats.bfree; GROWABLE_HEAP_I32()[(((buf) + (16)) >> 2)] = stats.bavail; GROWABLE_HEAP_I32()[(((buf) + (20)) >> 2)] = stats.files; GROWABLE_HEAP_I32()[(((buf) + (24)) >> 2)] = stats.ffree; GROWABLE_HEAP_I32()[(((buf) + (28)) >> 2)] = stats.fsid; GROWABLE_HEAP_I32()[(((buf) + (44)) >> 2)] = stats.flags; // ST_NOSUID GROWABLE_HEAP_I32()[(((buf) + (36)) >> 2)] = stats.namelen; }, doMsync(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43); } if (flags & 2) { // MAP_PRIVATE calls need not to be synced back to underlying fs return 0; } var buffer = GROWABLE_HEAP_U8().slice(addr, addr + len); FS.msync(stream, buffer, offset, len, flags); }, getStreamFromFD(fd) { var stream = FS.getStreamChecked(fd); return stream; }, varargs: undefined, getStr(ptr) { var ret = UTF8ToString(ptr); return ret; } }; function ___syscall_fcntl64(fd, cmd, varargs) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(3, 0, 1, fd, cmd, varargs); SYSCALLS.varargs = varargs; try { var stream = SYSCALLS.getStreamFromFD(fd); switch (cmd) { case 0: { var arg = syscallGetVarargI(); if (arg < 0) { return -28; } while (FS.streams[arg]) { arg++; } var newStream; newStream = FS.dupStream(stream, arg); return newStream.fd; } case 1: case 2: return 0; // FD_CLOEXEC makes no sense for a single process. case 3: return stream.flags; case 4: { var arg = syscallGetVarargI(); stream.flags |= arg; return 0; } case 12: { var arg = syscallGetVarargP(); var offset = 0; // We're always unlocked. GROWABLE_HEAP_I16()[(((arg) + (offset)) >> 1)] = 2; return 0; } case 13: case 14: // Pretend that the locking is successful. These are process-level locks, // and Emscripten programs are a single process. If we supported linking a // filesystem between programs, we'd need to do more here. // See https://github.com/emscripten-core/emscripten/issues/23697 return 0; } return -28; } catch (e) { if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; return -e.errno; } } function ___syscall_fstat64(fd, buf) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(4, 0, 1, fd, buf); try { return SYSCALLS.writeStat(buf, FS.fstat(fd)); } catch (e) { if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; return -e.errno; } } function ___syscall_openat(dirfd, path, flags, varargs) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(5, 0, 1, dirfd, path, flags, varargs); SYSCALLS.varargs = varargs; try { path = SYSCALLS.getStr(path); path = SYSCALLS.calculateAt(dirfd, path); var mode = varargs ? syscallGetVarargI() : 0; return FS.open(path, flags, mode).fd; } catch (e) { if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; return -e.errno; } } var __abort_js = () => abort("native code called abort()"); var embindRepr = v => { if (v === null) { return "null"; } var t = typeof v; if (t === "object" || t === "array" || t === "function") { return v.toString(); } else { return "" + v; } }; var embind_init_charCodes = () => { var codes = new Array(256); for (var i = 0; i < 256; ++i) { codes[i] = String.fromCharCode(i); } embind_charCodes = codes; }; var embind_charCodes; var readLatin1String = ptr => { var ret = ""; var c = ptr; while (GROWABLE_HEAP_U8()[c]) { ret += embind_charCodes[GROWABLE_HEAP_U8()[c++]]; } return ret; }; var awaitingDependencies = {}; var registeredTypes = {}; var typeDependencies = {}; var BindingError = Module["BindingError"] = class BindingError extends Error { constructor(message) { super(message); this.name = "BindingError"; } }; var throwBindingError = message => { throw new BindingError(message); }; /** @param {Object=} options */ function sharedRegisterType(rawType, registeredInstance, options = {}) { var name = registeredInstance.name; if (!rawType) { throwBindingError(`type "${name}" must have a positive integer typeid pointer`); } if (registeredTypes.hasOwnProperty(rawType)) { if (options.ignoreDuplicateRegistrations) { return; } else { throwBindingError(`Cannot register type '${name}' twice`); } } registeredTypes[rawType] = registeredInstance; delete typeDependencies[rawType]; if (awaitingDependencies.hasOwnProperty(rawType)) { var callbacks = awaitingDependencies[rawType]; delete awaitingDependencies[rawType]; callbacks.forEach(cb => cb()); } } /** @param {Object=} options */ function registerType(rawType, registeredInstance, options = {}) { if (registeredInstance.argPackAdvance === undefined) { throw new TypeError("registerType registeredInstance requires argPackAdvance"); } return sharedRegisterType(rawType, registeredInstance, options); } var integerReadValueFromPointer = (name, width, signed) => { // integers are quite common, so generate very specialized functions switch (width) { case 1: return signed ? pointer => GROWABLE_HEAP_I8()[pointer] : pointer => GROWABLE_HEAP_U8()[pointer]; case 2: return signed ? pointer => GROWABLE_HEAP_I16()[((pointer) >> 1)] : pointer => GROWABLE_HEAP_U16()[((pointer) >> 1)]; case 4: return signed ? pointer => GROWABLE_HEAP_I32()[((pointer) >> 2)] : pointer => GROWABLE_HEAP_U32()[((pointer) >> 2)]; case 8: return signed ? pointer => HEAP64[((pointer) >> 3)] : pointer => HEAPU64[((pointer) >> 3)]; default: throw new TypeError(`invalid integer width (${width}): ${name}`); } }; /** @suppress {globalThis} */ var __embind_register_bigint = (primitiveType, name, size, minRange, maxRange) => { name = readLatin1String(name); var isUnsignedType = (name.indexOf("u") != -1); // maxRange comes through as -1 for uint64_t (see issue 13902). Work around that temporarily if (isUnsignedType) { maxRange = (1n << 64n) - 1n; } registerType(primitiveType, { name, "fromWireType": value => value, "toWireType": function(destructors, value) { if (typeof value != "bigint" && typeof value != "number") { throw new TypeError(`Cannot convert "${embindRepr(value)}" to ${this.name}`); } if (typeof value == "number") { value = BigInt(value); } if (value < minRange || value > maxRange) { throw new TypeError(`Passing a number "${embindRepr(value)}" from JS side to C/C++ side to an argument of type "${name}", which is outside the valid range [${minRange}, ${maxRange}]!`); } return value; }, argPackAdvance: GenericWireTypeSize, "readValueFromPointer": integerReadValueFromPointer(name, size, !isUnsignedType), destructorFunction: null }); }; var GenericWireTypeSize = 8; /** @suppress {globalThis} */ var __embind_register_bool = (rawType, name, trueValue, falseValue) => { name = readLatin1String(name); registerType(rawType, { name, "fromWireType": function(wt) { // ambiguous emscripten ABI: sometimes return values are // true or false, and sometimes integers (0 or 1) return !!wt; }, "toWireType": function(destructors, o) { return o ? trueValue : falseValue; }, argPackAdvance: GenericWireTypeSize, "readValueFromPointer": function(pointer) { return this["fromWireType"](GROWABLE_HEAP_U8()[pointer]); }, destructorFunction: null }); }; var emval_freelist = []; var emval_handles = []; var __emval_decref = handle => { if (handle > 9 && 0 === --emval_handles[handle + 1]) { assert(emval_handles[handle] !== undefined, `Decref for unallocated handle.`); emval_handles[handle] = undefined; emval_freelist.push(handle); } }; var count_emval_handles = () => emval_handles.length / 2 - 5 - emval_freelist.length; var init_emval = () => { // reserve 0 and some special values. These never get de-allocated. emval_handles.push(0, 1, undefined, 1, null, 1, true, 1, false, 1); assert(emval_handles.length === 5 * 2); Module["count_emval_handles"] = count_emval_handles; }; var Emval = { toValue: handle => { if (!handle) { throwBindingError(`Cannot use deleted val. handle = ${handle}`); } // handle 2 is supposed to be `undefined`. assert(handle === 2 || emval_handles[handle] !== undefined && handle % 2 === 0, `invalid handle: ${handle}`); return emval_handles[handle]; }, toHandle: value => { switch (value) { case undefined: return 2; case null: return 4; case true: return 6; case false: return 8; default: { const handle = emval_freelist.pop() || emval_handles.length; emval_handles[handle] = value; emval_handles[handle + 1] = 1; return handle; } } } }; /** @suppress {globalThis} */ function readPointer(pointer) { return this["fromWireType"](GROWABLE_HEAP_U32()[((pointer) >> 2)]); } var EmValType = { name: "emscripten::val", "fromWireType": handle => { var rv = Emval.toValue(handle); __emval_decref(handle); return rv; }, "toWireType": (destructors, value) => Emval.toHandle(value), argPackAdvance: GenericWireTypeSize, "readValueFromPointer": readPointer, destructorFunction: null }; var __embind_register_emval = rawType => registerType(rawType, EmValType); var floatReadValueFromPointer = (name, width) => { switch (width) { case 4: return function(pointer) { return this["fromWireType"](GROWABLE_HEAP_F32()[((pointer) >> 2)]); }; case 8: return function(pointer) { return this["fromWireType"](GROWABLE_HEAP_F64()[((pointer) >> 3)]); }; default: throw new TypeError(`invalid float width (${width}): ${name}`); } }; var __embind_register_float = (rawType, name, size) => { name = readLatin1String(name); registerType(rawType, { name, "fromWireType": value => value, "toWireType": (destructors, value) => { if (typeof value != "number" && typeof value != "boolean") { throw new TypeError(`Cannot convert ${embindRepr(value)} to ${this.name}`); } // The VM will perform JS to Wasm value conversion, according to the spec: // https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue return value; }, argPackAdvance: GenericWireTypeSize, "readValueFromPointer": floatReadValueFromPointer(name, size), destructorFunction: null }); }; /** @suppress {globalThis} */ var __embind_register_integer = (primitiveType, name, size, minRange, maxRange) => { name = readLatin1String(name); // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come // out as 'i32 -1'. Always treat those as max u32. if (maxRange === -1) { maxRange = 4294967295; } var fromWireType = value => value; if (minRange === 0) { var bitshift = 32 - 8 * size; fromWireType = value => (value << bitshift) >>> bitshift; } var isUnsignedType = (name.includes("unsigned")); var checkAssertions = (value, toTypeName) => { if (typeof value != "number" && typeof value != "boolean") { throw new TypeError(`Cannot convert "${embindRepr(value)}" to ${toTypeName}`); } if (value < minRange || value > maxRange) { throw new TypeError(`Passing a number "${embindRepr(value)}" from JS side to C/C++ side to an argument of type "${name}", which is outside the valid range [${minRange}, ${maxRange}]!`); } }; var toWireType; if (isUnsignedType) { toWireType = function(destructors, value) { checkAssertions(value, this.name); return value >>> 0; }; } else { toWireType = function(destructors, value) { checkAssertions(value, this.name); // The VM will perform JS to Wasm value conversion, according to the spec: // https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue return value; }; } registerType(primitiveType, { name, "fromWireType": fromWireType, "toWireType": toWireType, argPackAdvance: GenericWireTypeSize, "readValueFromPointer": integerReadValueFromPointer(name, size, minRange !== 0), destructorFunction: null }); }; var __embind_register_memory_view = (rawType, dataTypeIndex, name) => { var typeMapping = [ Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, BigInt64Array, BigUint64Array ]; var TA = typeMapping[dataTypeIndex]; function decodeMemoryView(handle) { var size = GROWABLE_HEAP_U32()[((handle) >> 2)]; var data = GROWABLE_HEAP_U32()[(((handle) + (4)) >> 2)]; return new TA(GROWABLE_HEAP_I8().buffer, data, size); } name = readLatin1String(name); registerType(rawType, { name, "fromWireType": decodeMemoryView, argPackAdvance: GenericWireTypeSize, "readValueFromPointer": decodeMemoryView }, { ignoreDuplicateRegistrations: true }); }; var stringToUTF8 = (str, outPtr, maxBytesToWrite) => { assert(typeof maxBytesToWrite == "number", "stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!"); return stringToUTF8Array(str, GROWABLE_HEAP_U8(), outPtr, maxBytesToWrite); }; var __embind_register_std_string = (rawType, name) => { name = readLatin1String(name); var stdStringIsUTF8 = true; registerType(rawType, { name, // For some method names we use string keys here since they are part of // the public/external API and/or used by the runtime-generated code. "fromWireType"(value) { var length = GROWABLE_HEAP_U32()[((value) >> 2)]; var payload = value + 4; var str; if (stdStringIsUTF8) { var decodeStartPtr = payload; // Looping here to support possible embedded '0' bytes for (var i = 0; i <= length; ++i) { var currentBytePtr = payload + i; if (i == length || GROWABLE_HEAP_U8()[currentBytePtr] == 0) { var maxRead = currentBytePtr - decodeStartPtr; var stringSegment = UTF8ToString(decodeStartPtr, maxRead); if (str === undefined) { str = stringSegment; } else { str += String.fromCharCode(0); str += stringSegment; } decodeStartPtr = currentBytePtr + 1; } } } else { var a = new Array(length); for (var i = 0; i < length; ++i) { a[i] = String.fromCharCode(GROWABLE_HEAP_U8()[payload + i]); } str = a.join(""); } _free(value); return str; }, "toWireType"(destructors, value) { if (value instanceof ArrayBuffer) { value = new Uint8Array(value); } var length; var valueIsOfTypeString = (typeof value == "string"); // We accept `string` or array views with single byte elements if (!(valueIsOfTypeString || (ArrayBuffer.isView(value) && value.BYTES_PER_ELEMENT == 1))) { throwBindingError("Cannot pass non-string to std::string"); } if (stdStringIsUTF8 && valueIsOfTypeString) { length = lengthBytesUTF8(value); } else { length = value.length; } // assumes POINTER_SIZE alignment var base = _malloc(4 + length + 1); var ptr = base + 4; GROWABLE_HEAP_U32()[((base) >> 2)] = length; if (valueIsOfTypeString) { if (stdStringIsUTF8) { stringToUTF8(value, ptr, length + 1); } else { for (var i = 0; i < length; ++i) { var charCode = value.charCodeAt(i); if (charCode > 255) { _free(base); throwBindingError("String has UTF-16 code units that do not fit in 8 bits"); } GROWABLE_HEAP_U8()[ptr + i] = charCode; } } } else { GROWABLE_HEAP_U8().set(value, ptr); } if (destructors !== null) { destructors.push(_free, base); } return base; }, argPackAdvance: GenericWireTypeSize, "readValueFromPointer": readPointer, destructorFunction(ptr) { _free(ptr); } }); }; var UTF16Decoder = typeof TextDecoder != "undefined" ? new TextDecoder("utf-16le") : undefined; var UTF16ToString = (ptr, maxBytesToRead) => { assert(ptr % 2 == 0, "Pointer passed to UTF16ToString must be aligned to two bytes!"); var endPtr = ptr; // TextDecoder needs to know the byte length in advance, it doesn't stop on // null terminator by itself. // Also, use the length info to avoid running tiny strings through // TextDecoder, since .subarray() allocates garbage. var idx = endPtr >> 1; var maxIdx = idx + maxBytesToRead / 2; // If maxBytesToRead is not passed explicitly, it will be undefined, and this // will always evaluate to true. This saves on code size. while (!(idx >= maxIdx) && GROWABLE_HEAP_U16()[idx]) ++idx; endPtr = idx << 1; if (endPtr - ptr > 32 && UTF16Decoder) return UTF16Decoder.decode(GROWABLE_HEAP_U8().slice(ptr, endPtr)); // Fallback: decode without UTF16Decoder var str = ""; // If maxBytesToRead is not passed explicitly, it will be undefined, and the // for-loop's condition will always evaluate to true. The loop is then // terminated on the first null char. for (var i = 0; !(i >= maxBytesToRead / 2); ++i) { var codeUnit = GROWABLE_HEAP_I16()[(((ptr) + (i * 2)) >> 1)]; if (codeUnit == 0) break; // fromCharCode constructs a character from a UTF-16 code unit, so we can // pass the UTF16 string right through. str += String.fromCharCode(codeUnit); } return str; }; var stringToUTF16 = (str, outPtr, maxBytesToWrite) => { assert(outPtr % 2 == 0, "Pointer passed to stringToUTF16 must be aligned to two bytes!"); assert(typeof maxBytesToWrite == "number", "stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!"); // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. maxBytesToWrite ??= 2147483647; if (maxBytesToWrite < 2) return 0; maxBytesToWrite -= 2; // Null terminator. var startPtr = outPtr; var numCharsToWrite = (maxBytesToWrite < str.length * 2) ? (maxBytesToWrite / 2) : str.length; for (var i = 0; i < numCharsToWrite; ++i) { // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. var codeUnit = str.charCodeAt(i); // possibly a lead surrogate GROWABLE_HEAP_I16()[((outPtr) >> 1)] = codeUnit; outPtr += 2; } // Null-terminate the pointer to the HEAP. GROWABLE_HEAP_I16()[((outPtr) >> 1)] = 0; return outPtr - startPtr; }; var lengthBytesUTF16 = str => str.length * 2; var UTF32ToString = (ptr, maxBytesToRead) => { assert(ptr % 4 == 0, "Pointer passed to UTF32ToString must be aligned to four bytes!"); var i = 0; var str = ""; // If maxBytesToRead is not passed explicitly, it will be undefined, and this // will always evaluate to true. This saves on code size. while (!(i >= maxBytesToRead / 4)) { var utf32 = GROWABLE_HEAP_I32()[(((ptr) + (i * 4)) >> 2)]; if (utf32 == 0) break; ++i; // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing. // See http://unicode.org/faq/utf_bom.html#utf16-3 if (utf32 >= 65536) { var ch = utf32 - 65536; str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)); } else { str += String.fromCharCode(utf32); } } return str; }; var stringToUTF32 = (str, outPtr, maxBytesToWrite) => { assert(outPtr % 4 == 0, "Pointer passed to stringToUTF32 must be aligned to four bytes!"); assert(typeof maxBytesToWrite == "number", "stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!"); // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. maxBytesToWrite ??= 2147483647; if (maxBytesToWrite < 4) return 0; var startPtr = outPtr; var endPtr = startPtr + maxBytesToWrite - 4; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. // See http://unicode.org/faq/utf_bom.html#utf16-3 var codeUnit = str.charCodeAt(i); // possibly a lead surrogate if (codeUnit >= 55296 && codeUnit <= 57343) { var trailSurrogate = str.charCodeAt(++i); codeUnit = 65536 + ((codeUnit & 1023) << 10) | (trailSurrogate & 1023); } GROWABLE_HEAP_I32()[((outPtr) >> 2)] = codeUnit; outPtr += 4; if (outPtr + 4 > endPtr) break; } // Null-terminate the pointer to the HEAP. GROWABLE_HEAP_I32()[((outPtr) >> 2)] = 0; return outPtr - startPtr; }; var lengthBytesUTF32 = str => { var len = 0; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. // See http://unicode.org/faq/utf_bom.html#utf16-3 var codeUnit = str.charCodeAt(i); if (codeUnit >= 55296 && codeUnit <= 57343) ++i; // possibly a lead surrogate, so skip over the tail surrogate. len += 4; } return len; }; var __embind_register_std_wstring = (rawType, charSize, name) => { name = readLatin1String(name); var decodeString, encodeString, readCharAt, lengthBytesUTF; if (charSize === 2) { decodeString = UTF16ToString; encodeString = stringToUTF16; lengthBytesUTF = lengthBytesUTF16; readCharAt = pointer => GROWABLE_HEAP_U16()[((pointer) >> 1)]; } else if (charSize === 4) { decodeString = UTF32ToString; encodeString = stringToUTF32; lengthBytesUTF = lengthBytesUTF32; readCharAt = pointer => GROWABLE_HEAP_U32()[((pointer) >> 2)]; } registerType(rawType, { name, "fromWireType": value => { // Code mostly taken from _embind_register_std_string fromWireType var length = GROWABLE_HEAP_U32()[((value) >> 2)]; var str; var decodeStartPtr = value + 4; // Looping here to support possible embedded '0' bytes for (var i = 0; i <= length; ++i) { var currentBytePtr = value + 4 + i * charSize; if (i == length || readCharAt(currentBytePtr) == 0) { var maxReadBytes = currentBytePtr - decodeStartPtr; var stringSegment = decodeString(decodeStartPtr, maxReadBytes); if (str === undefined) { str = stringSegment; } else { str += String.fromCharCode(0); str += stringSegment; } decodeStartPtr = currentBytePtr + charSize; } } _free(value); return str; }, "toWireType": (destructors, value) => { if (!(typeof value == "string")) { throwBindingError(`Cannot pass non-string to C++ string type ${name}`); } // assumes POINTER_SIZE alignment var length = lengthBytesUTF(value); var ptr = _malloc(4 + length + charSize); GROWABLE_HEAP_U32()[((ptr) >> 2)] = length / charSize; encodeString(value, ptr + 4, length + charSize); if (destructors !== null) { destructors.push(_free, ptr); } return ptr; }, argPackAdvance: GenericWireTypeSize, "readValueFromPointer": readPointer, destructorFunction(ptr) { _free(ptr); } }); }; var __embind_register_void = (rawType, name) => { name = readLatin1String(name); registerType(rawType, { isVoid: true, // void return values can be optimized out sometimes name, argPackAdvance: 0, "fromWireType": () => undefined, // TODO: assert if anything else is given? "toWireType": (destructors, o) => undefined }); }; var __emscripten_init_main_thread_js = tb => { // Pass the thread address to the native code where they stored in wasm // globals which act as a form of TLS. Global constructors trying // to access this value will read the wrong value, but that is UB anyway. __emscripten_thread_init(tb, /*is_main=*/ !ENVIRONMENT_IS_WORKER, /*is_runtime=*/ 1, /*can_block=*/ !ENVIRONMENT_IS_WEB, /*default_stacksize=*/ 16777216, /*start_profiling=*/ false); PThread.threadInitTLS(); }; var handleException = e => { // Certain exception types we do not treat as errors since they are used for // internal control flow. // 1. ExitStatus, which is thrown by exit() // 2. "unwind", which is thrown by emscripten_unwind_to_js_event_loop() and others // that wish to return to JS event loop. if (e instanceof ExitStatus || e == "unwind") { return EXITSTATUS; } checkStackCookie(); if (e instanceof WebAssembly.RuntimeError) { if (_emscripten_stack_get_current() <= 0) { err("Stack overflow detected. You can try increasing -sSTACK_SIZE (currently set to 16777216)"); } } quit_(1, e); }; var maybeExit = () => { if (!keepRuntimeAlive()) { try { if (ENVIRONMENT_IS_PTHREAD) __emscripten_thread_exit(EXITSTATUS); else _exit(EXITSTATUS); } catch (e) { handleException(e); } } }; var callUserCallback = func => { if (ABORT) { err("user callback triggered after runtime exited or application aborted. Ignoring."); return; } try { func(); maybeExit(); } catch (e) { handleException(e); } }; var __emscripten_thread_mailbox_await = pthread_ptr => { if (typeof Atomics.waitAsync === "function") { // Wait on the pthread's initial self-pointer field because it is easy and // safe to access from sending threads that need to notify the waiting // thread. // TODO: How to make this work with wasm64? var wait = Atomics.waitAsync(GROWABLE_HEAP_I32(), ((pthread_ptr) >> 2), pthread_ptr); assert(wait.async); wait.value.then(checkMailbox); var waitingAsync = pthread_ptr + 128; Atomics.store(GROWABLE_HEAP_I32(), ((waitingAsync) >> 2), 1); } }; var checkMailbox = () => { // Only check the mailbox if we have a live pthread runtime. We implement // pthread_self to return 0 if there is no live runtime. var pthread_ptr = _pthread_self(); if (pthread_ptr) { // If we are using Atomics.waitAsync as our notification mechanism, wait // for a notification before processing the mailbox to avoid missing any // work that could otherwise arrive after we've finished processing the // mailbox and before we're ready for the next notification. __emscripten_thread_mailbox_await(pthread_ptr); callUserCallback(__emscripten_check_mailbox); } }; var __emscripten_notify_mailbox_postmessage = (targetThread, currThreadId) => { if (targetThread == currThreadId) { setTimeout(checkMailbox); } else if (ENVIRONMENT_IS_PTHREAD) { postMessage({ targetThread, cmd: "checkMailbox" }); } else { var worker = PThread.pthreads[targetThread]; if (!worker) { err(`Cannot send message to thread with ID ${targetThread}, unknown thread ID!`); return; } worker.postMessage({ cmd: "checkMailbox" }); } }; var proxiedJSCallArgs = []; var __emscripten_receive_on_main_thread_js = (funcIndex, emAsmAddr, callingThread, numCallArgs, args) => { // Sometimes we need to backproxy events to the calling thread (e.g. // HTML5 DOM events handlers such as // emscripten_set_mousemove_callback()), so keep track in a globally // accessible variable about the thread that initiated the proxying. numCallArgs /= 2; proxiedJSCallArgs.length = numCallArgs; var b = ((args) >> 3); for (var i = 0; i < numCallArgs; i++) { if (HEAP64[b + 2 * i]) { // It's a BigInt. proxiedJSCallArgs[i] = HEAP64[b + 2 * i + 1]; } else { // It's a Number. proxiedJSCallArgs[i] = GROWABLE_HEAP_F64()[b + 2 * i + 1]; } } // Proxied JS library funcs use funcIndex and EM_ASM functions use emAsmAddr var func = emAsmAddr ? ASM_CONSTS[emAsmAddr] : proxiedFunctionTable[funcIndex]; assert(!(funcIndex && emAsmAddr)); assert(func.length == numCallArgs, "Call args mismatch in _emscripten_receive_on_main_thread_js"); PThread.currentProxiedOperationCallerThread = callingThread; var rtn = func(...proxiedJSCallArgs); PThread.currentProxiedOperationCallerThread = 0; // Proxied functions can return any type except bigint. All other types // cooerce to f64/double (the return type of this function in C) but not // bigint. assert(typeof rtn != "bigint"); return rtn; }; var __emscripten_runtime_keepalive_clear = () => { noExitRuntime = false; runtimeKeepaliveCounter = 0; }; var __emscripten_thread_cleanup = thread => { // Called when a thread needs to be cleaned up so it can be reused. // A thread is considered reusable when it either returns from its // entry point, calls pthread_exit, or acts upon a cancellation. // Detached threads are responsible for calling this themselves, // otherwise pthread_join is responsible for calling this. if (!ENVIRONMENT_IS_PTHREAD) cleanupThread(thread); else postMessage({ cmd: "cleanupThread", thread }); }; var __emscripten_thread_set_strongref = thread => {}; var __emscripten_throw_longjmp = () => { throw Infinity; }; var INT53_MAX = 9007199254740992; var INT53_MIN = -9007199254740992; var bigintToI53Checked = num => (num < INT53_MIN || num > INT53_MAX) ? NaN : Number(num); function __gmtime_js(time, tmPtr) { time = bigintToI53Checked(time); var date = new Date(time * 1e3); GROWABLE_HEAP_I32()[((tmPtr) >> 2)] = date.getUTCSeconds(); GROWABLE_HEAP_I32()[(((tmPtr) + (4)) >> 2)] = date.getUTCMinutes(); GROWABLE_HEAP_I32()[(((tmPtr) + (8)) >> 2)] = date.getUTCHours(); GROWABLE_HEAP_I32()[(((tmPtr) + (12)) >> 2)] = date.getUTCDate(); GROWABLE_HEAP_I32()[(((tmPtr) + (16)) >> 2)] = date.getUTCMonth(); GROWABLE_HEAP_I32()[(((tmPtr) + (20)) >> 2)] = date.getUTCFullYear() - 1900; GROWABLE_HEAP_I32()[(((tmPtr) + (24)) >> 2)] = date.getUTCDay(); var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); var yday = ((date.getTime() - start) / (1e3 * 60 * 60 * 24)) | 0; GROWABLE_HEAP_I32()[(((tmPtr) + (28)) >> 2)] = yday; } var isLeapYear = year => year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); var MONTH_DAYS_LEAP_CUMULATIVE = [ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 ]; var MONTH_DAYS_REGULAR_CUMULATIVE = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ]; var ydayFromDate = date => { var leap = isLeapYear(date.getFullYear()); var monthDaysCumulative = (leap ? MONTH_DAYS_LEAP_CUMULATIVE : MONTH_DAYS_REGULAR_CUMULATIVE); var yday = monthDaysCumulative[date.getMonth()] + date.getDate() - 1; // -1 since it's days since Jan 1 return yday; }; function __localtime_js(time, tmPtr) { time = bigintToI53Checked(time); var date = new Date(time * 1e3); GROWABLE_HEAP_I32()[((tmPtr) >> 2)] = date.getSeconds(); GROWABLE_HEAP_I32()[(((tmPtr) + (4)) >> 2)] = date.getMinutes(); GROWABLE_HEAP_I32()[(((tmPtr) + (8)) >> 2)] = date.getHours(); GROWABLE_HEAP_I32()[(((tmPtr) + (12)) >> 2)] = date.getDate(); GROWABLE_HEAP_I32()[(((tmPtr) + (16)) >> 2)] = date.getMonth(); GROWABLE_HEAP_I32()[(((tmPtr) + (20)) >> 2)] = date.getFullYear() - 1900; GROWABLE_HEAP_I32()[(((tmPtr) + (24)) >> 2)] = date.getDay(); var yday = ydayFromDate(date) | 0; GROWABLE_HEAP_I32()[(((tmPtr) + (28)) >> 2)] = yday; GROWABLE_HEAP_I32()[(((tmPtr) + (36)) >> 2)] = -(date.getTimezoneOffset() * 60); // Attention: DST is in December in South, and some regions don't have DST at all. var start = new Date(date.getFullYear(), 0, 1); var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset(); var winterOffset = start.getTimezoneOffset(); var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset)) | 0; GROWABLE_HEAP_I32()[(((tmPtr) + (32)) >> 2)] = dst; } var __mktime_js = function(tmPtr) { var ret = (() => { var date = new Date(GROWABLE_HEAP_I32()[(((tmPtr) + (20)) >> 2)] + 1900, GROWABLE_HEAP_I32()[(((tmPtr) + (16)) >> 2)], GROWABLE_HEAP_I32()[(((tmPtr) + (12)) >> 2)], GROWABLE_HEAP_I32()[(((tmPtr) + (8)) >> 2)], GROWABLE_HEAP_I32()[(((tmPtr) + (4)) >> 2)], GROWABLE_HEAP_I32()[((tmPtr) >> 2)], 0); // There's an ambiguous hour when the time goes back; the tm_isdst field is // used to disambiguate it. Date() basically guesses, so we fix it up if it // guessed wrong, or fill in tm_isdst with the guess if it's -1. var dst = GROWABLE_HEAP_I32()[(((tmPtr) + (32)) >> 2)]; var guessedOffset = date.getTimezoneOffset(); var start = new Date(date.getFullYear(), 0, 1); var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset(); var winterOffset = start.getTimezoneOffset(); var dstOffset = Math.min(winterOffset, summerOffset); // DST is in December in South if (dst < 0) { // Attention: some regions don't have DST at all. GROWABLE_HEAP_I32()[(((tmPtr) + (32)) >> 2)] = Number(summerOffset != winterOffset && dstOffset == guessedOffset); } else if ((dst > 0) != (dstOffset == guessedOffset)) { var nonDstOffset = Math.max(winterOffset, summerOffset); var trueOffset = dst > 0 ? dstOffset : nonDstOffset; // Don't try setMinutes(date.getMinutes() + ...) -- it's messed up. date.setTime(date.getTime() + (trueOffset - guessedOffset) * 6e4); } GROWABLE_HEAP_I32()[(((tmPtr) + (24)) >> 2)] = date.getDay(); var yday = ydayFromDate(date) | 0; GROWABLE_HEAP_I32()[(((tmPtr) + (28)) >> 2)] = yday; // To match expected behavior, update fields from date GROWABLE_HEAP_I32()[((tmPtr) >> 2)] = date.getSeconds(); GROWABLE_HEAP_I32()[(((tmPtr) + (4)) >> 2)] = date.getMinutes(); GROWABLE_HEAP_I32()[(((tmPtr) + (8)) >> 2)] = date.getHours(); GROWABLE_HEAP_I32()[(((tmPtr) + (12)) >> 2)] = date.getDate(); GROWABLE_HEAP_I32()[(((tmPtr) + (16)) >> 2)] = date.getMonth(); GROWABLE_HEAP_I32()[(((tmPtr) + (20)) >> 2)] = date.getYear(); var timeMs = date.getTime(); if (isNaN(timeMs)) { return -1; } // Return time in microseconds return timeMs / 1e3; })(); return BigInt(ret); }; function __mmap_js(len, prot, flags, fd, offset, allocated, addr) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(6, 0, 1, len, prot, flags, fd, offset, allocated, addr); offset = bigintToI53Checked(offset); try { if (isNaN(offset)) return 61; var stream = SYSCALLS.getStreamFromFD(fd); var res = FS.mmap(stream, len, offset, prot, flags); var ptr = res.ptr; GROWABLE_HEAP_I32()[((allocated) >> 2)] = res.allocated; GROWABLE_HEAP_U32()[((addr) >> 2)] = ptr; return 0; } catch (e) { if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; return -e.errno; } } function __munmap_js(addr, len, prot, flags, fd, offset) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(7, 0, 1, addr, len, prot, flags, fd, offset); offset = bigintToI53Checked(offset); try { var stream = SYSCALLS.getStreamFromFD(fd); if (prot & 2) { SYSCALLS.doMsync(addr, stream, len, flags, offset); } } catch (e) { if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; return -e.errno; } } var timers = {}; var _emscripten_get_now = () => performance.timeOrigin + performance.now(); function __setitimer_js(which, timeout_ms) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(8, 0, 1, which, timeout_ms); // First, clear any existing timer. if (timers[which]) { clearTimeout(timers[which].id); delete timers[which]; } // A timeout of zero simply cancels the current timeout so we have nothing // more to do. if (!timeout_ms) return 0; var id = setTimeout(() => { assert(which in timers); delete timers[which]; callUserCallback(() => __emscripten_timeout(which, _emscripten_get_now())); }, timeout_ms); timers[which] = { id, timeout_ms }; return 0; } var __tzset_js = (timezone, daylight, std_name, dst_name) => { // TODO: Use (malleable) environment variables instead of system settings. var currentYear = (new Date).getFullYear(); var winter = new Date(currentYear, 0, 1); var summer = new Date(currentYear, 6, 1); var winterOffset = winter.getTimezoneOffset(); var summerOffset = summer.getTimezoneOffset(); // Local standard timezone offset. Local standard time is not adjusted for // daylight savings. This code uses the fact that getTimezoneOffset returns // a greater value during Standard Time versus Daylight Saving Time (DST). // Thus it determines the expected output during Standard Time, and it // compares whether the output of the given date the same (Standard) or less // (DST). var stdTimezoneOffset = Math.max(winterOffset, summerOffset); // timezone is specified as seconds west of UTC ("The external variable // `timezone` shall be set to the difference, in seconds, between // Coordinated Universal Time (UTC) and local standard time."), the same // as returned by stdTimezoneOffset. // See http://pubs.opengroup.org/onlinepubs/009695399/functions/tzset.html GROWABLE_HEAP_U32()[((timezone) >> 2)] = stdTimezoneOffset * 60; GROWABLE_HEAP_I32()[((daylight) >> 2)] = Number(winterOffset != summerOffset); 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 absOffset = Math.abs(timezoneOffset); var hours = String(Math.floor(absOffset / 60)).padStart(2, "0"); var minutes = String(absOffset % 60).padStart(2, "0"); return `UTC${sign}${hours}${minutes}`; }; var winterName = extractZone(winterOffset); var summerName = extractZone(summerOffset); assert(winterName); assert(summerName); assert(lengthBytesUTF8(winterName) <= 16, `timezone name truncated to fit in TZNAME_MAX (${winterName})`); assert(lengthBytesUTF8(summerName) <= 16, `timezone name truncated to fit in TZNAME_MAX (${summerName})`); if (summerOffset < winterOffset) { // Northern hemisphere stringToUTF8(winterName, std_name, 17); stringToUTF8(summerName, dst_name, 17); } else { stringToUTF8(winterName, dst_name, 17); stringToUTF8(summerName, std_name, 17); } }; var _emscripten_date_now = () => Date.now(); var nowIsMonotonic = 1; var checkWasiClock = clock_id => clock_id >= 0 && clock_id <= 3; function _clock_time_get(clk_id, ignored_precision, ptime) { ignored_precision = bigintToI53Checked(ignored_precision); if (!checkWasiClock(clk_id)) { return 28; } var now; // all wasi clocks but realtime are monotonic if (clk_id === 0) { now = _emscripten_date_now(); } else if (nowIsMonotonic) { now = _emscripten_get_now(); } else { return 52; } // "now" is in ms, and wasi times are in ns. var nsec = Math.round(now * 1e3 * 1e3); HEAP64[((ptime) >> 3)] = BigInt(nsec); return 0; } var readEmAsmArgsArray = []; var readEmAsmArgs = (sigPtr, buf) => { // Nobody should have mutated _readEmAsmArgsArray underneath us to be something else than an array. assert(Array.isArray(readEmAsmArgsArray)); // The input buffer is allocated on the stack, so it must be stack-aligned. assert(buf % 16 == 0); readEmAsmArgsArray.length = 0; var ch; // Most arguments are i32s, so shift the buffer pointer so it is a plain // index into HEAP32. while (ch = GROWABLE_HEAP_U8()[sigPtr++]) { var chr = String.fromCharCode(ch); var validChars = [ "d", "f", "i", "p" ]; // In WASM_BIGINT mode we support passing i64 values as bigint. validChars.push("j"); assert(validChars.includes(chr), `Invalid character ${ch}("${chr}") in readEmAsmArgs! Use only [${validChars}], and do not specify "v" for void return argument.`); // Floats are always passed as doubles, so all types except for 'i' // are 8 bytes and require alignment. var wide = (ch != 105); wide &= (ch != 112); buf += wide && (buf % 8) ? 4 : 0; readEmAsmArgsArray.push(// Special case for pointers under wasm64 or CAN_ADDRESS_2GB mode. ch == 112 ? GROWABLE_HEAP_U32()[((buf) >> 2)] : ch == 106 ? HEAP64[((buf) >> 3)] : ch == 105 ? GROWABLE_HEAP_I32()[((buf) >> 2)] : GROWABLE_HEAP_F64()[((buf) >> 3)]); buf += wide ? 8 : 4; } return readEmAsmArgsArray; }; var runEmAsmFunction = (code, sigPtr, argbuf) => { var args = readEmAsmArgs(sigPtr, argbuf); assert(ASM_CONSTS.hasOwnProperty(code), `No EM_ASM constant found at address ${code}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); return ASM_CONSTS[code](...args); }; var _emscripten_asm_const_int = (code, sigPtr, argbuf) => runEmAsmFunction(code, sigPtr, argbuf); var _emscripten_check_blocking_allowed = () => { if (ENVIRONMENT_IS_WORKER) return; // Blocking in a worker/pthread is fine. warnOnce("Blocking on the main thread is very dangerous, see https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread"); }; var _emscripten_exit_with_live_runtime = () => { runtimeKeepalivePush(); throw "unwind"; }; function _emscripten_fetch_free(id) { if (Fetch.xhrs.has(id)) { var xhr = Fetch.xhrs.get(id); Fetch.xhrs.free(id); // check if fetch is still in progress and should be aborted if (xhr.readyState > 0 && xhr.readyState < 4) { xhr.abort(); } } } var getHeapMax = () => // Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate // full 4GB Wasm memories, the size will wrap back to 0 bytes in Wasm side // for any code that deals with heap sizes, which would require special // casing all heap size related code to treat 0 specially. 2147483648; var _emscripten_get_heap_max = () => getHeapMax(); var _emscripten_num_logical_cores = () => navigator["hardwareConcurrency"]; var growMemory = size => { var b = wasmMemory.buffer; var pages = ((size - b.byteLength + 65535) / 65536) | 0; try { // round size grow request up to wasm page size (fixed 64KB per spec) wasmMemory.grow(pages); // .grow() takes a delta compared to the previous size updateMemoryViews(); return 1; } catch (e) { err(`growMemory: Attempted to grow heap from ${b.byteLength} bytes to ${size} bytes, but got error: ${e}`); } }; var _emscripten_resize_heap = requestedSize => { var oldSize = GROWABLE_HEAP_U8().length; // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. requestedSize >>>= 0; // With multithreaded builds, races can happen (another thread might increase the size // in between), so return a failure, and let the caller retry. if (requestedSize <= oldSize) { return false; } // Memory resize rules: // 1. Always increase heap size to at least the requested size, rounded up // to next page multiple. // 2a. If MEMORY_GROWTH_LINEAR_STEP == -1, excessively resize the heap // geometrically: increase the heap size according to // MEMORY_GROWTH_GEOMETRIC_STEP factor (default +20%), At most // overreserve by MEMORY_GROWTH_GEOMETRIC_CAP bytes (default 96MB). // 2b. If MEMORY_GROWTH_LINEAR_STEP != -1, excessively resize the heap // linearly: increase the heap size by at least // MEMORY_GROWTH_LINEAR_STEP bytes. // 3. Max size for the heap is capped at 2048MB-WASM_PAGE_SIZE, or by // MAXIMUM_MEMORY, or by ASAN limit, depending on which is smallest // 4. If we were unable to allocate as much memory, it may be due to // over-eager decision to excessively reserve due to (3) above. // Hence if an allocation fails, cut down on the amount of excess // growth, in an attempt to succeed to perform a smaller allocation. // A limit is set for how much we can grow. We should not exceed that // (the wasm binary specifies it, so if we tried, we'd fail anyhow). var maxHeapSize = getHeapMax(); if (requestedSize > maxHeapSize) { err(`Cannot enlarge memory, requested ${requestedSize} bytes, but the limit is ${maxHeapSize} bytes!`); return false; } // Loop through potential heap size increases. If we attempt a too eager // reservation that fails, cut down on the attempted size and reserve a // smaller bump instead. (max 3 times, chosen somewhat arbitrarily) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + .2 / cutDown); // ensure geometric growth // but limit overreserving (default to capping at +96MB overgrowth at most) overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296); var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)); var replacement = growMemory(newSize); if (replacement) { return true; } } err(`Failed to grow the heap from ${oldSize} bytes to ${newSize} bytes, not enough memory!`); return false; }; var _emscripten_runtime_keepalive_check = keepRuntimeAlive; var stringToNewUTF8 = str => { var size = lengthBytesUTF8(str) + 1; var ret = _malloc(size); if (ret) stringToUTF8(str, ret, size); return ret; }; var setOffscreenCanvasSizeOnTargetThread = (targetThread, targetCanvas, width, height) => { targetCanvas = targetCanvas ? UTF8ToString(targetCanvas) : ""; var targetCanvasPtr = 0; if (targetCanvas) { targetCanvasPtr = stringToNewUTF8(targetCanvas); } __emscripten_set_offscreencanvas_size_on_thread(targetThread, targetCanvasPtr, width, height); }; var GLctx; var webgl_enable_ANGLE_instanced_arrays = ctx => { // Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2. var ext = ctx.getExtension("ANGLE_instanced_arrays"); // Because this extension is a core function in WebGL 2, assign the extension entry points in place of // where the core functions will reside in WebGL 2. This way the calling code can call these without // having to dynamically branch depending if running against WebGL 1 or WebGL 2. if (ext) { ctx["vertexAttribDivisor"] = (index, divisor) => ext["vertexAttribDivisorANGLE"](index, divisor); ctx["drawArraysInstanced"] = (mode, first, count, primcount) => ext["drawArraysInstancedANGLE"](mode, first, count, primcount); ctx["drawElementsInstanced"] = (mode, count, type, indices, primcount) => ext["drawElementsInstancedANGLE"](mode, count, type, indices, primcount); return 1; } }; var webgl_enable_OES_vertex_array_object = ctx => { // Extension available in WebGL 1 from Firefox 25 and WebKit 536.28/desktop Safari 6.0.3 onwards. Core feature in WebGL 2. var ext = ctx.getExtension("OES_vertex_array_object"); if (ext) { ctx["createVertexArray"] = () => ext["createVertexArrayOES"](); ctx["deleteVertexArray"] = vao => ext["deleteVertexArrayOES"](vao); ctx["bindVertexArray"] = vao => ext["bindVertexArrayOES"](vao); ctx["isVertexArray"] = vao => ext["isVertexArrayOES"](vao); return 1; } }; var webgl_enable_WEBGL_draw_buffers = ctx => { // Extension available in WebGL 1 from Firefox 28 onwards. Core feature in WebGL 2. var ext = ctx.getExtension("WEBGL_draw_buffers"); if (ext) { ctx["drawBuffers"] = (n, bufs) => ext["drawBuffersWEBGL"](n, bufs); return 1; } }; var webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance = ctx => // Closure is expected to be allowed to minify the '.dibvbi' property, so not accessing it quoted. !!(ctx.dibvbi = ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance")); var webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance = ctx => !!(ctx.mdibvbi = ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance")); var webgl_enable_EXT_polygon_offset_clamp = ctx => !!(ctx.extPolygonOffsetClamp = ctx.getExtension("EXT_polygon_offset_clamp")); var webgl_enable_EXT_clip_control = ctx => !!(ctx.extClipControl = ctx.getExtension("EXT_clip_control")); var webgl_enable_WEBGL_polygon_mode = ctx => !!(ctx.webglPolygonMode = ctx.getExtension("WEBGL_polygon_mode")); var webgl_enable_WEBGL_multi_draw = ctx => // Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted. !!(ctx.multiDrawWebgl = ctx.getExtension("WEBGL_multi_draw")); var getEmscriptenSupportedExtensions = ctx => { // Restrict the list of advertised extensions to those that we actually // support. var supportedExtensions = [ // WebGL 1 extensions "ANGLE_instanced_arrays", "EXT_blend_minmax", "EXT_disjoint_timer_query", "EXT_frag_depth", "EXT_shader_texture_lod", "EXT_sRGB", "OES_element_index_uint", "OES_fbo_render_mipmap", "OES_standard_derivatives", "OES_texture_float", "OES_texture_half_float", "OES_texture_half_float_linear", "OES_vertex_array_object", "WEBGL_color_buffer_float", "WEBGL_depth_texture", "WEBGL_draw_buffers", // WebGL 2 extensions "EXT_color_buffer_float", "EXT_conservative_depth", "EXT_disjoint_timer_query_webgl2", "EXT_texture_norm16", "NV_shader_noperspective_interpolation", "WEBGL_clip_cull_distance", // WebGL 1 and WebGL 2 extensions "EXT_clip_control", "EXT_color_buffer_half_float", "EXT_depth_clamp", "EXT_float_blend", "EXT_polygon_offset_clamp", "EXT_texture_compression_bptc", "EXT_texture_compression_rgtc", "EXT_texture_filter_anisotropic", "KHR_parallel_shader_compile", "OES_texture_float_linear", "WEBGL_blend_func_extended", "WEBGL_compressed_texture_astc", "WEBGL_compressed_texture_etc", "WEBGL_compressed_texture_etc1", "WEBGL_compressed_texture_s3tc", "WEBGL_compressed_texture_s3tc_srgb", "WEBGL_debug_renderer_info", "WEBGL_debug_shaders", "WEBGL_lose_context", "WEBGL_multi_draw", "WEBGL_polygon_mode" ]; // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. return (ctx.getSupportedExtensions() || []).filter(ext => supportedExtensions.includes(ext)); }; var GL = { counter: 1, buffers: [], programs: [], framebuffers: [], renderbuffers: [], textures: [], shaders: [], vaos: [], contexts: {}, offscreenCanvases: {}, queries: [], samplers: [], transformFeedbacks: [], syncs: [], stringCache: {}, stringiCache: {}, unpackAlignment: 4, unpackRowLength: 0, recordError: errorCode => { if (!GL.lastError) { GL.lastError = errorCode; } }, getNewId: table => { var ret = GL.counter++; for (var i = table.length; i < ret; i++) { table[i] = null; } return ret; }, genObject: (n, buffers, createFunction, objectTable) => { for (var i = 0; i < n; i++) { var buffer = GLctx[createFunction](); var id = buffer && GL.getNewId(objectTable); if (buffer) { buffer.name = id; objectTable[id] = buffer; } else { GL.recordError(1282); } GROWABLE_HEAP_I32()[(((buffers) + (i * 4)) >> 2)] = id; } }, getSource: (shader, count, string, length) => { var source = ""; for (var i = 0; i < count; ++i) { var len = length ? GROWABLE_HEAP_U32()[(((length) + (i * 4)) >> 2)] : undefined; source += UTF8ToString(GROWABLE_HEAP_U32()[(((string) + (i * 4)) >> 2)], len); } return source; }, createContext: (/** @type {HTMLCanvasElement} */ canvas, webGLContextAttributes) => { // BUG: Workaround Safari WebGL issue: After successfully acquiring WebGL // context on a canvas, calling .getContext() will always return that // context independent of which 'webgl' or 'webgl2' // context version was passed. See: // https://bugs.webkit.org/show_bug.cgi?id=222758 // and: // https://github.com/emscripten-core/emscripten/issues/13295. // TODO: Once the bug is fixed and shipped in Safari, adjust the Safari // version field in above check. if (!canvas.getContextSafariWebGL2Fixed) { canvas.getContextSafariWebGL2Fixed = canvas.getContext; /** @type {function(this:HTMLCanvasElement, string, (Object|null)=): (Object|null)} */ function fixedGetContext(ver, attrs) { var gl = canvas.getContextSafariWebGL2Fixed(ver, attrs); return ((ver == "webgl") == (gl instanceof WebGLRenderingContext)) ? gl : null; } canvas.getContext = fixedGetContext; } var ctx = (webGLContextAttributes.majorVersion > 1) ? canvas.getContext("webgl2", webGLContextAttributes) : canvas.getContext("webgl", webGLContextAttributes); if (!ctx) return 0; var handle = GL.registerContext(ctx, webGLContextAttributes); return handle; }, registerContext: (ctx, webGLContextAttributes) => { // with pthreads a context is a location in memory with some synchronized // data between threads var handle = _malloc(8); GROWABLE_HEAP_U32()[(((handle) + (4)) >> 2)] = _pthread_self(); // the thread pointer of the thread that owns the control of the context var context = { handle, attributes: webGLContextAttributes, version: webGLContextAttributes.majorVersion, GLctx: ctx }; // Store the created context object so that we can access the context // given a canvas without having to pass the parameters again. if (ctx.canvas) ctx.canvas.GLctxObject = context; GL.contexts[handle] = context; if (typeof webGLContextAttributes.enableExtensionsByDefault == "undefined" || webGLContextAttributes.enableExtensionsByDefault) { GL.initExtensions(context); } return handle; }, makeContextCurrent: contextHandle => { // Active Emscripten GL layer context object. GL.currentContext = GL.contexts[contextHandle]; // Active WebGL context object. Module["ctx"] = GLctx = GL.currentContext?.GLctx; return !(contextHandle && !GLctx); }, getContext: contextHandle => GL.contexts[contextHandle], deleteContext: contextHandle => { if (GL.currentContext === GL.contexts[contextHandle]) { GL.currentContext = null; } if (typeof JSEvents == "object") { // Release all JS event handlers on the DOM element that the GL context is // associated with since the context is now deleted. JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); } // Make sure the canvas object no longer refers to the context object so // there are no GC surprises. if (GL.contexts[contextHandle]?.GLctx.canvas) { GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; } _free(GL.contexts[contextHandle].handle); GL.contexts[contextHandle] = null; }, initExtensions: context => { // If this function is called without a specific context object, init the // extensions of the currently active context. context ||= GL.currentContext; if (context.initExtensionsDone) return; context.initExtensionsDone = true; var GLctx = context.GLctx; // Detect the presence of a few extensions manually, ction GL interop // layer itself will need to know if they exist. // Extensions that are available in both WebGL 1 and WebGL 2 webgl_enable_WEBGL_multi_draw(GLctx); webgl_enable_EXT_polygon_offset_clamp(GLctx); webgl_enable_EXT_clip_control(GLctx); webgl_enable_WEBGL_polygon_mode(GLctx); // Extensions that are only available in WebGL 1 (the calls will be no-ops // if called on a WebGL 2 context active) webgl_enable_ANGLE_instanced_arrays(GLctx); webgl_enable_OES_vertex_array_object(GLctx); webgl_enable_WEBGL_draw_buffers(GLctx); // Extensions that are available from WebGL >= 2 (no-op if called on a WebGL 1 context active) webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx); // On WebGL 2, EXT_disjoint_timer_query is replaced with an alternative // that's based on core APIs, and exposes only the queryCounterEXT() // entrypoint. if (context.version >= 2) { GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query_webgl2"); } // However, Firefox exposes the WebGL 1 version on WebGL 2 as well and // thus we look for the WebGL 1 version again if the WebGL 2 version // isn't present. https://bugzilla.mozilla.org/show_bug.cgi?id=1328882 if (context.version < 2 || !GLctx.disjointTimerQueryExt) { GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); } getEmscriptenSupportedExtensions(GLctx).forEach(ext => { // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders // are not enabled by default. if (!ext.includes("lose_context") && !ext.includes("debug")) { // Call .getExtension() to enable that extension permanently. GLctx.getExtension(ext); } }); } }; var maybeCStringToJsString = cString => cString > 2 ? UTF8ToString(cString) : cString; var findCanvasEventTarget = target => { target = maybeCStringToJsString(target); // When compiling with OffscreenCanvas support and looking up a canvas to target, // we first look up if the target Canvas has been transferred to OffscreenCanvas use. // These transfers are represented/tracked by GL.offscreenCanvases object, which contain // the OffscreenCanvas element for each regular Canvas element that has been transferred. // Note that each pthread/worker have their own set of GL.offscreenCanvases. That is, // when an OffscreenCanvas is transferred from a pthread/main thread to another pthread, // it will move in the GL.offscreenCanvases array between threads. Hence GL.offscreenCanvases // represents the set of OffscreenCanvases owned by the current calling thread. // First check out the list of OffscreenCanvases by CSS selector ID ('#myCanvasID') return GL.offscreenCanvases[target.slice(1)] || (target == "canvas" && Object.keys(GL.offscreenCanvases)[0]) || (typeof document != "undefined" && document.querySelector(target)); }; var setCanvasElementSizeCallingThread = (target, width, height) => { var canvas = findCanvasEventTarget(target); if (!canvas) return -4; if (canvas.canvasSharedPtr) { // N.B. We hold the canvasSharedPtr info structure as the authoritative source for specifying the size of a canvas // since the actual canvas size changes are asynchronous if the canvas is owned by an OffscreenCanvas on another thread. // Therefore when setting the size, eagerly set the size of the canvas on the calling thread here, though this thread // might not be the one that actually ends up specifying the size, but the actual size change may be dispatched // as an asynchronous event below. GROWABLE_HEAP_I32()[((canvas.canvasSharedPtr) >> 2)] = width; GROWABLE_HEAP_I32()[(((canvas.canvasSharedPtr) + (4)) >> 2)] = height; } if (canvas.offscreenCanvas || !canvas.controlTransferredOffscreen) { if (canvas.offscreenCanvas) canvas = canvas.offscreenCanvas; var autoResizeViewport = false; if (canvas.GLctxObject?.GLctx) { var prevViewport = canvas.GLctxObject.GLctx.getParameter(2978); // TODO: Perhaps autoResizeViewport should only be true if FBO 0 is currently active? autoResizeViewport = (prevViewport[0] === 0 && prevViewport[1] === 0 && prevViewport[2] === canvas.width && prevViewport[3] === canvas.height); } canvas.width = width; canvas.height = height; if (autoResizeViewport) { // TODO: Add -sCANVAS_RESIZE_SETS_GL_VIEWPORT=0/1 option (default=1). This is commonly done and several graphics engines depend on this, // but this can be quite disruptive. canvas.GLctxObject.GLctx.viewport(0, 0, width, height); } } else if (canvas.canvasSharedPtr) { var targetThread = GROWABLE_HEAP_U32()[(((canvas.canvasSharedPtr) + (8)) >> 2)]; setOffscreenCanvasSizeOnTargetThread(targetThread, target, width, height); return 1; } else { return -4; } return 0; }; function setCanvasElementSizeMainThread(target, width, height) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(9, 0, 1, target, width, height); return setCanvasElementSizeCallingThread(target, width, height); } var _emscripten_set_canvas_element_size = (target, width, height) => { var canvas = findCanvasEventTarget(target); if (canvas) { return setCanvasElementSizeCallingThread(target, width, height); } return setCanvasElementSizeMainThread(target, width, height); }; class HandleAllocator { allocated=[ undefined ]; freelist=[]; get(id) { assert(this.allocated[id] !== undefined, `invalid handle: ${id}`); return this.allocated[id]; } has(id) { return this.allocated[id] !== undefined; } allocate(handle) { var id = this.freelist.pop() || this.allocated.length; this.allocated[id] = handle; return id; } free(id) { assert(this.allocated[id] !== undefined); // Set the slot to `undefined` rather than using `delete` here since // apparently arrays with holes in them can be less efficient. this.allocated[id] = undefined; this.freelist.push(id); } } var Fetch = { openDatabase(dbname, dbversion, onsuccess, onerror) { try { var openRequest = indexedDB.open(dbname, dbversion); } catch (e) { return onerror(e); } openRequest.onupgradeneeded = event => { var db = /** @type {IDBDatabase} */ (event.target.result); if (db.objectStoreNames.contains("FILES")) { db.deleteObjectStore("FILES"); } db.createObjectStore("FILES"); }; openRequest.onsuccess = event => onsuccess(event.target.result); openRequest.onerror = onerror; }, init() { Fetch.xhrs = new HandleAllocator; if (ENVIRONMENT_IS_PTHREAD) return; var onsuccess = db => { Fetch.dbInstance = db; removeRunDependency("library_fetch_init"); }; var onerror = () => { Fetch.dbInstance = false; removeRunDependency("library_fetch_init"); }; addRunDependency("library_fetch_init"); Fetch.openDatabase("emscripten_filesystem", 1, onsuccess, onerror); } }; function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) { var url = GROWABLE_HEAP_U32()[(((fetch) + (8)) >> 2)]; if (!url) { onerror(fetch, 0, "no url specified!"); return; } var url_ = UTF8ToString(url); var fetch_attr = fetch + 108; var requestMethod = UTF8ToString(fetch_attr + 0); requestMethod ||= "GET"; var timeoutMsecs = GROWABLE_HEAP_U32()[(((fetch_attr) + (56)) >> 2)]; var userName = GROWABLE_HEAP_U32()[(((fetch_attr) + (68)) >> 2)]; var password = GROWABLE_HEAP_U32()[(((fetch_attr) + (72)) >> 2)]; var requestHeaders = GROWABLE_HEAP_U32()[(((fetch_attr) + (76)) >> 2)]; var overriddenMimeType = GROWABLE_HEAP_U32()[(((fetch_attr) + (80)) >> 2)]; var dataPtr = GROWABLE_HEAP_U32()[(((fetch_attr) + (84)) >> 2)]; var dataLength = GROWABLE_HEAP_U32()[(((fetch_attr) + (88)) >> 2)]; var fetchAttributes = GROWABLE_HEAP_U32()[(((fetch_attr) + (52)) >> 2)]; var fetchAttrLoadToMemory = !!(fetchAttributes & 1); var fetchAttrStreamData = !!(fetchAttributes & 2); var fetchAttrSynchronous = !!(fetchAttributes & 64); var userNameStr = userName ? UTF8ToString(userName) : undefined; var passwordStr = password ? UTF8ToString(password) : undefined; var xhr = new XMLHttpRequest; xhr.withCredentials = !!GROWABLE_HEAP_U8()[(fetch_attr) + (60)]; xhr.open(requestMethod, url_, !fetchAttrSynchronous, userNameStr, passwordStr); if (!fetchAttrSynchronous) xhr.timeout = timeoutMsecs; // XHR timeout field is only accessible in async XHRs, and must be set after .open() but before .send(). xhr.url_ = url_; // Save the url for debugging purposes (and for comparing to the responseURL that server side advertised) assert(!fetchAttrStreamData, "streaming uses moz-chunked-arraybuffer which is no longer supported; TODO: rewrite using fetch()"); xhr.responseType = "arraybuffer"; if (overriddenMimeType) { var overriddenMimeTypeStr = UTF8ToString(overriddenMimeType); xhr.overrideMimeType(overriddenMimeTypeStr); } if (requestHeaders) { for (;;) { var key = GROWABLE_HEAP_U32()[((requestHeaders) >> 2)]; if (!key) break; var value = GROWABLE_HEAP_U32()[(((requestHeaders) + (4)) >> 2)]; if (!value) break; requestHeaders += 8; var keyStr = UTF8ToString(key); var valueStr = UTF8ToString(value); xhr.setRequestHeader(keyStr, valueStr); } } var id = Fetch.xhrs.allocate(xhr); GROWABLE_HEAP_U32()[((fetch) >> 2)] = id; var data = (dataPtr && dataLength) ? GROWABLE_HEAP_U8().slice(dataPtr, dataPtr + dataLength) : null; // TODO: Support specifying custom headers to the request. // Share the code to save the response, as we need to do so both on success // and on error (despite an error, there may be a response, like a 404 page). // This receives a condition, which determines whether to save the xhr's // response, or just 0. function saveResponseAndStatus() { var ptr = 0; var ptrLen = 0; if (xhr.response && fetchAttrLoadToMemory && GROWABLE_HEAP_U32()[(((fetch) + (12)) >> 2)] === 0) { ptrLen = xhr.response.byteLength; } if (ptrLen > 0) { // The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is // freed when emscripten_fetch_close() is called. ptr = _malloc(ptrLen); GROWABLE_HEAP_U8().set(new Uint8Array(/** @type{Array} */ (xhr.response)), ptr); } GROWABLE_HEAP_U32()[(((fetch) + (12)) >> 2)] = ptr; writeI53ToI64(fetch + 16, ptrLen); writeI53ToI64(fetch + 24, 0); var len = xhr.response ? xhr.response.byteLength : 0; if (len) { // If the final XHR.onload handler receives the bytedata to compute total length, report that, // otherwise don't write anything out here, which will retain the latest byte size reported in // the most recent XHR.onprogress handler. writeI53ToI64(fetch + 32, len); } GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = xhr.readyState; GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = xhr.status; if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + 44, 64); } xhr.onload = e => { // check if xhr was aborted by user and don't try to call back if (!Fetch.xhrs.has(id)) { return; } saveResponseAndStatus(); if (xhr.status >= 200 && xhr.status < 300) { onsuccess?.(fetch, xhr, e); } else { onerror?.(fetch, xhr, e); } }; xhr.onerror = e => { // check if xhr was aborted by user and don't try to call back if (!Fetch.xhrs.has(id)) { return; } saveResponseAndStatus(); onerror?.(fetch, xhr, e); }; xhr.ontimeout = e => { // check if xhr was aborted by user and don't try to call back if (!Fetch.xhrs.has(id)) { return; } onerror?.(fetch, xhr, e); }; xhr.onprogress = e => { // check if xhr was aborted by user and don't try to call back if (!Fetch.xhrs.has(id)) { return; } var ptrLen = (fetchAttrLoadToMemory && fetchAttrStreamData && xhr.response) ? xhr.response.byteLength : 0; var ptr = 0; if (ptrLen > 0 && fetchAttrLoadToMemory && fetchAttrStreamData) { assert(onprogress, "When doing a streaming fetch, you should have an onprogress handler registered to receive the chunks!"); // Allocate byte data in Emscripten heap for the streamed memory block (freed immediately after onprogress call) ptr = _malloc(ptrLen); GROWABLE_HEAP_U8().set(new Uint8Array(/** @type{Array} */ (xhr.response)), ptr); } GROWABLE_HEAP_U32()[(((fetch) + (12)) >> 2)] = ptr; writeI53ToI64(fetch + 16, ptrLen); writeI53ToI64(fetch + 24, e.loaded - ptrLen); writeI53ToI64(fetch + 32, e.total); GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = xhr.readyState; // If loading files from a source that does not give HTTP status code, assume success if we get data bytes if (xhr.readyState >= 3 && xhr.status === 0 && e.loaded > 0) xhr.status = 200; GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = xhr.status; if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + 44, 64); onprogress?.(fetch, xhr, e); if (ptr) { _free(ptr); } }; xhr.onreadystatechange = e => { // check if xhr was aborted by user and don't try to call back if (!Fetch.xhrs.has(id)) { runtimeKeepalivePop(); return; } GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = xhr.readyState; if (xhr.readyState >= 2) { GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = xhr.status; } onreadystatechange?.(fetch, xhr, e); }; try { xhr.send(data); } catch (e) { onerror?.(fetch, xhr, e); } } var readI53FromI64 = ptr => GROWABLE_HEAP_U32()[((ptr) >> 2)] + GROWABLE_HEAP_I32()[(((ptr) + (4)) >> 2)] * 4294967296; var readI53FromU64 = ptr => GROWABLE_HEAP_U32()[((ptr) >> 2)] + GROWABLE_HEAP_U32()[(((ptr) + (4)) >> 2)] * 4294967296; var writeI53ToI64 = (ptr, num) => { GROWABLE_HEAP_U32()[((ptr) >> 2)] = num; var lower = GROWABLE_HEAP_U32()[((ptr) >> 2)]; GROWABLE_HEAP_U32()[(((ptr) + (4)) >> 2)] = (num - lower) / 4294967296; var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr); var offset = ((ptr) >> 2); if (deserialized != num) warnOnce(`writeI53ToI64() out of range: serialized JS Number ${num} to Wasm heap as bytes lo=${ptrToString(GROWABLE_HEAP_U32()[offset])}, hi=${ptrToString(GROWABLE_HEAP_U32()[offset + 1])}, which deserializes back to ${deserialized} instead!`); }; function fetchCacheData(/** @type {IDBDatabase} */ db, fetch, data, onsuccess, onerror) { if (!db) { onerror(fetch, 0, "IndexedDB not available!"); return; } var fetch_attr = fetch + 108; var destinationPath = GROWABLE_HEAP_U32()[(((fetch_attr) + (64)) >> 2)]; destinationPath ||= GROWABLE_HEAP_U32()[(((fetch) + (8)) >> 2)]; var destinationPathStr = UTF8ToString(destinationPath); try { var transaction = db.transaction([ "FILES" ], "readwrite"); var packages = transaction.objectStore("FILES"); var putRequest = packages.put(data, destinationPathStr); putRequest.onsuccess = event => { GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = 200; // Mimic XHR HTTP status code 200 "OK" stringToUTF8("OK", fetch + 44, 64); onsuccess(fetch, 0, destinationPathStr); }; putRequest.onerror = error => { // Most likely we got an error if IndexedDB is unwilling to store any more data for this page. // TODO: Can we identify and break down different IndexedDB-provided errors and convert those // to more HTTP status codes for more information? GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = 413; // Mimic XHR HTTP status code 413 "Payload Too Large" stringToUTF8("Payload Too Large", fetch + 44, 64); onerror(fetch, 0, error); }; } catch (e) { onerror(fetch, 0, e); } } function fetchLoadCachedData(db, fetch, onsuccess, onerror) { if (!db) { onerror(fetch, 0, "IndexedDB not available!"); return; } var fetch_attr = fetch + 108; var path = GROWABLE_HEAP_U32()[(((fetch_attr) + (64)) >> 2)]; path ||= GROWABLE_HEAP_U32()[(((fetch) + (8)) >> 2)]; var pathStr = UTF8ToString(path); try { var transaction = db.transaction([ "FILES" ], "readonly"); var packages = transaction.objectStore("FILES"); var getRequest = packages.get(pathStr); getRequest.onsuccess = event => { if (event.target.result) { var value = event.target.result; var len = value.byteLength || value.length; // The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is // freed when emscripten_fetch_close() is called. var ptr = _malloc(len); GROWABLE_HEAP_U8().set(new Uint8Array(value), ptr); GROWABLE_HEAP_U32()[(((fetch) + (12)) >> 2)] = ptr; writeI53ToI64(fetch + 16, len); writeI53ToI64(fetch + 24, 0); writeI53ToI64(fetch + 32, len); GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = 200; // Mimic XHR HTTP status code 200 "OK" stringToUTF8("OK", fetch + 44, 64); onsuccess(fetch, 0, value); } else { // Succeeded to load, but the load came back with the value of undefined, treat that as an error since we never store undefined in db. GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = 404; // Mimic XHR HTTP status code 404 "Not Found" stringToUTF8("Not Found", fetch + 44, 64); onerror(fetch, 0, "no data"); } }; getRequest.onerror = error => { GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = 404; // Mimic XHR HTTP status code 404 "Not Found" stringToUTF8("Not Found", fetch + 44, 64); onerror(fetch, 0, error); }; } catch (e) { onerror(fetch, 0, e); } } function fetchDeleteCachedData(db, fetch, onsuccess, onerror) { if (!db) { onerror(fetch, 0, "IndexedDB not available!"); return; } var fetch_attr = fetch + 108; var path = GROWABLE_HEAP_U32()[(((fetch_attr) + (64)) >> 2)]; path ||= GROWABLE_HEAP_U32()[(((fetch) + (8)) >> 2)]; var pathStr = UTF8ToString(path); try { var transaction = db.transaction([ "FILES" ], "readwrite"); var packages = transaction.objectStore("FILES"); var request = packages.delete(pathStr); request.onsuccess = event => { var value = event.target.result; GROWABLE_HEAP_U32()[(((fetch) + (12)) >> 2)] = 0; writeI53ToI64(fetch + 16, 0); writeI53ToI64(fetch + 24, 0); writeI53ToI64(fetch + 32, 0); // Mimic XHR readyState 4 === 'DONE: The operation is complete' GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = 4; // Mimic XHR HTTP status code 200 "OK" GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = 200; stringToUTF8("OK", fetch + 44, 64); onsuccess(fetch, 0, value); }; request.onerror = error => { GROWABLE_HEAP_I16()[(((fetch) + (40)) >> 1)] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' GROWABLE_HEAP_I16()[(((fetch) + (42)) >> 1)] = 404; // Mimic XHR HTTP status code 404 "Not Found" stringToUTF8("Not Found", fetch + 44, 64); onerror(fetch, 0, error); }; } catch (e) { onerror(fetch, 0, e); } } function _emscripten_start_fetch(fetch, successcb, errorcb, progresscb, readystatechangecb) { // Avoid shutting down the runtime since we want to wait for the async // response. runtimeKeepalivePush(); var fetch_attr = fetch + 108; var onsuccess = GROWABLE_HEAP_U32()[(((fetch_attr) + (36)) >> 2)]; var onerror = GROWABLE_HEAP_U32()[(((fetch_attr) + (40)) >> 2)]; var onprogress = GROWABLE_HEAP_U32()[(((fetch_attr) + (44)) >> 2)]; var onreadystatechange = GROWABLE_HEAP_U32()[(((fetch_attr) + (48)) >> 2)]; var fetchAttributes = GROWABLE_HEAP_U32()[(((fetch_attr) + (52)) >> 2)]; var fetchAttrSynchronous = !!(fetchAttributes & 64); function doCallback(f) { if (fetchAttrSynchronous) { f(); } else { callUserCallback(f); } } var reportSuccess = (fetch, xhr, e) => { runtimeKeepalivePop(); doCallback(() => { if (onsuccess) (a1 => dynCall_vi(onsuccess, a1))(fetch); else successcb?.(fetch); }); }; var reportProgress = (fetch, xhr, e) => { doCallback(() => { if (onprogress) (a1 => dynCall_vi(onprogress, a1))(fetch); else progresscb?.(fetch); }); }; var reportError = (fetch, xhr, e) => { runtimeKeepalivePop(); doCallback(() => { if (onerror) (a1 => dynCall_vi(onerror, a1))(fetch); else errorcb?.(fetch); }); }; var reportReadyStateChange = (fetch, xhr, e) => { doCallback(() => { if (onreadystatechange) (a1 => dynCall_vi(onreadystatechange, a1))(fetch); else readystatechangecb?.(fetch); }); }; var performUncachedXhr = (fetch, xhr, e) => { fetchXHR(fetch, reportSuccess, reportError, reportProgress, reportReadyStateChange); }; var cacheResultAndReportSuccess = (fetch, xhr, e) => { var storeSuccess = (fetch, xhr, e) => { runtimeKeepalivePop(); doCallback(() => { if (onsuccess) (a1 => dynCall_vi(onsuccess, a1))(fetch); else successcb?.(fetch); }); }; var storeError = (fetch, xhr, e) => { runtimeKeepalivePop(); doCallback(() => { if (onsuccess) (a1 => dynCall_vi(onsuccess, a1))(fetch); else successcb?.(fetch); }); }; fetchCacheData(Fetch.dbInstance, fetch, xhr.response, storeSuccess, storeError); }; var performCachedXhr = (fetch, xhr, e) => { fetchXHR(fetch, cacheResultAndReportSuccess, reportError, reportProgress, reportReadyStateChange); }; var requestMethod = UTF8ToString(fetch_attr + 0); var fetchAttrReplace = !!(fetchAttributes & 16); var fetchAttrPersistFile = !!(fetchAttributes & 4); var fetchAttrNoDownload = !!(fetchAttributes & 32); if (requestMethod === "EM_IDB_STORE") { // TODO(?): Here we perform a clone of the data, because storing shared typed arrays to IndexedDB does not seem to be allowed. var ptr = GROWABLE_HEAP_U32()[(((fetch_attr) + (84)) >> 2)]; var size = GROWABLE_HEAP_U32()[(((fetch_attr) + (88)) >> 2)]; fetchCacheData(Fetch.dbInstance, fetch, GROWABLE_HEAP_U8().slice(ptr, ptr + size), reportSuccess, reportError); } else if (requestMethod === "EM_IDB_DELETE") { fetchDeleteCachedData(Fetch.dbInstance, fetch, reportSuccess, reportError); } else if (!fetchAttrReplace) { fetchLoadCachedData(Fetch.dbInstance, fetch, reportSuccess, fetchAttrNoDownload ? reportError : (fetchAttrPersistFile ? performCachedXhr : performUncachedXhr)); } else if (!fetchAttrNoDownload) { fetchXHR(fetch, fetchAttrPersistFile ? cacheResultAndReportSuccess : reportSuccess, reportError, reportProgress, reportReadyStateChange); } else { return 0; } return fetch; } var registerPreMainLoop = f => { // Does nothing unless $MainLoop is included/used. typeof MainLoop != "undefined" && MainLoop.preMainLoop.push(f); }; var _emscripten_supports_offscreencanvas = () => // TODO: Add a new build mode, e.g. OFFSCREENCANVAS_SUPPORT=2, which // necessitates OffscreenCanvas support at build time, and "return 1;" here in that build mode. typeof OffscreenCanvas != "undefined"; var webglPowerPreferences = [ "default", "low-power", "high-performance" ]; /** @suppress {duplicate } */ var _emscripten_webgl_do_create_context = (target, attributes) => { assert(attributes); var attr32 = ((attributes) >> 2); var powerPreference = GROWABLE_HEAP_I32()[attr32 + (8 >> 2)]; var contextAttributes = { "alpha": !!GROWABLE_HEAP_I8()[attributes + 0], "depth": !!GROWABLE_HEAP_I8()[attributes + 1], "stencil": !!GROWABLE_HEAP_I8()[attributes + 2], "antialias": !!GROWABLE_HEAP_I8()[attributes + 3], "premultipliedAlpha": !!GROWABLE_HEAP_I8()[attributes + 4], "preserveDrawingBuffer": !!GROWABLE_HEAP_I8()[attributes + 5], "powerPreference": webglPowerPreferences[powerPreference], "failIfMajorPerformanceCaveat": !!GROWABLE_HEAP_I8()[attributes + 12], // The following are not predefined WebGL context attributes in the WebGL specification, so the property names can be minified by Closure. majorVersion: GROWABLE_HEAP_I32()[attr32 + (16 >> 2)], minorVersion: GROWABLE_HEAP_I32()[attr32 + (20 >> 2)], enableExtensionsByDefault: GROWABLE_HEAP_I8()[attributes + 24], explicitSwapControl: GROWABLE_HEAP_I8()[attributes + 25], proxyContextToMainThread: GROWABLE_HEAP_I32()[attr32 + (28 >> 2)], renderViaOffscreenBackBuffer: GROWABLE_HEAP_I8()[attributes + 32] }; // TODO: Make these into hard errors at some point in the future if (contextAttributes.majorVersion !== 1 && contextAttributes.majorVersion !== 2) { err(`Invalid WebGL version requested: ${contextAttributes.majorVersion}`); } var canvas = findCanvasEventTarget(target); // If our canvas from findCanvasEventTarget is actually an offscreen canvas record, we should extract the inner canvas. if (canvas?.canvas) { canvas = canvas.canvas; } if (!canvas) { return 0; } if (canvas.offscreenCanvas) canvas = canvas.offscreenCanvas; if (contextAttributes.explicitSwapControl) { var supportsOffscreenCanvas = canvas.transferControlToOffscreen || (_emscripten_supports_offscreencanvas() && canvas instanceof OffscreenCanvas); if (!supportsOffscreenCanvas) { return 0; } if (canvas.transferControlToOffscreen) { if (!canvas.controlTransferredOffscreen) { GL.offscreenCanvases[canvas.id] = { canvas: canvas.transferControlToOffscreen(), canvasSharedPtr: _malloc(12), id: canvas.id }; canvas.controlTransferredOffscreen = true; } else if (!GL.offscreenCanvases[canvas.id]) { return 0; } canvas = GL.offscreenCanvases[canvas.id].canvas; } } var contextHandle = GL.createContext(canvas, contextAttributes); return contextHandle; }; var _emscripten_webgl_create_context = _emscripten_webgl_do_create_context; var _emscripten_webgl_destroy_context_calling_thread = contextHandle => { if (GL.currentContext == contextHandle) GL.currentContext = 0; GL.deleteContext(contextHandle); }; var _emscripten_webgl_destroy_context_main_thread = _emscripten_webgl_destroy_context_calling_thread; function _emscripten_webgl_destroy_context(p0) { return GL.contexts[p0] ? _emscripten_webgl_destroy_context_calling_thread(p0) : _emscripten_webgl_destroy_context_main_thread(p0); } var _emscripten_webgl_make_context_current = contextHandle => { var success = GL.makeContextCurrent(contextHandle); return success ? 0 : -5; }; var ENV = {}; var getExecutableName = () => thisProgram || "./this.program"; var getEnvStrings = () => { if (!getEnvStrings.strings) { // Default values. // Browser language detection #8751 var lang = ((typeof navigator == "object" && navigator.languages && navigator.languages[0]) || "C").replace("-", "_") + ".UTF-8"; var env = { "USER": "web_user", "LOGNAME": "web_user", "PATH": "/", "PWD": "/", "HOME": "/home/web_user", "LANG": lang, "_": getExecutableName() }; // Apply the user-provided values, if any. for (var x in ENV) { // x is a key in ENV; if ENV[x] is undefined, that means it was // explicitly set to be so. We allow user code to do that to // force variables with default values to remain unset. if (ENV[x] === undefined) delete env[x]; else env[x] = ENV[x]; } var strings = []; for (var x in env) { strings.push(`${x}=${env[x]}`); } getEnvStrings.strings = strings; } return getEnvStrings.strings; }; var stringToAscii = (str, buffer) => { for (var i = 0; i < str.length; ++i) { assert(str.charCodeAt(i) === (str.charCodeAt(i) & 255)); GROWABLE_HEAP_I8()[buffer++] = str.charCodeAt(i); } // Null-terminate the string GROWABLE_HEAP_I8()[buffer] = 0; }; var _environ_get = function(__environ, environ_buf) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(10, 0, 1, __environ, environ_buf); var bufSize = 0; getEnvStrings().forEach((string, i) => { var ptr = environ_buf + bufSize; GROWABLE_HEAP_U32()[(((__environ) + (i * 4)) >> 2)] = ptr; stringToAscii(string, ptr); bufSize += string.length + 1; }); return 0; }; var _environ_sizes_get = function(penviron_count, penviron_buf_size) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(11, 0, 1, penviron_count, penviron_buf_size); var strings = getEnvStrings(); GROWABLE_HEAP_U32()[((penviron_count) >> 2)] = strings.length; var bufSize = 0; strings.forEach(string => bufSize += string.length + 1); GROWABLE_HEAP_U32()[((penviron_buf_size) >> 2)] = bufSize; return 0; }; function _fd_close(fd) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(12, 0, 1, fd); try { var stream = SYSCALLS.getStreamFromFD(fd); FS.close(stream); return 0; } catch (e) { if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; return e.errno; } } function _fd_fdstat_get(fd, pbuf) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(13, 0, 1, fd, pbuf); try { var rightsBase = 0; var rightsInheriting = 0; var flags = 0; { var stream = SYSCALLS.getStreamFromFD(fd); // All character devices are terminals (other things a Linux system would // assume is a character device, like the mouse, we have special APIs for). var type = stream.tty ? 2 : FS.isDir(stream.mode) ? 3 : FS.isLink(stream.mode) ? 7 : 4; } GROWABLE_HEAP_I8()[pbuf] = type; GROWABLE_HEAP_I16()[(((pbuf) + (2)) >> 1)] = flags; HEAP64[(((pbuf) + (8)) >> 3)] = BigInt(rightsBase); HEAP64[(((pbuf) + (16)) >> 3)] = BigInt(rightsInheriting); return 0; } catch (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; for (var i = 0; i < iovcnt; i++) { var ptr = GROWABLE_HEAP_U32()[((iov) >> 2)]; var len = GROWABLE_HEAP_U32()[(((iov) + (4)) >> 2)]; iov += 8; var curr = FS.read(stream, GROWABLE_HEAP_I8(), ptr, len, offset); if (curr < 0) return -1; ret += curr; if (curr < len) break; // nothing more to read if (typeof offset != "undefined") { offset += curr; } } return ret; }; function _fd_read(fd, iov, iovcnt, pnum) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(14, 0, 1, fd, iov, iovcnt, pnum); try { var stream = SYSCALLS.getStreamFromFD(fd); var num = doReadv(stream, iov, iovcnt); GROWABLE_HEAP_U32()[((pnum) >> 2)] = num; return 0; } catch (e) { if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; return e.errno; } } function _fd_seek(fd, offset, whence, newOffset) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(15, 0, 1, fd, offset, whence, newOffset); offset = bigintToI53Checked(offset); try { if (isNaN(offset)) return 61; 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 return 0; } catch (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++) { var ptr = GROWABLE_HEAP_U32()[((iov) >> 2)]; var len = GROWABLE_HEAP_U32()[(((iov) + (4)) >> 2)]; iov += 8; var curr = FS.write(stream, GROWABLE_HEAP_I8(), 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; } } return ret; }; function _fd_write(fd, iov, iovcnt, pnum) { if (ENVIRONMENT_IS_PTHREAD) return proxyToMainThread(16, 0, 1, fd, iov, iovcnt, pnum); try { var stream = SYSCALLS.getStreamFromFD(fd); var num = doWritev(stream, iov, iovcnt); GROWABLE_HEAP_U32()[((pnum) >> 2)] = num; return 0; } catch (e) { if (typeof FS == "undefined" || !(e.name === "ErrnoError")) throw e; return e.errno; } } var _glActiveTexture = x0 => GLctx.activeTexture(x0); var _glAttachShader = (program, shader) => { GLctx.attachShader(GL.programs[program], GL.shaders[shader]); }; var _glBindBuffer = (target, buffer) => { if (target == 35051) { // In WebGL 2 glReadPixels entry point, we need to use a different WebGL 2 // API function call when a buffer is bound to // GL_PIXEL_PACK_BUFFER_BINDING point, so must keep track whether that // binding point is non-null to know what is the proper API function to // call. GLctx.currentPixelPackBufferBinding = buffer; } else if (target == 35052) { // In WebGL 2 gl(Compressed)Tex(Sub)Image[23]D entry points, we need to // use a different WebGL 2 API function call when a buffer is bound to // GL_PIXEL_UNPACK_BUFFER_BINDING point, so must keep track whether that // binding point is non-null to know what is the proper API function to // call. GLctx.currentPixelUnpackBufferBinding = buffer; } GLctx.bindBuffer(target, GL.buffers[buffer]); }; var _glBindFramebuffer = (target, framebuffer) => { GLctx.bindFramebuffer(target, GL.framebuffers[framebuffer]); }; var _glBindTexture = (target, texture) => { GLctx.bindTexture(target, GL.textures[texture]); }; var _glBlendFunc = (x0, x1) => GLctx.blendFunc(x0, x1); var _glBufferData = (target, size, data, usage) => { if (GL.currentContext.version >= 2) { // If size is zero, WebGL would interpret uploading the whole input // arraybuffer (starting from given offset), which would not make sense in // WebAssembly, so avoid uploading if size is zero. However we must still // call bufferData to establish a backing storage of zero bytes. if (data && size) { GLctx.bufferData(target, GROWABLE_HEAP_U8(), usage, data, size); } else { GLctx.bufferData(target, size, usage); } return; } // N.b. here first form specifies a heap subarray, second form an integer // size, so the ?: code here is polymorphic. It is advised to avoid // randomly mixing both uses in calling code, to avoid any potential JS // engine JIT issues. GLctx.bufferData(target, data ? GROWABLE_HEAP_U8().subarray(data, data + size) : size, usage); }; var _glClear = x0 => GLctx.clear(x0); var _glClearColor = (x0, x1, x2, x3) => GLctx.clearColor(x0, x1, x2, x3); var _glCompileShader = shader => { GLctx.compileShader(GL.shaders[shader]); }; var _glCreateProgram = () => { var id = GL.getNewId(GL.programs); var program = GLctx.createProgram(); // Store additional information needed for each shader program: program.name = id; // Lazy cache results of // glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH/GL_ACTIVE_ATTRIBUTE_MAX_LENGTH/GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH) program.maxUniformLength = program.maxAttributeLength = program.maxUniformBlockNameLength = 0; program.uniformIdCounter = 1; GL.programs[id] = program; return id; }; var _glCreateShader = shaderType => { var id = GL.getNewId(GL.shaders); GL.shaders[id] = GLctx.createShader(shaderType); return id; }; var _glDeleteFramebuffers = (n, framebuffers) => { for (var i = 0; i < n; ++i) { var id = GROWABLE_HEAP_I32()[(((framebuffers) + (i * 4)) >> 2)]; var framebuffer = GL.framebuffers[id]; if (!framebuffer) continue; // GL spec: "glDeleteFramebuffers silently ignores 0s and names that do not correspond to existing framebuffer objects". GLctx.deleteFramebuffer(framebuffer); framebuffer.name = 0; GL.framebuffers[id] = null; } }; var _glDeleteProgram = id => { if (!id) return; var program = GL.programs[id]; if (!program) { // glDeleteProgram actually signals an error when deleting a nonexisting // object, unlike some other GL delete functions. GL.recordError(1281); return; } GLctx.deleteProgram(program); program.name = 0; GL.programs[id] = null; }; var _glDeleteTextures = (n, textures) => { for (var i = 0; i < n; i++) { var id = GROWABLE_HEAP_I32()[(((textures) + (i * 4)) >> 2)]; var texture = GL.textures[id]; // GL spec: "glDeleteTextures silently ignores 0s and names that do not // correspond to existing textures". if (!texture) continue; GLctx.deleteTexture(texture); texture.name = 0; GL.textures[id] = null; } }; var _glDepthFunc = x0 => GLctx.depthFunc(x0); var _glDisable = x0 => GLctx.disable(x0); var _glDrawArrays = (mode, first, count) => { GLctx.drawArrays(mode, first, count); }; var _glEnable = x0 => GLctx.enable(x0); var _glEnableVertexAttribArray = index => { GLctx.enableVertexAttribArray(index); }; var _glFramebufferTexture2D = (target, attachment, textarget, texture, level) => { GLctx.framebufferTexture2D(target, attachment, textarget, GL.textures[texture], level); }; var _glGenBuffers = (n, buffers) => { GL.genObject(n, buffers, "createBuffer", GL.buffers); }; var _glGenFramebuffers = (n, ids) => { GL.genObject(n, ids, "createFramebuffer", GL.framebuffers); }; var _glGenTextures = (n, textures) => { GL.genObject(n, textures, "createTexture", GL.textures); }; var _glGetAttribLocation = (program, name) => GLctx.getAttribLocation(GL.programs[program], UTF8ToString(name)); var _glGetError = () => { var error = GLctx.getError() || GL.lastError; GL.lastError = 0; return error; }; var _glGetProgramiv = (program, pname, p) => { if (!p) { // GLES2 specification does not specify how to behave if p is a null // pointer. Since calling this function does not make sense if p == null, // issue a GL error to notify user about it. GL.recordError(1281); return; } if (program >= GL.counter) { GL.recordError(1281); return; } program = GL.programs[program]; if (pname == 35716) { // GL_INFO_LOG_LENGTH var log = GLctx.getProgramInfoLog(program); if (log === null) log = "(unknown error)"; GROWABLE_HEAP_I32()[((p) >> 2)] = log.length + 1; } else if (pname == 35719) { if (!program.maxUniformLength) { var numActiveUniforms = GLctx.getProgramParameter(program, 35718); for (var i = 0; i < numActiveUniforms; ++i) { program.maxUniformLength = Math.max(program.maxUniformLength, GLctx.getActiveUniform(program, i).name.length + 1); } } GROWABLE_HEAP_I32()[((p) >> 2)] = program.maxUniformLength; } else if (pname == 35722) { if (!program.maxAttributeLength) { var numActiveAttributes = GLctx.getProgramParameter(program, 35721); for (var i = 0; i < numActiveAttributes; ++i) { program.maxAttributeLength = Math.max(program.maxAttributeLength, GLctx.getActiveAttrib(program, i).name.length + 1); } } GROWABLE_HEAP_I32()[((p) >> 2)] = program.maxAttributeLength; } else if (pname == 35381) { if (!program.maxUniformBlockNameLength) { var numActiveUniformBlocks = GLctx.getProgramParameter(program, 35382); for (var i = 0; i < numActiveUniformBlocks; ++i) { program.maxUniformBlockNameLength = Math.max(program.maxUniformBlockNameLength, GLctx.getActiveUniformBlockName(program, i).length + 1); } } GROWABLE_HEAP_I32()[((p) >> 2)] = program.maxUniformBlockNameLength; } else { GROWABLE_HEAP_I32()[((p) >> 2)] = GLctx.getProgramParameter(program, pname); } }; var _glGetShaderInfoLog = (shader, maxLength, length, infoLog) => { var log = GLctx.getShaderInfoLog(GL.shaders[shader]); if (log === null) log = "(unknown error)"; var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; if (length) GROWABLE_HEAP_I32()[((length) >> 2)] = numBytesWrittenExclNull; }; var _glGetShaderiv = (shader, pname, p) => { if (!p) { // GLES2 specification does not specify how to behave if p is a null // pointer. Since calling this function does not make sense if p == null, // issue a GL error to notify user about it. GL.recordError(1281); return; } if (pname == 35716) { // GL_INFO_LOG_LENGTH var log = GLctx.getShaderInfoLog(GL.shaders[shader]); if (log === null) log = "(unknown error)"; // The GLES2 specification says that if the shader has an empty info log, // a value of 0 is returned. Otherwise the log has a null char appended. // (An empty string is falsey, so we can just check that instead of // looking at log.length.) var logLength = log ? log.length + 1 : 0; GROWABLE_HEAP_I32()[((p) >> 2)] = logLength; } else if (pname == 35720) { // GL_SHADER_SOURCE_LENGTH var source = GLctx.getShaderSource(GL.shaders[shader]); // source may be a null, or the empty string, both of which are falsey // values that we report a 0 length for. var sourceLength = source ? source.length + 1 : 0; GROWABLE_HEAP_I32()[((p) >> 2)] = sourceLength; } else { GROWABLE_HEAP_I32()[((p) >> 2)] = GLctx.getShaderParameter(GL.shaders[shader], pname); } }; /** @suppress {checkTypes} */ var jstoi_q = str => parseInt(str); /** @noinline */ var webglGetLeftBracePos = name => name.slice(-1) == "]" && name.lastIndexOf("["); var webglPrepareUniformLocationsBeforeFirstUse = program => { var uniformLocsById = program.uniformLocsById, // Maps GLuint -> WebGLUniformLocation uniformSizeAndIdsByName = program.uniformSizeAndIdsByName, // Maps name -> [uniform array length, GLuint] i, j; // On the first time invocation of glGetUniformLocation on this shader program: // initialize cache data structures and discover which uniforms are arrays. if (!uniformLocsById) { // maps GLint integer locations to WebGLUniformLocations program.uniformLocsById = uniformLocsById = {}; // maps integer locations back to uniform name strings, so that we can lazily fetch uniform array locations program.uniformArrayNamesById = {}; var numActiveUniforms = GLctx.getProgramParameter(program, 35718); for (i = 0; i < numActiveUniforms; ++i) { var u = GLctx.getActiveUniform(program, i); var nm = u.name; var sz = u.size; var lb = webglGetLeftBracePos(nm); var arrayName = lb > 0 ? nm.slice(0, lb) : nm; // Assign a new location. var id = program.uniformIdCounter; program.uniformIdCounter += sz; // Eagerly get the location of the uniformArray[0] base element. // The remaining indices >0 will be left for lazy evaluation to // improve performance. Those may never be needed to fetch, if the // application fills arrays always in full starting from the first // element of the array. uniformSizeAndIdsByName[arrayName] = [ sz, id ]; // Store placeholder integers in place that highlight that these // >0 index locations are array indices pending population. for (j = 0; j < sz; ++j) { uniformLocsById[id] = j; program.uniformArrayNamesById[id++] = arrayName; } } } }; var _glGetUniformLocation = (program, name) => { name = UTF8ToString(name); if (program = GL.programs[program]) { webglPrepareUniformLocationsBeforeFirstUse(program); var uniformLocsById = program.uniformLocsById; // Maps GLuint -> WebGLUniformLocation var arrayIndex = 0; var uniformBaseName = name; // Invariant: when populating integer IDs for uniform locations, we must // maintain the precondition that arrays reside in contiguous addresses, // i.e. for a 'vec4 colors[10];', colors[4] must be at location // colors[0]+4. However, user might call glGetUniformLocation(program, // "colors") for an array, so we cannot discover based on the user input // arguments whether the uniform we are dealing with is an array. The only // way to discover which uniforms are arrays is to enumerate over all the // active uniforms in the program. var leftBrace = webglGetLeftBracePos(name); // If user passed an array accessor "[index]", parse the array index off the accessor. if (leftBrace > 0) { arrayIndex = jstoi_q(name.slice(leftBrace + 1)) >>> 0; // "index]", coerce parseInt(']') with >>>0 to treat "foo[]" as "foo[0]" and foo[-1] as unsigned out-of-bounds. uniformBaseName = name.slice(0, leftBrace); } // Have we cached the location of this uniform before? // A pair [array length, GLint of the uniform location] var sizeAndId = program.uniformSizeAndIdsByName[uniformBaseName]; // If an uniform with this name exists, and if its index is within the // array limits (if it's even an array), query the WebGLlocation, or // return an existing cached location. if (sizeAndId && arrayIndex < sizeAndId[0]) { arrayIndex += sizeAndId[1]; // Add the base location of the uniform to the array index offset. if ((uniformLocsById[arrayIndex] = uniformLocsById[arrayIndex] || GLctx.getUniformLocation(program, name))) { return arrayIndex; } } } else { // N.b. we are currently unable to distinguish between GL program IDs that // never existed vs GL program IDs that have been deleted, so report // GL_INVALID_VALUE in both cases. GL.recordError(1281); } return -1; }; var _glLinkProgram = program => { program = GL.programs[program]; GLctx.linkProgram(program); // Invalidate earlier computed uniform->ID mappings, those have now become stale program.uniformLocsById = 0; // Mark as null-like so that glGetUniformLocation() knows to populate this again. program.uniformSizeAndIdsByName = {}; }; var _glShaderSource = (shader, count, string, length) => { var source = GL.getSource(shader, count, string, length); GLctx.shaderSource(GL.shaders[shader], source); }; var computeUnpackAlignedImageSize = (width, height, sizePerPixel) => { function roundedToNextMultipleOf(x, y) { return (x + y - 1) & -y; } var plainRowSize = (GL.unpackRowLength || width) * sizePerPixel; var alignedRowSize = roundedToNextMultipleOf(plainRowSize, GL.unpackAlignment); return height * alignedRowSize; }; var colorChannelsInGlTextureFormat = format => { // Micro-optimizations for size: map format to size by subtracting smallest // enum value (0x1902) from all values first. Also omit the most common // size value (1) from the list, which is assumed by formats not on the // list. var colorChannels = { // 0x1902 /* GL_DEPTH_COMPONENT */ - 0x1902: 1, // 0x1906 /* GL_ALPHA */ - 0x1902: 1, 5: 3, 6: 4, // 0x1909 /* GL_LUMINANCE */ - 0x1902: 1, 8: 2, 29502: 3, 29504: 4, // 0x1903 /* GL_RED */ - 0x1902: 1, 26917: 2, 26918: 2, // 0x8D94 /* GL_RED_INTEGER */ - 0x1902: 1, 29846: 3, 29847: 4 }; return colorChannels[format - 6402] || 1; }; var heapObjectForWebGLType = type => { // Micro-optimization for size: Subtract lowest GL enum number (0x1400/* GL_BYTE */) from type to compare // smaller values for the heap, for shorter generated code size. // Also the type HEAPU16 is not tested for explicitly, but any unrecognized type will return out HEAPU16. // (since most types are HEAPU16) type -= 5120; if (type == 0) return GROWABLE_HEAP_I8(); if (type == 1) return GROWABLE_HEAP_U8(); if (type == 2) return GROWABLE_HEAP_I16(); if (type == 4) return GROWABLE_HEAP_I32(); if (type == 6) return GROWABLE_HEAP_F32(); if (type == 5 || type == 28922 || type == 28520 || type == 30779 || type == 30782) return GROWABLE_HEAP_U32(); return GROWABLE_HEAP_U16(); }; var toTypedArrayIndex = (pointer, heap) => pointer >>> (31 - Math.clz32(heap.BYTES_PER_ELEMENT)); var emscriptenWebGLGetTexPixelData = (type, format, width, height, pixels, internalFormat) => { var heap = heapObjectForWebGLType(type); var sizePerPixel = colorChannelsInGlTextureFormat(format) * heap.BYTES_PER_ELEMENT; var bytes = computeUnpackAlignedImageSize(width, height, sizePerPixel); return heap.subarray(toTypedArrayIndex(pixels, heap), toTypedArrayIndex(pixels + bytes, heap)); }; var _glTexImage2D = (target, level, internalFormat, width, height, border, format, type, pixels) => { if (GL.currentContext.version >= 2) { if (GLctx.currentPixelUnpackBufferBinding) { GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels); return; } if (pixels) { var heap = heapObjectForWebGLType(type); var index = toTypedArrayIndex(pixels, heap); GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, heap, index); return; } } var pixelData = pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null; GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixelData); }; var _glTexParameterf = (x0, x1, x2) => GLctx.texParameterf(x0, x1, x2); var _glTexParameteri = (x0, x1, x2) => GLctx.texParameteri(x0, x1, x2); var webglGetUniformLocation = location => { var p = GLctx.currentProgram; if (p) { var webglLoc = p.uniformLocsById[location]; // p.uniformLocsById[location] stores either an integer, or a // WebGLUniformLocation. // If an integer, we have not yet bound the location, so do it now. The // integer value specifies the array index we should bind to. if (typeof webglLoc == "number") { p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? `[${webglLoc}]` : "")); } // Else an already cached WebGLUniformLocation, return it. return webglLoc; } else { GL.recordError(1282); } }; var _glUniform1i = (location, v0) => { GLctx.uniform1i(webglGetUniformLocation(location), v0); }; var miniTempWebGLFloatBuffers = []; var _glUniformMatrix4fv = (location, count, transpose, value) => { if (GL.currentContext.version >= 2) { count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, GROWABLE_HEAP_F32(), ((value) >> 2), count * 16); return; } if (count <= 18) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[16 * count]; // hoist the heap out of the loop for size and for pthreads+growth. var heap = GROWABLE_HEAP_F32(); value = ((value) >> 2); count *= 16; for (var i = 0; i < count; i += 16) { var dst = value + i; view[i] = heap[dst]; view[i + 1] = heap[dst + 1]; view[i + 2] = heap[dst + 2]; view[i + 3] = heap[dst + 3]; view[i + 4] = heap[dst + 4]; view[i + 5] = heap[dst + 5]; view[i + 6] = heap[dst + 6]; view[i + 7] = heap[dst + 7]; view[i + 8] = heap[dst + 8]; view[i + 9] = heap[dst + 9]; view[i + 10] = heap[dst + 10]; view[i + 11] = heap[dst + 11]; view[i + 12] = heap[dst + 12]; view[i + 13] = heap[dst + 13]; view[i + 14] = heap[dst + 14]; view[i + 15] = heap[dst + 15]; } } else { var view = GROWABLE_HEAP_F32().subarray((((value) >> 2)), ((value + count * 64) >> 2)); } GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view); }; var _glUseProgram = program => { program = GL.programs[program]; GLctx.useProgram(program); // Record the currently active program so that we can access the uniform // mapping table of that program. GLctx.currentProgram = program; }; var _glVertexAttribPointer = (index, size, type, normalized, stride, ptr) => { GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); }; var _glViewport = (x0, x1, x2, x3) => GLctx.viewport(x0, x1, x2, x3); var wasmTableMirror = []; /** @type {WebAssembly.Table} */ var wasmTable; var getWasmTableEntry = funcPtr => { var func = wasmTableMirror[funcPtr]; if (!func) { /** @suppress {checkTypes} */ wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); } /** @suppress {checkTypes} */ assert(wasmTable.get(funcPtr) == func, "JavaScript-side Wasm function table mirror is out of date!"); return func; }; var runAndAbortIfError = func => { try { return func(); } catch (e) { abort(e); } }; var sigToWasmTypes = sig => { var typeNames = { "i": "i32", "j": "i64", "f": "f32", "d": "f64", "e": "externref", "p": "i32" }; var type = { parameters: [], results: sig[0] == "v" ? [] : [ typeNames[sig[0]] ] }; for (var i = 1; i < sig.length; ++i) { assert(sig[i] in typeNames, "invalid signature char: " + sig[i]); type.parameters.push(typeNames[sig[i]]); } return type; }; var Asyncify = { instrumentWasmImports(imports) { var importPattern = /^(invoke_.*|__asyncjs__.*)$/; for (let [x, original] of Object.entries(imports)) { if (typeof original == "function") { let isAsyncifyImport = original.isAsync || importPattern.test(x); imports[x] = (...args) => { var originalAsyncifyState = Asyncify.state; try { return original(...args); } finally { // Only asyncify-declared imports are allowed to change the // state. // Changing the state from normal to disabled is allowed (in any // function) as that is what shutdown does (and we don't have an // explicit list of shutdown imports). var changedToDisabled = originalAsyncifyState === Asyncify.State.Normal && Asyncify.state === Asyncify.State.Disabled; // invoke_* functions are allowed to change the state if we do // not ignore indirect calls. var ignoredInvoke = x.startsWith("invoke_") && true; if (Asyncify.state !== originalAsyncifyState && !isAsyncifyImport && !changedToDisabled && !ignoredInvoke) { throw new Error(`import ${x} was not in ASYNCIFY_IMPORTS, but changed the state`); } } }; } } }, instrumentWasmExports(exports) { var ret = {}; for (let [x, original] of Object.entries(exports)) { if (typeof original == "function") { ret[x] = (...args) => { Asyncify.exportCallStack.push(x); try { return original(...args); } finally { if (!ABORT) { var y = Asyncify.exportCallStack.pop(); assert(y === x); Asyncify.maybeStopUnwind(); } } }; } else { ret[x] = original; } } return ret; }, State: { Normal: 0, Unwinding: 1, Rewinding: 2, Disabled: 3 }, state: 0, StackSize: 4096, currData: null, handleSleepReturnValue: 0, exportCallStack: [], callStackNameToId: {}, callStackIdToName: {}, callStackId: 0, asyncPromiseHandlers: null, sleepCallbacks: [], getCallStackId(funcName) { var id = Asyncify.callStackNameToId[funcName]; if (id === undefined) { id = Asyncify.callStackId++; Asyncify.callStackNameToId[funcName] = id; Asyncify.callStackIdToName[id] = funcName; } return id; }, maybeStopUnwind() { if (Asyncify.currData && Asyncify.state === Asyncify.State.Unwinding && Asyncify.exportCallStack.length === 0) { // We just finished unwinding. // Be sure to set the state before calling any other functions to avoid // possible infinite recursion here (For example in debug pthread builds // the dbg() function itself can call back into WebAssembly to get the // current pthread_self() pointer). Asyncify.state = Asyncify.State.Normal; runtimeKeepalivePush(); // Keep the runtime alive so that a re-wind can be done later. runAndAbortIfError(_asyncify_stop_unwind); if (typeof Fibers != "undefined") { Fibers.trampoline(); } } }, whenDone() { assert(Asyncify.currData, "Tried to wait for an async operation when none is in progress."); assert(!Asyncify.asyncPromiseHandlers, "Cannot have multiple async operations in flight at once"); return new Promise((resolve, reject) => { Asyncify.asyncPromiseHandlers = { resolve, reject }; }); }, allocateData() { // An asyncify data structure has three fields: // 0 current stack pos // 4 max stack pos // 8 id of function at bottom of the call stack (callStackIdToName[id] == name of js function) // The Asyncify ABI only interprets the first two fields, the rest is for the runtime. // We also embed a stack in the same memory region here, right next to the structure. // This struct is also defined as asyncify_data_t in emscripten/fiber.h var ptr = _malloc(12 + Asyncify.StackSize); Asyncify.setDataHeader(ptr, ptr + 12, Asyncify.StackSize); Asyncify.setDataRewindFunc(ptr); return ptr; }, setDataHeader(ptr, stack, stackSize) { GROWABLE_HEAP_U32()[((ptr) >> 2)] = stack; GROWABLE_HEAP_U32()[(((ptr) + (4)) >> 2)] = stack + stackSize; }, setDataRewindFunc(ptr) { var bottomOfCallStack = Asyncify.exportCallStack[0]; var rewindId = Asyncify.getCallStackId(bottomOfCallStack); GROWABLE_HEAP_I32()[(((ptr) + (8)) >> 2)] = rewindId; }, getDataRewindFuncName(ptr) { var id = GROWABLE_HEAP_I32()[(((ptr) + (8)) >> 2)]; var name = Asyncify.callStackIdToName[id]; return name; }, getDataRewindFunc(name) { var func = wasmExports[name]; return func; }, doRewind(ptr) { var name = Asyncify.getDataRewindFuncName(ptr); var func = Asyncify.getDataRewindFunc(name); // Once we have rewound and the stack we no longer need to artificially // keep the runtime alive. runtimeKeepalivePop(); return func(); }, handleSleep(startAsync) { assert(Asyncify.state !== Asyncify.State.Disabled, "Asyncify cannot be done during or after the runtime exits"); if (ABORT) return; if (Asyncify.state === Asyncify.State.Normal) { // Prepare to sleep. Call startAsync, and see what happens: // if the code decided to call our callback synchronously, // then no async operation was in fact begun, and we don't // need to do anything. var reachedCallback = false; var reachedAfterCallback = false; startAsync((handleSleepReturnValue = 0) => { assert(!handleSleepReturnValue || typeof handleSleepReturnValue == "number" || typeof handleSleepReturnValue == "boolean"); // old emterpretify API supported other stuff if (ABORT) return; Asyncify.handleSleepReturnValue = handleSleepReturnValue; reachedCallback = true; if (!reachedAfterCallback) { // We are happening synchronously, so no need for async. return; } // This async operation did not happen synchronously, so we did // unwind. In that case there can be no compiled code on the stack, // as it might break later operations (we can rewind ok now, but if // we unwind again, we would unwind through the extra compiled code // too). assert(!Asyncify.exportCallStack.length, "Waking up (starting to rewind) must be done from JS, without compiled code on the stack."); Asyncify.state = Asyncify.State.Rewinding; runAndAbortIfError(() => _asyncify_start_rewind(Asyncify.currData)); if (typeof MainLoop != "undefined" && MainLoop.func) { MainLoop.resume(); } var asyncWasmReturnValue, isError = false; try { asyncWasmReturnValue = Asyncify.doRewind(Asyncify.currData); } catch (err) { asyncWasmReturnValue = err; isError = true; } // Track whether the return value was handled by any promise handlers. var handled = false; if (!Asyncify.currData) { // All asynchronous execution has finished. // `asyncWasmReturnValue` now contains the final // return value of the exported async WASM function. // Note: `asyncWasmReturnValue` is distinct from // `Asyncify.handleSleepReturnValue`. // `Asyncify.handleSleepReturnValue` contains the return // value of the last C function to have executed // `Asyncify.handleSleep()`, where as `asyncWasmReturnValue` // contains the return value of the exported WASM function // that may have called C functions that // call `Asyncify.handleSleep()`. var asyncPromiseHandlers = Asyncify.asyncPromiseHandlers; if (asyncPromiseHandlers) { Asyncify.asyncPromiseHandlers = null; (isError ? asyncPromiseHandlers.reject : asyncPromiseHandlers.resolve)(asyncWasmReturnValue); handled = true; } } if (isError && !handled) { // If there was an error and it was not handled by now, we have no choice but to // rethrow that error into the global scope where it can be caught only by // `onerror` or `onunhandledpromiserejection`. throw asyncWasmReturnValue; } }); reachedAfterCallback = true; if (!reachedCallback) { // A true async operation was begun; start a sleep. Asyncify.state = Asyncify.State.Unwinding; // TODO: reuse, don't alloc/free every sleep Asyncify.currData = Asyncify.allocateData(); if (typeof MainLoop != "undefined" && MainLoop.func) { MainLoop.pause(); } runAndAbortIfError(() => _asyncify_start_unwind(Asyncify.currData)); } } else if (Asyncify.state === Asyncify.State.Rewinding) { // Stop a resume. Asyncify.state = Asyncify.State.Normal; runAndAbortIfError(_asyncify_stop_rewind); _free(Asyncify.currData); Asyncify.currData = null; // Call all sleep callbacks now that the sleep-resume is all done. Asyncify.sleepCallbacks.forEach(callUserCallback); } else { abort(`invalid state: ${Asyncify.state}`); } return Asyncify.handleSleepReturnValue; }, handleAsync(startAsync) { return Asyncify.handleSleep(wakeUp => { // TODO: add error handling as a second param when handleSleep implements it. startAsync().then(wakeUp); }); } }; var getCFunc = ident => { var func = Module["_" + ident]; // closure exported function assert(func, "Cannot call unknown function " + ident + ", make sure it is exported"); return func; }; var writeArrayToMemory = (array, buffer) => { assert(array.length >= 0, "writeArrayToMemory array must have a length (should be an array or typed array)"); GROWABLE_HEAP_I8().set(array, buffer); }; var stringToUTF8OnStack = str => { var size = lengthBytesUTF8(str) + 1; var ret = stackAlloc(size); stringToUTF8(str, ret, size); return ret; }; /** * @param {string|null=} returnType * @param {Array=} argTypes * @param {Arguments|Array=} args * @param {Object=} opts */ var ccall = (ident, returnType, argTypes, args, opts) => { // For fast lookup of conversion functions var toC = { "string": str => { var ret = 0; if (str !== null && str !== undefined && str !== 0) { // null string ret = stringToUTF8OnStack(str); } return ret; }, "array": arr => { var ret = stackAlloc(arr.length); writeArrayToMemory(arr, ret); return ret; } }; function convertReturnValue(ret) { if (returnType === "string") { return UTF8ToString(ret); } if (returnType === "boolean") return Boolean(ret); return ret; } var func = getCFunc(ident); var cArgs = []; var stack = 0; assert(returnType !== "array", 'Return type should not be "array".'); if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]]; if (converter) { if (stack === 0) stack = stackSave(); cArgs[i] = converter(args[i]); } else { cArgs[i] = args[i]; } } } // Data for a previous async operation that was in flight before us. var previousAsync = Asyncify.currData; var ret = func(...cArgs); function onDone(ret) { runtimeKeepalivePop(); if (stack !== 0) stackRestore(stack); return convertReturnValue(ret); } var asyncMode = opts?.async; // Keep the runtime alive through all calls. Note that this call might not be // async, but for simplicity we push and pop in all calls. runtimeKeepalivePush(); if (Asyncify.currData != previousAsync) { // A change in async operation happened. If there was already an async // operation in flight before us, that is an error: we should not start // another async operation while one is active, and we should not stop one // either. The only valid combination is to have no change in the async // data (so we either had one in flight and left it alone, or we didn't have // one), or to have nothing in flight and to start one. assert(!(previousAsync && Asyncify.currData), "We cannot start an async operation when one is already flight"); assert(!(previousAsync && !Asyncify.currData), "We cannot stop an async operation in flight"); // This is a new async operation. The wasm is paused and has unwound its stack. // We need to return a Promise that resolves the return value // once the stack is rewound and execution finishes. assert(asyncMode, "The call to " + ident + " is running asynchronously. If this was intended, add the async option to the ccall/cwrap call."); return Asyncify.whenDone().then(onDone); } ret = onDone(ret); // If this is an async ccall, ensure we return a promise if (asyncMode) return Promise.resolve(ret); return ret; }; /** * @param {string=} returnType * @param {Array=} argTypes * @param {Object=} opts */ var cwrap = (ident, returnType, argTypes, opts) => (...args) => ccall(ident, returnType, argTypes, args, opts); var uleb128Encode = (n, target) => { assert(n < 16384); if (n < 128) { target.push(n); } else { target.push((n % 128) | 128, n >> 7); } }; var generateFuncType = (sig, target) => { var sigRet = sig.slice(0, 1); var sigParam = sig.slice(1); var typeCodes = { "i": 127, // i32 "p": 127, // i32 "j": 126, // i64 "f": 125, // f32 "d": 124, // f64 "e": 111 }; // Parameters, length + signatures target.push(96); uleb128Encode(sigParam.length, target); for (var paramType of sigParam) { assert(paramType in typeCodes, `invalid signature char: ${paramType}`); target.push(typeCodes[paramType]); } // Return values, length + signatures // With no multi-return in MVP, either 0 (void) or 1 (anything else) if (sigRet == "v") { target.push(0); } else { target.push(1, typeCodes[sigRet]); } }; var convertJsFunctionToWasm = (func, sig) => { // If the type reflection proposal is available, use the new // "WebAssembly.Function" constructor. // Otherwise, construct a minimal wasm module importing the JS function and // re-exporting it. if (typeof WebAssembly.Function == "function") { return new WebAssembly.Function(sigToWasmTypes(sig), func); } // The module is static, with the exception of the type section, which is // generated based on the signature passed in. var typeSectionBody = [ 1 ]; generateFuncType(sig, typeSectionBody); // Rest of the module is static var bytes = [ 0, 97, 115, 109, // magic ("\0asm") 1, 0, 0, 0, // version: 1 1 ]; // Write the overall length of the type section followed by the body uleb128Encode(typeSectionBody.length, bytes); bytes.push(...typeSectionBody); // The rest of the module is static bytes.push(2, 7, // import section // (import "e" "f" (func 0 (type 0))) 1, 1, 101, 1, 102, 0, 0, 7, 5, // export section // (export "f" (func 0 (type 0))) 1, 1, 102, 0, 0); // We can compile this wasm module synchronously because it is very small. // This accepts an import (at "e.f"), that it reroutes to an export (at "f") var module = new WebAssembly.Module(new Uint8Array(bytes)); var instance = new WebAssembly.Instance(module, { "e": { "f": func } }); var wrappedFunc = instance.exports["f"]; return wrappedFunc; }; var updateTableMap = (offset, count) => { if (functionsInTableMap) { for (var i = offset; i < offset + count; i++) { var item = getWasmTableEntry(i); // Ignore null values. if (item) { functionsInTableMap.set(item, i); } } } }; var functionsInTableMap; var getFunctionAddress = func => { // First, create the map if this is the first use. if (!functionsInTableMap) { functionsInTableMap = new WeakMap; updateTableMap(0, wasmTable.length); } return functionsInTableMap.get(func) || 0; }; var freeTableIndexes = []; var getEmptyTableSlot = () => { // Reuse a free index if there is one, otherwise grow. if (freeTableIndexes.length) { return freeTableIndexes.pop(); } // Grow the table try { /** @suppress {checkTypes} */ wasmTable.grow(1); } catch (err) { if (!(err instanceof RangeError)) { throw err; } throw "Unable to grow wasm table. Set ALLOW_TABLE_GROWTH."; } return wasmTable.length - 1; }; var setWasmTableEntry = (idx, func) => { /** @suppress {checkTypes} */ wasmTable.set(idx, func); // With ABORT_ON_WASM_EXCEPTIONS wasmTable.get is overridden to return wrapped // functions so we need to call it here to retrieve the potential wrapper correctly // instead of just storing 'func' directly into wasmTableMirror /** @suppress {checkTypes} */ wasmTableMirror[idx] = wasmTable.get(idx); }; /** @param {string=} sig */ var addFunction = (func, sig) => { assert(typeof func != "undefined"); // Check if the function is already in the table, to ensure each function // gets a unique index. var rtn = getFunctionAddress(func); if (rtn) { return rtn; } // It's not in the table, add it now. // Make sure functionsInTableMap is actually up to date, that is, that this // function is not actually in the wasm Table despite not being tracked in // functionsInTableMap. for (var i = 0; i < wasmTable.length; i++) { assert(getWasmTableEntry(i) != func, "function in Table but not functionsInTableMap"); } var ret = getEmptyTableSlot(); // Set the new value. try { // Attempting to call this with JS function will cause of table.set() to fail setWasmTableEntry(ret, func); } catch (err) { if (!(err instanceof TypeError)) { throw err; } assert(typeof sig != "undefined", "Missing signature argument to addFunction: " + func); var wrapped = convertJsFunctionToWasm(func, sig); setWasmTableEntry(ret, wrapped); } functionsInTableMap.set(func, ret); return ret; }; var removeFunction = index => { functionsInTableMap.delete(getWasmTableEntry(index)); setWasmTableEntry(index, null); freeTableIndexes.push(index); }; var allocateUTF8 = stringToNewUTF8; var FS_createPath = FS.createPath; var FS_unlink = path => FS.unlink(path); var FS_createLazyFile = FS.createLazyFile; var FS_createDevice = FS.createDevice; PThread.init(); FS.createPreloadedFile = FS_createPreloadedFile; FS.staticInit(); // Set module methods based on EXPORTED_RUNTIME_METHODS Module["FS_createPath"] = FS.createPath; Module["FS_createDataFile"] = FS.createDataFile; Module["FS_createPreloadedFile"] = FS.createPreloadedFile; Module["FS_unlink"] = FS.unlink; Module["FS_createLazyFile"] = FS.createLazyFile; Module["FS_createDevice"] = FS.createDevice; embind_init_charCodes(); init_emval(); Fetch.init(); registerPreMainLoop(() => { // If the current GL context is an OffscreenCanvas, but it was initialized // with implicit swap mode, perform the swap on behalf of the user. if (GL.currentContext && !GL.currentContextIsProxied && !GL.currentContext.attributes.explicitSwapControl && GL.currentContext.GLctx.commit) { GL.currentContext.GLctx.commit(); } }); var miniTempWebGLFloatBuffersStorage = new Float32Array(288); // Create GL_POOL_TEMP_BUFFERS_SIZE+1 temporary buffers, for uploads of size 0 through GL_POOL_TEMP_BUFFERS_SIZE inclusive for (/**@suppress{duplicate}*/ var i = 0; i <= 288; ++i) { miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i); } // End JS library code // proxiedFunctionTable specifies the list of functions that can be called // either synchronously or asynchronously from other threads in postMessage()d // or internally queued events. This way a pthread in a Worker can synchronously // access e.g. the DOM on the main thread. var proxiedFunctionTable = [ _proc_exit, exitOnMainThread, pthreadCreateProxied, ___syscall_fcntl64, ___syscall_fstat64, ___syscall_openat, __mmap_js, __munmap_js, __setitimer_js, setCanvasElementSizeMainThread, _environ_get, _environ_sizes_get, _fd_close, _fd_fdstat_get, _fd_read, _fd_seek, _fd_write ]; function checkIncomingModuleAPI() { ignoredModuleProp("fetchSettings"); } var ASM_CONSTS = { 414036: () => { if (typeof window != "undefined") { console.log("logRequest_downloadSucceeded OK"); window.dispatchEvent(new CustomEvent("wasmTextDownloadSuccessed")); } else { console.log("logRequest_downloadSucceeded failed"); } }, 414251: () => { self.postMessage({ type: "restart-load-media" }); }, 414305: () => { self.postMessage({ type: "release_done" }); }, 414353: $0 => { const canvasId = Module.UTF8ToString($0); Module.webcodec_seek_target_pts = -1; if (!Module.frameQueueLastQueuedPtsMap) { Module.frameQueueLastQueuedPtsMap = {}; } if (!Module.frameQueueLastRenderedPtsMap) { Module.frameQueueLastRenderedPtsMap = {}; } if (!Module.frameQueueReplayDiscardBeforePtsMap) { Module.frameQueueReplayDiscardBeforePtsMap = {}; } if (!Module.outputSerialMap) { Module.outputSerialMap = {}; } Module.frameQueueLastQueuedPtsMap[canvasId] = -1; Module.frameQueueLastRenderedPtsMap[canvasId] = -1; Module.frameQueueReplayDiscardBeforePtsMap[canvasId] = -1; Module.outputSerialMap[canvasId] = Promise.resolve(); const decoder = Module.decoder; Module.decoder = null; if (decoder) { try { decoder.close(); console.log("Closed VideoDecoder"); } catch (e) { console.warn("VideoDecoder close error:", e); } } if (Module.frameQueueMap && Module.frameQueueMap[canvasId]) { Module.frameQueueMap[canvasId].forEach(frame => { if (frame.texture && Module.gl) { Module.gl.deleteTexture(frame.texture); frame.texture = null; } frame = null; }); Module.frameQueueMap[canvasId] = []; Module.frameQueueMap = null; console.log("Cleared frame queue"); } if (Module.gopChunk) { Module.gopChunk.forEach(chunk => { chunk.data = null; chunk = null; }); Module.gopChunk = []; console.log("Cleared GOP chunks"); } if (Module.shaderProgram && Module.gl) { Module.gl.deleteProgram(Module.shaderProgram); Module.shaderProgram = null; console.log("Deleted shader program"); } if (Module.gl) { Module.gl.getExtension("WEBGL_lose_context").loseContext(); Module.gl = null; console.log("Released WebGL context"); } }, 415962: () => { self.postMessage({ type: "release_done" }); }, 416010: $0 => { const canvasId = Module.UTF8ToString($0); Module.webcodec_seek_target_pts = -1; if (!Module.frameQueueLastQueuedPtsMap) { Module.frameQueueLastQueuedPtsMap = {}; } if (!Module.frameQueueLastRenderedPtsMap) { Module.frameQueueLastRenderedPtsMap = {}; } if (!Module.frameQueueReplayDiscardBeforePtsMap) { Module.frameQueueReplayDiscardBeforePtsMap = {}; } if (!Module.outputSerialMap) { Module.outputSerialMap = {}; } Module.frameQueueLastQueuedPtsMap[canvasId] = -1; Module.frameQueueLastRenderedPtsMap[canvasId] = -1; Module.frameQueueReplayDiscardBeforePtsMap[canvasId] = -1; Module.outputSerialMap[canvasId] = Promise.resolve(); const decoder = Module.decoder; if (decoder) { try { console.warn("wcodec ctx root: clean_tex_queue decoder flush", Module.decoder); decoder.flush(); } catch (e) { console.warn("VideoDecoder close error:", e); } } if (Module.frameQueueMap && Module.frameQueueMap[canvasId]) { Module.frameQueueMap[canvasId].forEach(frame => { if (frame.texture && Module.gl) { Module.gl.deleteTexture(frame.texture); frame.texture = null; } frame = null; }); Module.frameQueueMap[canvasId] = []; console.log("Cleared frame queue"); } if (Module.gopChunk) { Module.gopChunk.forEach(chunk => { chunk.data = null; chunk = null; }); Module.gopChunk = []; console.log("Cleared GOP chunks"); } console.warn("wcodec ctx root: clean_tex_queue queue data", Module.frameQueueMap[canvasId]); }, 417410: $0 => { const canvasId = Module.UTF8ToString($0); if (!Module.frameQueueMap || !Module.frameQueueMap[canvasId] || Module.frameQueueMap[canvasId].length === 0) { return -1; } const frame = Module.frameQueueMap[canvasId][0]; if (frame && typeof frame.pts !== "undefined") { return frame.pts; } return -1; }, 417709: $0 => { const canvasId = Module.UTF8ToString($0); if (!Module.frameQueueMap || !Module.frameQueueMap[canvasId] || Module.frameQueueMap[canvasId].length === 0) { return 0; } const frame = Module.frameQueueMap[canvasId].shift(); if (frame && frame.texture) { Module.gl.deleteTexture(frame.texture); frame.texture = null; } return Module.frameQueueMap[canvasId].length; }, 418072: () => { const gl = Module.gl; if (!gl) return -1; const info = gl.getExtension("WEBGL_debug_renderer_info"); if (info) { const vendor = gl.getParameter(info.UNMASKED_VENDOR_WEBGL); const renderer = gl.getParameter(info.UNMASKED_RENDERER_WEBGL); console.log(`GPU: ${vendor} ${renderer}`); } let count = 0; const width = 1920; const height = 1080; while (true) { try { const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); count++; console.log(`Created texture ${count}`); } catch (e) { break; } } return count; }, 418695: $0 => { Module.gl = null; Module.shaderProgram = null; Module.gopChunk = []; Module.decoder_reconfiguring = false; const c_id = Module.UTF8ToString($0); Module.webcodec_seek_target_pts = -1; function initFrameQueue(canvasId) { if (!Module.frameQueueMap) { Module.frameQueueMap = {}; } Module.frameQueueMap[c_id] = []; if (!Module.frameQueueLastQueuedPtsMap) { Module.frameQueueLastQueuedPtsMap = {}; } if (!Module.frameQueueLastRenderedPtsMap) { Module.frameQueueLastRenderedPtsMap = {}; } if (!Module.frameQueueReplayDiscardBeforePtsMap) { Module.frameQueueReplayDiscardBeforePtsMap = {}; } if (!Module.outputSerialMap) { Module.outputSerialMap = {}; } Module.frameQueueLastQueuedPtsMap[c_id] = -1; Module.frameQueueLastRenderedPtsMap[c_id] = -1; Module.frameQueueReplayDiscardBeforePtsMap[c_id] = -1; Module.outputSerialMap[c_id] = Promise.resolve(); } function initWebGL(canvas) { Module.gl = canvas.getContext("webgl2", { antialias: false, powerPreference: "high-performance" }); if (!Module.gl) { console.error("WebGL 2.0 not supported"); return; } Module.gl.viewport(0, 0, canvas.width, canvas.height); const vsSource = "#version 300 es\n" + "in vec4 aVertexPosition;\n" + "in vec2 aTextureCoord;\n" + "out vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = aVertexPosition;\n" + " vTextureCoord = vec2(aTextureCoord.x, 1.0 - aTextureCoord.y); // 方法1 Y 坐标翻转\n" + "}"; const fsSource = "#version 300 es\n" + "precision mediump float;\n" + "in vec2 vTextureCoord;\n" + "out vec4 FragColor;\n" + "uniform sampler2D uSampler;\n" + "void main() {\n" + " FragColor = texture(uSampler, vTextureCoord);\n" + " //FragColor = texture(uSampler, vec2(vTextureCoord.x, 1.0 - vTextureCoord.y)); // 方法2 反转 Y 坐标\n" + "}"; const vertexShader = Module.gl.createShader(Module.gl.VERTEX_SHADER); Module.gl.shaderSource(vertexShader, vsSource); Module.gl.compileShader(vertexShader); if (!Module.gl.getShaderParameter(vertexShader, Module.gl.COMPILE_STATUS)) { console.error("顶点着色器编译错误:", Module.gl.getShaderInfoLog(vertexShader)); return; } const fragmentShader = Module.gl.createShader(Module.gl.FRAGMENT_SHADER); Module.gl.shaderSource(fragmentShader, fsSource); Module.gl.compileShader(fragmentShader); if (!Module.gl.getShaderParameter(fragmentShader, Module.gl.COMPILE_STATUS)) { console.error("片段着色器编译错误:", Module.gl.getShaderInfoLog(fragmentShader)); return; } Module.shaderProgram = Module.gl.createProgram(); Module.gl.attachShader(Module.shaderProgram, vertexShader); Module.gl.attachShader(Module.shaderProgram, fragmentShader); Module.gl.linkProgram(Module.shaderProgram); Module.gl.useProgram(Module.shaderProgram); if (!Module.gl.getProgramParameter(Module.shaderProgram, Module.gl.LINK_STATUS)) { console.error("着色器程序链接错误:", Module.gl.getProgramInfoLog(Module.shaderProgram)); return; } const vertices = new Float32Array([ -1, 1, 0, 1, -1, -1, 0, 0, 1, 1, 1, 1, 1, -1, 1, 0 ]); const vertexBuffer = Module.gl.createBuffer(); Module.gl.bindBuffer(Module.gl.ARRAY_BUFFER, vertexBuffer); Module.gl.bufferData(Module.gl.ARRAY_BUFFER, vertices, Module.gl.STATIC_DRAW); const aVertexPosition = Module.gl.getAttribLocation(Module.shaderProgram, "aVertexPosition"); Module.gl.vertexAttribPointer(aVertexPosition, 2, Module.gl.FLOAT, false, 16, 0); Module.gl.enableVertexAttribArray(aVertexPosition); const aTextureCoord = Module.gl.getAttribLocation(Module.shaderProgram, "aTextureCoord"); Module.gl.vertexAttribPointer(aTextureCoord, 2, Module.gl.FLOAT, false, 16, 8); Module.gl.enableVertexAttribArray(aTextureCoord); } function initDecoderCall(canvas_id) { Module.decoderGeneration = (Module.decoderGeneration || 0) + 1; const decoderGeneration = Module.decoderGeneration; Module.decoder = new VideoDecoder({ output: frame => { const pts = frame.timestamp; const frame_width = frame.codedWidth; const frame_height = frame.codedHeight; const outputChain = Module.outputSerialMap[canvas_id] || Promise.resolve(); Module.outputSerialMap[canvas_id] = outputChain.then(async () => { let bitmap = null; let texture = null; let frameClosed = false; try { if (decoderGeneration !== Module.decoderGeneration) { console.warn("wcodec drop stale generation output pts=", pts, "generation=", decoderGeneration, "current=", Module.decoderGeneration); frame.close(); frameClosed = true; return; } if (Module.webcodec_seek_target_pts >= 0) { if (pts < Module.webcodec_seek_target_pts) { frame.close(); frameClosed = true; return; } else { Module.webcodec_seek_target_pts = -1; if (Module.frameQueueMap && Module.frameQueueMap[canvas_id]) { Module.frameQueueMap[canvas_id].forEach(frame => { if (frame.texture && Module.gl) { Module.gl.deleteTexture(frame.texture); frame.texture = null; } frame = null; }); Module.frameQueueMap[canvas_id] = []; Module.frameQueueLastQueuedPtsMap[canvas_id] = -1; Module.frameQueueLastRenderedPtsMap[canvas_id] = -1; if (Module.frameQueueReplayDiscardBeforePtsMap) { Module.frameQueueReplayDiscardBeforePtsMap[canvas_id] = -1; } console.log("Cleared frame queue"); } self.postMessage({ type: "seek_success_target", payload: { "width": frame_width, "height": frame_height, "pts": pts, "target_pts": Module.webcodec_seek_target_pts, "cache_size": Module.frameQueueMap[canvas_id].length } }); } } if (!Module.gl) { console.error("wcodec ctx root:decoder callback : WebGL 2.0 not supported"); frame.close(); frameClosed = true; return; } bitmap = await createImageBitmap(frame); if (decoderGeneration !== Module.decoderGeneration) { console.warn("wcodec drop stale generation bitmap pts=", pts, "generation=", decoderGeneration, "current=", Module.decoderGeneration); return; } const lastRenderedPts = Module.frameQueueLastRenderedPtsMap[canvas_id] ?? -1; const lastQueuedPts = Module.frameQueueLastQueuedPtsMap[canvas_id] ?? -1; const replayDiscardBeforePts = Module.frameQueueReplayDiscardBeforePtsMap ? (Module.frameQueueReplayDiscardBeforePtsMap[canvas_id] ?? -1) : -1; if (replayDiscardBeforePts >= 0 && pts <= replayDiscardBeforePts) { console.warn("wcodec drop replay output pts=", pts, "discardBefore=", replayDiscardBeforePts, "lastRendered=", lastRenderedPts); return; } if (replayDiscardBeforePts >= 0 && pts > replayDiscardBeforePts && Module.frameQueueReplayDiscardBeforePtsMap) { console.warn("wcodec replay resume output pts=", pts, "discardBefore=", replayDiscardBeforePts); Module.frameQueueReplayDiscardBeforePtsMap[canvas_id] = -1; } if (pts <= lastRenderedPts || (lastQueuedPts >= 0 && pts <= lastQueuedPts)) { console.warn("wcodec drop stale output pts=", pts, "lastRendered=", lastRenderedPts, "lastQueued=", lastQueuedPts); return; } texture = Module.gl.createTexture(); Module.gl.bindTexture(Module.gl.TEXTURE_2D, texture); Module.gl.texImage2D(Module.gl.TEXTURE_2D, 0, Module.gl.RGBA, Module.gl.RGBA, Module.gl.UNSIGNED_BYTE, bitmap); Module.gl.texParameteri(Module.gl.TEXTURE_2D, Module.gl.TEXTURE_WRAP_S, Module.gl.CLAMP_TO_EDGE); Module.gl.texParameteri(Module.gl.TEXTURE_2D, Module.gl.TEXTURE_WRAP_T, Module.gl.CLAMP_TO_EDGE); Module.gl.texParameteri(Module.gl.TEXTURE_2D, Module.gl.TEXTURE_MIN_FILTER, Module.gl.LINEAR); Module.frameQueueMap[canvas_id].push({ "texture": texture, "width": frame_width, "height": frame_height, "pts": pts }); Module.frameQueueLastQueuedPtsMap[canvas_id] = pts; texture = null; self.postMessage({ type: "decode_video_progress", payload: { "width": frame_width, "height": frame_height, "pts": pts, "cache_size": Module.frameQueueMap[canvas_id].length } }); } catch (e) { console.error("Frame processing error:", e); if (texture && Module.gl) { Module.gl.deleteTexture(texture); } } finally { if (bitmap) { bitmap.close(); } if (!frameClosed) { frame.close(); } } }).catch(e => { console.error("Frame output chain error:", e); try { frame.close(); } catch (closeErr) {} }); }, error: e => { if (Module.decoder_reconfiguring) { return; } Module.decoder_reconfiguring = true; Module.decoder_error_wait = true; const currentQueueLength = Module.frameQueueMap && Module.frameQueueMap[canvas_id] ? Module.frameQueueMap[canvas_id].length : 0; const currentLastRenderedPts = Module.frameQueueLastRenderedPtsMap ? (Module.frameQueueLastRenderedPtsMap[canvas_id] ?? -1) : -1; const currentLastQueuedPts = Module.frameQueueLastQueuedPtsMap ? (Module.frameQueueLastQueuedPtsMap[canvas_id] ?? -1) : -1; const summarizeChunks = chunks => { if (!chunks || chunks.length <= 0) { return "len=0"; } const first = chunks[0]; const last = chunks[chunks.length - 1]; let keyCount = 0; for (const chunk of chunks) { if (chunk && chunk.type === "key") { keyCount += 1; } } const head = chunks.slice(0, Math.min(chunks.length, 4)).map(chunk => `${chunk.type}@${chunk.timestamp}`).join("|"); const tail = chunks.length > 4 ? chunks.slice(Math.max(4, chunks.length - 2)).map(chunk => `${chunk.type}@${chunk.timestamp}`).join("|") : ""; return `len=${chunks.length} key_count=${keyCount} first=${first.type}@${first.timestamp} last=${last.type}@${last.timestamp} head=${head}${tail ? ` tail=${tail}` : ""}`; }; const gopSnapshotBeforeReplay = Module.gopChunk ? Module.gopChunk.slice() : []; console.error("wcodec decoder error, replay current gop", "generation=", decoderGeneration, "queue_len=", currentQueueLength, "last_rendered_pts=", currentLastRenderedPts, "last_queued_pts=", currentLastQueuedPts, "gop_summary=", summarizeChunks(gopSnapshotBeforeReplay), "name=", e && e.name, "message=", e && e.message, "code=", e && e.code); (async () => { try { if (Module.decoder) { try { Module.decoder.close(); } catch (closeErr) {} } Module.ccall("wcodec_reset_decoder_status", "", [ "number" ], [ Module.decoder_ctx_ptr ]); if (Module.frameQueueReplayDiscardBeforePtsMap) { Module.frameQueueReplayDiscardBeforePtsMap[canvas_id] = currentLastQueuedPts; } if (Module.outputSerialMap) { Module.outputSerialMap[canvas_id] = Promise.resolve(); } console.warn("wcodec preserve decoded queue during replay", "queue_len=", currentQueueLength, "last_rendered_pts=", currentLastRenderedPts, "last_queued_pts=", currentLastQueuedPts); initDecoderCall(canvas_id); Module.decoder.configure(Module.decoder_conf); const replayChunks = gopSnapshotBeforeReplay; const replayArmsKeyframeGate = replayChunks.length > 0 && replayChunks[0] && replayChunks[0].type === "key"; if (replayArmsKeyframeGate) { Module.ccall("wcodec_mark_keyframe_started", "", [ "number" ], [ Module.decoder_ctx_ptr ]); console.warn("wcodec replay arm keyframe gate from replay buffer", "summary=", summarizeChunks(replayChunks)); } else { console.warn("wcodec replay keeps keyframe gate closed", "summary=", summarizeChunks(replayChunks)); } Module.gopChunk = replayChunks.slice(); console.warn("wcodec replay reseed live gop buffer before decode", "summary=", summarizeChunks(Module.gopChunk)); for (const chunk of replayChunks) { Module.decoder.decode(new EncodedVideoChunk(chunk)); } } catch (err) { console.error("reset decoder failed:", err); } finally { Module.decoder_error_wait = false; Module.decoder_reconfiguring = false; } })(); } }); } initWebGL(Module.canvas); initFrameQueue(c_id); initDecoderCall(c_id); }, 429858: $0 => { const c_id = Module.UTF8ToString($0); if (!Module.frameQueueMap || !Module.frameQueueMap[c_id]) { return 0; } return Module.frameQueueMap[c_id].length; }, 430014: $0 => { if (Module.webcodec_seek_target_pts >= 0) { return -1; } const canvasId = UTF8ToString($0); if (!Module.frameQueueMap || !Module.frameQueueMap[canvasId] || !Module.frameQueueMap[canvasId].length) { return -1; } const frameData = Module.frameQueueMap[canvasId].shift(); if (!frameData) { return 0; } Module.gl.activeTexture(Module.gl.TEXTURE0); Module.gl.bindTexture(Module.gl.TEXTURE_2D, frameData["texture"]); Module.gl.uniform1i(Module.gl.getUniformLocation(Module.shaderProgram, "uSampler"), 0); Module.gl.drawArrays(Module.gl.TRIANGLE_STRIP, 0, 4); Module.gl.deleteTexture(frameData["texture"]); Module.av_align_video_play_ms = frameData["pts"]; if (!Module.frameQueueLastRenderedPtsMap) { Module.frameQueueLastRenderedPtsMap = {}; } Module.frameQueueLastRenderedPtsMap[canvasId] = Module.av_align_video_play_ms; self.postMessage({ type: "render_progress", payload: { "width": frameData["width"], "height": frameData["height"], "pts": Module.av_align_video_play_ms } }); return 1; }, 431003: ($0, $1, $2, $3, $4, $5) => { const canvasId = Module.UTF8ToString($4); const data = new Uint8Array(Module.HEAPU8.subarray($0, $0 + $1)); if ($3 > 0) { const oldGopLen = Module.gopChunk ? Module.gopChunk.length : 0; const oldGopLastTs = oldGopLen > 0 ? Module.gopChunk[oldGopLen - 1].timestamp : -1; console.warn("core feed start new gop on key", "ts=", Number($2), "old_gop_len=", oldGopLen, "old_gop_last_ts=", oldGopLastTs); Module.gopChunk = []; } if ($5 > 0 && Module.webcodec_seek_target_pts >= 0) { if (Module.frameQueueMap && Module.frameQueueMap[canvasId]) { Module.frameQueueMap[canvasId].forEach(frame => { if (frame.texture && Module.gl) { Module.gl.deleteTexture(frame.texture); frame.texture = null; } frame = null; }); Module.frameQueueMap[canvasId] = []; if (Module.frameQueueLastQueuedPtsMap) { Module.frameQueueLastQueuedPtsMap[canvasId] = -1; } if (Module.frameQueueLastRenderedPtsMap) { Module.frameQueueLastRenderedPtsMap[canvasId] = -1; } if (Module.frameQueueReplayDiscardBeforePtsMap) { Module.frameQueueReplayDiscardBeforePtsMap[canvasId] = -1; } console.log("Cleared frame queue"); } } const key_type = $3 > 0 ? "key" : "delta"; const timestamp = Number($2); Module.gopChunk.push({ "type": key_type, "timestamp": timestamp, "data": data }); const chunk = new EncodedVideoChunk({ type: key_type, timestamp, data }); Module.decoder.decode(chunk); } }; function js_alert_demuxer(msg) { console.log(UTF8ToString(msg)); if (typeof self !== "undefined" && self.constructor?.name === "DedicatedWorkerGlobalScope") { postMessage({ type: "alert_page", payload: UTF8ToString(msg) }); self.close(); } else { alert(UTF8ToString(msg)); } } function js_reload_page_demuxer() { if (typeof self !== "undefined" && self.constructor?.name === "DedicatedWorkerGlobalScope") { postMessage({ type: "reload_page" }); self.close(); } else { location.reload(); } } function js_alert_wdec(msg) { console.log(UTF8ToString(msg)); if (typeof self !== "undefined" && self.constructor?.name === "DedicatedWorkerGlobalScope") { postMessage({ type: "alert_page", payload: UTF8ToString(msg) }); self.close(); } else { alert(UTF8ToString(msg)); } } function js_reload_page_wdec() { if (typeof self !== "undefined" && self.constructor?.name === "DedicatedWorkerGlobalScope") { postMessage({ type: "reload_page" }); self.close(); } else { location.reload(); } } function js_alert_ffdec(msg) { console.log(UTF8ToString(msg)); if (typeof self !== "undefined" && self.constructor?.name === "DedicatedWorkerGlobalScope") { postMessage({ type: "alert_page", payload: UTF8ToString(msg) }); self.close(); } else { alert(UTF8ToString(msg)); } } function js_reload_page_ffdec() { if (typeof self !== "undefined" && self.constructor?.name === "DedicatedWorkerGlobalScope") { postMessage({ type: "reload_page" }); self.close(); } else { location.reload(); } } var wasmImports; function assignWasmImports() { wasmImports = { /** @export */ __assert_fail: ___assert_fail, /** @export */ __cxa_throw: ___cxa_throw, /** @export */ __handle_stack_overflow: ___handle_stack_overflow, /** @export */ __pthread_create_js: ___pthread_create_js, /** @export */ __syscall_fcntl64: ___syscall_fcntl64, /** @export */ __syscall_fstat64: ___syscall_fstat64, /** @export */ __syscall_openat: ___syscall_openat, /** @export */ _abort_js: __abort_js, /** @export */ _embind_register_bigint: __embind_register_bigint, /** @export */ _embind_register_bool: __embind_register_bool, /** @export */ _embind_register_emval: __embind_register_emval, /** @export */ _embind_register_float: __embind_register_float, /** @export */ _embind_register_integer: __embind_register_integer, /** @export */ _embind_register_memory_view: __embind_register_memory_view, /** @export */ _embind_register_std_string: __embind_register_std_string, /** @export */ _embind_register_std_wstring: __embind_register_std_wstring, /** @export */ _embind_register_void: __embind_register_void, /** @export */ _emscripten_init_main_thread_js: __emscripten_init_main_thread_js, /** @export */ _emscripten_notify_mailbox_postmessage: __emscripten_notify_mailbox_postmessage, /** @export */ _emscripten_receive_on_main_thread_js: __emscripten_receive_on_main_thread_js, /** @export */ _emscripten_runtime_keepalive_clear: __emscripten_runtime_keepalive_clear, /** @export */ _emscripten_thread_cleanup: __emscripten_thread_cleanup, /** @export */ _emscripten_thread_mailbox_await: __emscripten_thread_mailbox_await, /** @export */ _emscripten_thread_set_strongref: __emscripten_thread_set_strongref, /** @export */ _emscripten_throw_longjmp: __emscripten_throw_longjmp, /** @export */ _gmtime_js: __gmtime_js, /** @export */ _localtime_js: __localtime_js, /** @export */ _mktime_js: __mktime_js, /** @export */ _mmap_js: __mmap_js, /** @export */ _munmap_js: __munmap_js, /** @export */ _setitimer_js: __setitimer_js, /** @export */ _tzset_js: __tzset_js, /** @export */ clock_time_get: _clock_time_get, /** @export */ emscripten_asm_const_int: _emscripten_asm_const_int, /** @export */ emscripten_check_blocking_allowed: _emscripten_check_blocking_allowed, /** @export */ emscripten_date_now: _emscripten_date_now, /** @export */ emscripten_exit_with_live_runtime: _emscripten_exit_with_live_runtime, /** @export */ emscripten_fetch_free: _emscripten_fetch_free, /** @export */ emscripten_get_heap_max: _emscripten_get_heap_max, /** @export */ emscripten_get_now: _emscripten_get_now, /** @export */ emscripten_num_logical_cores: _emscripten_num_logical_cores, /** @export */ emscripten_resize_heap: _emscripten_resize_heap, /** @export */ emscripten_runtime_keepalive_check: _emscripten_runtime_keepalive_check, /** @export */ emscripten_set_canvas_element_size: _emscripten_set_canvas_element_size, /** @export */ emscripten_start_fetch: _emscripten_start_fetch, /** @export */ emscripten_webgl_create_context: _emscripten_webgl_create_context, /** @export */ emscripten_webgl_destroy_context: _emscripten_webgl_destroy_context, /** @export */ emscripten_webgl_make_context_current: _emscripten_webgl_make_context_current, /** @export */ environ_get: _environ_get, /** @export */ environ_sizes_get: _environ_sizes_get, /** @export */ exit: _exit, /** @export */ fd_close: _fd_close, /** @export */ fd_fdstat_get: _fd_fdstat_get, /** @export */ fd_read: _fd_read, /** @export */ fd_seek: _fd_seek, /** @export */ fd_write: _fd_write, /** @export */ glActiveTexture: _glActiveTexture, /** @export */ glAttachShader: _glAttachShader, /** @export */ glBindBuffer: _glBindBuffer, /** @export */ glBindFramebuffer: _glBindFramebuffer, /** @export */ glBindTexture: _glBindTexture, /** @export */ glBlendFunc: _glBlendFunc, /** @export */ glBufferData: _glBufferData, /** @export */ glClear: _glClear, /** @export */ glClearColor: _glClearColor, /** @export */ glCompileShader: _glCompileShader, /** @export */ glCreateProgram: _glCreateProgram, /** @export */ glCreateShader: _glCreateShader, /** @export */ glDeleteFramebuffers: _glDeleteFramebuffers, /** @export */ glDeleteProgram: _glDeleteProgram, /** @export */ glDeleteTextures: _glDeleteTextures, /** @export */ glDepthFunc: _glDepthFunc, /** @export */ glDisable: _glDisable, /** @export */ glDrawArrays: _glDrawArrays, /** @export */ glEnable: _glEnable, /** @export */ glEnableVertexAttribArray: _glEnableVertexAttribArray, /** @export */ glFramebufferTexture2D: _glFramebufferTexture2D, /** @export */ glGenBuffers: _glGenBuffers, /** @export */ glGenFramebuffers: _glGenFramebuffers, /** @export */ glGenTextures: _glGenTextures, /** @export */ glGetAttribLocation: _glGetAttribLocation, /** @export */ glGetError: _glGetError, /** @export */ glGetProgramiv: _glGetProgramiv, /** @export */ glGetShaderInfoLog: _glGetShaderInfoLog, /** @export */ glGetShaderiv: _glGetShaderiv, /** @export */ glGetUniformLocation: _glGetUniformLocation, /** @export */ glLinkProgram: _glLinkProgram, /** @export */ glShaderSource: _glShaderSource, /** @export */ glTexImage2D: _glTexImage2D, /** @export */ glTexParameterf: _glTexParameterf, /** @export */ glTexParameteri: _glTexParameteri, /** @export */ glUniform1i: _glUniform1i, /** @export */ glUniformMatrix4fv: _glUniformMatrix4fv, /** @export */ glUseProgram: _glUseProgram, /** @export */ glVertexAttribPointer: _glVertexAttribPointer, /** @export */ glViewport: _glViewport, /** @export */ invoke_iii, /** @export */ invoke_iiii, /** @export */ invoke_iiiii, /** @export */ memory: wasmMemory, /** @export */ proc_exit: _proc_exit }; } var wasmExports = await createWasm(); var ___wasm_call_ctors = createExportWrapper("__wasm_call_ctors", 0); var _malloc = Module["_malloc"] = createExportWrapper("malloc", 1); var _free = Module["_free"] = createExportWrapper("free", 1); var _bind_demuxer_callback = Module["_bind_demuxer_callback"] = createExportWrapper("bind_demuxer_callback", 10); var _init_demuxer_ctx = Module["_init_demuxer_ctx"] = createExportWrapper("init_demuxer_ctx", 0); var _release_demuxer_ctx = Module["_release_demuxer_ctx"] = createExportWrapper("release_demuxer_ctx", 1); var _push_buffer = Module["_push_buffer"] = createExportWrapper("push_buffer", 4); var _push_probe = Module["_push_probe"] = createExportWrapper("push_probe", 2); var _fetch_done_buffer = Module["_fetch_done_buffer"] = createExportWrapper("fetch_done_buffer", 1); var _demuxer_video_pkt = Module["_demuxer_video_pkt"] = createExportWrapper("demuxer_video_pkt", 3); var _ffdemuxer_set_read_frame_multiple_times = Module["_ffdemuxer_set_read_frame_multiple_times"] = createExportWrapper("ffdemuxer_set_read_frame_multiple_times", 2); var _ffdemuxer_set_mode_live = Module["_ffdemuxer_set_mode_live"] = createExportWrapper("ffdemuxer_set_mode_live", 1); var _ffdemuxer_set_format_hint = Module["_ffdemuxer_set_format_hint"] = createExportWrapper("ffdemuxer_set_format_hint", 2); var _test_call = Module["_test_call"] = createExportWrapper("test_call", 1); var _init_webcodec_ctx = Module["_init_webcodec_ctx"] = createExportWrapper("init_webcodec_ctx", 0); var _release_webcodec_ctx = Module["_release_webcodec_ctx"] = createExportWrapper("release_webcodec_ctx", 1); var _wcodec_gpu_memory_info = Module["_wcodec_gpu_memory_info"] = createExportWrapper("wcodec_gpu_memory_info", 1); var _wcodec_init_gl_cb = Module["_wcodec_init_gl_cb"] = createExportWrapper("wcodec_init_gl_cb", 2); var _wcodec_create_data_buffer = Module["_wcodec_create_data_buffer"] = createExportWrapper("wcodec_create_data_buffer", 2); var _wcodec_feed_data = Module["_wcodec_feed_data"] = createExportWrapper("wcodec_feed_data", 5); var _wcodec_reset_decoder_status = Module["_wcodec_reset_decoder_status"] = createExportWrapper("wcodec_reset_decoder_status", 1); var _wcodec_mark_keyframe_started = Module["_wcodec_mark_keyframe_started"] = createExportWrapper("wcodec_mark_keyframe_started", 1); var _free_buffer = Module["_free_buffer"] = createExportWrapper("free_buffer", 2); var _wcodec_get_frame_queue_length = Module["_wcodec_get_frame_queue_length"] = createExportWrapper("wcodec_get_frame_queue_length", 1); var _wcodec_render_frame_from_queue = Module["_wcodec_render_frame_from_queue"] = createExportWrapper("wcodec_render_frame_from_queue", 1); var _wcodec_first_cache_pts_ms = Module["_wcodec_first_cache_pts_ms"] = createExportWrapper("wcodec_first_cache_pts_ms", 1); var _wcodec_pop_cache = Module["_wcodec_pop_cache"] = createExportWrapper("wcodec_pop_cache", 1); var _wcodec_clean_tex_queue = Module["_wcodec_clean_tex_queue"] = createExportWrapper("wcodec_clean_tex_queue", 1); var _init_ffdecoder_ctx = Module["_init_ffdecoder_ctx"] = createExportWrapper("init_ffdecoder_ctx", 0); var _release_ffdecoder_ctx = Module["_release_ffdecoder_ctx"] = createExportWrapper("release_ffdecoder_ctx", 1); var _ffdecoder_set_video_decoder = Module["_ffdecoder_set_video_decoder"] = createExportWrapper("ffdecoder_set_video_decoder", 4); var _ffdecoder_set_video_decoder_with_extra = Module["_ffdecoder_set_video_decoder_with_extra"] = createExportWrapper("ffdecoder_set_video_decoder_with_extra", 6); var _ffdecoder_set_audio_decoder = Module["_ffdecoder_set_audio_decoder"] = createExportWrapper("ffdecoder_set_audio_decoder", 2); var _ffdecoder_decode_video_frame = Module["_ffdecoder_decode_video_frame"] = createExportWrapper("ffdecoder_decode_video_frame", 6); var _ffdecoder_decode_audio_frame = Module["_ffdecoder_decode_audio_frame"] = createExportWrapper("ffdecoder_decode_audio_frame", 6); var _ffdecoder_bind_callback = Module["_ffdecoder_bind_callback"] = createExportWrapper("ffdecoder_bind_callback", 3); var _ffdecoder_seek_set = Module["_ffdecoder_seek_set"] = createExportWrapper("ffdecoder_seek_set", 2); var _init_cylopengl_ctx = Module["_init_cylopengl_ctx"] = createExportWrapper("init_cylopengl_ctx", 0); var _cylopengl_create_context = Module["_cylopengl_create_context"] = createExportWrapper("cylopengl_create_context", 5); var _cylopengl_create_shader_algorithm = Module["_cylopengl_create_shader_algorithm"] = createExportWrapper("cylopengl_create_shader_algorithm", 4); var _cylopengl_release_renderer_source = Module["_cylopengl_release_renderer_source"] = createExportWrapper("cylopengl_release_renderer_source", 0); var _cylopengl_change_viewport = Module["_cylopengl_change_viewport"] = createExportWrapper("cylopengl_change_viewport", 2); var _cylopengl_is_init = Module["_cylopengl_is_init"] = createExportWrapper("cylopengl_is_init", 0); var _cylopengl_install_text_object = Module["_cylopengl_install_text_object"] = createExportWrapper("cylopengl_install_text_object", 1); var _cylopengl_render_text = Module["_cylopengl_render_text"] = createExportWrapper("cylopengl_render_text", 2); var _cylopengl_request_font_text_style_file = Module["_cylopengl_request_font_text_style_file"] = createExportWrapper("cylopengl_request_font_text_style_file", 1); var _cylopengl_append_cache_texture = Module["_cylopengl_append_cache_texture"] = createExportWrapper("cylopengl_append_cache_texture", 3); var _cylopengl_get_cache_length = Module["_cylopengl_get_cache_length"] = createExportWrapper("cylopengl_get_cache_length", 0); var _cylopengl_clean_cache_queue = Module["_cylopengl_clean_cache_queue"] = createExportWrapper("cylopengl_clean_cache_queue", 0); var _cylopengl_render_cache_texture = Module["_cylopengl_render_cache_texture"] = createExportWrapper("cylopengl_render_cache_texture", 0); var _cylopengl_render_test_rgba = Module["_cylopengl_render_test_rgba"] = createExportWrapper("cylopengl_render_test_rgba", 4); var _cylopengl_append_cache_yuv420p = Module["_cylopengl_append_cache_yuv420p"] = createExportWrapper("cylopengl_append_cache_yuv420p", 9); var _cylopengl_get_cache_yuv_length = Module["_cylopengl_get_cache_yuv_length"] = createExportWrapper("cylopengl_get_cache_yuv_length", 0); var _cylopengl_render_cache_yuv420p = Module["_cylopengl_render_cache_yuv420p"] = createExportWrapper("cylopengl_render_cache_yuv420p", 0); var _cylopengl_first_cache_pts_ms = Module["_cylopengl_first_cache_pts_ms"] = createExportWrapper("cylopengl_first_cache_pts_ms", 0); var _cylopengl_pop_cache_yuv420p = Module["_cylopengl_pop_cache_yuv420p"] = createExportWrapper("cylopengl_pop_cache_yuv420p", 0); var _cylopengl_render_test_yuv420p = Module["_cylopengl_render_test_yuv420p"] = createExportWrapper("cylopengl_render_test_yuv420p", 8); var _main = Module["_main"] = createExportWrapper("main", 2); var ___getTypeName = createExportWrapper("__getTypeName", 1); var __embind_initialize_bindings = createExportWrapper("_embind_initialize_bindings", 0); var __emscripten_tls_init = createExportWrapper("_emscripten_tls_init", 0); var _pthread_self = () => (_pthread_self = wasmExports["pthread_self"])(); var _emscripten_builtin_memalign = createExportWrapper("emscripten_builtin_memalign", 2); var __emscripten_proxy_main = Module["__emscripten_proxy_main"] = createExportWrapper("_emscripten_proxy_main", 2); var _emscripten_stack_get_base = () => (_emscripten_stack_get_base = wasmExports["emscripten_stack_get_base"])(); var _emscripten_stack_get_end = () => (_emscripten_stack_get_end = wasmExports["emscripten_stack_get_end"])(); var __emscripten_set_offscreencanvas_size_on_thread = createExportWrapper("_emscripten_set_offscreencanvas_size_on_thread", 4); var __emscripten_thread_init = createExportWrapper("_emscripten_thread_init", 6); var __emscripten_thread_crashed = createExportWrapper("_emscripten_thread_crashed", 0); var _fflush = createExportWrapper("fflush", 1); var __emscripten_run_on_main_thread_js = createExportWrapper("_emscripten_run_on_main_thread_js", 5); var __emscripten_thread_free_data = createExportWrapper("_emscripten_thread_free_data", 1); var __emscripten_thread_exit = createExportWrapper("_emscripten_thread_exit", 1); var __emscripten_timeout = createExportWrapper("_emscripten_timeout", 2); var _strerror = createExportWrapper("strerror", 1); var __emscripten_check_mailbox = createExportWrapper("_emscripten_check_mailbox", 0); var _setThrew = createExportWrapper("setThrew", 2); var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports["emscripten_stack_init"])(); var _emscripten_stack_set_limits = (a0, a1) => (_emscripten_stack_set_limits = wasmExports["emscripten_stack_set_limits"])(a0, a1); var _emscripten_stack_get_free = () => (_emscripten_stack_get_free = wasmExports["emscripten_stack_get_free"])(); var __emscripten_stack_restore = a0 => (__emscripten_stack_restore = wasmExports["_emscripten_stack_restore"])(a0); var __emscripten_stack_alloc = a0 => (__emscripten_stack_alloc = wasmExports["_emscripten_stack_alloc"])(a0); var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports["emscripten_stack_get_current"])(); var ___cxa_increment_exception_refcount = createExportWrapper("__cxa_increment_exception_refcount", 1); var ___set_stack_limits = Module["___set_stack_limits"] = createExportWrapper("__set_stack_limits", 2); var dynCall_ii = Module["dynCall_ii"] = createExportWrapper("dynCall_ii", 2); var dynCall_iii = Module["dynCall_iii"] = createExportWrapper("dynCall_iii", 3); var dynCall_iiii = Module["dynCall_iiii"] = createExportWrapper("dynCall_iiii", 4); var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji", 4); var dynCall_jiiji = Module["dynCall_jiiji"] = createExportWrapper("dynCall_jiiji", 5); var dynCall_iiiji = Module["dynCall_iiiji"] = createExportWrapper("dynCall_iiiji", 5); var dynCall_v = Module["dynCall_v"] = createExportWrapper("dynCall_v", 1); var dynCall_viiiiii = Module["dynCall_viiiiii"] = createExportWrapper("dynCall_viiiiii", 7); var dynCall_vi = Module["dynCall_vi"] = createExportWrapper("dynCall_vi", 2); var dynCall_iiiii = Module["dynCall_iiiii"] = createExportWrapper("dynCall_iiiii", 5); var dynCall_jiiij = Module["dynCall_jiiij"] = createExportWrapper("dynCall_jiiij", 5); var dynCall_viii = Module["dynCall_viii"] = createExportWrapper("dynCall_viii", 4); var dynCall_iiiiij = Module["dynCall_iiiiij"] = createExportWrapper("dynCall_iiiiij", 6); var dynCall_vii = Module["dynCall_vii"] = createExportWrapper("dynCall_vii", 3); var dynCall_iiiiii = Module["dynCall_iiiiii"] = createExportWrapper("dynCall_iiiiii", 6); var dynCall_viiiii = Module["dynCall_viiiii"] = createExportWrapper("dynCall_viiiii", 6); var dynCall_viiiiiifi = Module["dynCall_viiiiiifi"] = createExportWrapper("dynCall_viiiiiifi", 9); var dynCall_viiii = Module["dynCall_viiii"] = createExportWrapper("dynCall_viiii", 5); var dynCall_iiiiiii = Module["dynCall_iiiiiii"] = createExportWrapper("dynCall_iiiiiii", 7); var dynCall_viiiiiiiiiiiiii = Module["dynCall_viiiiiiiiiiiiii"] = createExportWrapper("dynCall_viiiiiiiiiiiiii", 15); var dynCall_viiiiiiiii = Module["dynCall_viiiiiiiii"] = createExportWrapper("dynCall_viiiiiiiii", 10); var dynCall_viiiiiiiiiii = Module["dynCall_viiiiiiiiiii"] = createExportWrapper("dynCall_viiiiiiiiiii", 12); var dynCall_viiiiiiii = Module["dynCall_viiiiiiii"] = createExportWrapper("dynCall_viiiiiiii", 9); var dynCall_viiiiiii = Module["dynCall_viiiiiii"] = createExportWrapper("dynCall_viiiiiii", 8); var dynCall_viiiiiiiiiiii = Module["dynCall_viiiiiiiiiiii"] = createExportWrapper("dynCall_viiiiiiiiiiii", 13); var dynCall_viiiifii = Module["dynCall_viiiifii"] = createExportWrapper("dynCall_viiiifii", 8); var dynCall_fii = Module["dynCall_fii"] = createExportWrapper("dynCall_fii", 3); var dynCall_viiiiiiiiii = Module["dynCall_viiiiiiiiii"] = createExportWrapper("dynCall_viiiiiiiiii", 11); var dynCall_viiijj = Module["dynCall_viiijj"] = createExportWrapper("dynCall_viiijj", 6); var dynCall_iiiiiiidiiddii = Module["dynCall_iiiiiiidiiddii"] = createExportWrapper("dynCall_iiiiiiidiiddii", 14); var dynCall_jij = Module["dynCall_jij"] = createExportWrapper("dynCall_jij", 3); var dynCall_jii = Module["dynCall_jii"] = createExportWrapper("dynCall_jii", 3); var dynCall_viifi = Module["dynCall_viifi"] = createExportWrapper("dynCall_viifi", 5); var dynCall_fiii = Module["dynCall_fiii"] = createExportWrapper("dynCall_fiii", 4); var dynCall_viidi = Module["dynCall_viidi"] = createExportWrapper("dynCall_viidi", 5); var dynCall_dd = Module["dynCall_dd"] = createExportWrapper("dynCall_dd", 2); var dynCall_iiiiiiiii = Module["dynCall_iiiiiiiii"] = createExportWrapper("dynCall_iiiiiiiii", 9); var dynCall_iidiiii = Module["dynCall_iidiiii"] = createExportWrapper("dynCall_iidiiii", 7); var dynCall_iiiiiiiiii = Module["dynCall_iiiiiiiiii"] = createExportWrapper("dynCall_iiiiiiiiii", 10); var dynCall_iiiiiiii = Module["dynCall_iiiiiiii"] = createExportWrapper("dynCall_iiiiiiii", 8); var dynCall_viijii = Module["dynCall_viijii"] = createExportWrapper("dynCall_viijii", 6); var dynCall_iiiiid = Module["dynCall_iiiiid"] = createExportWrapper("dynCall_iiiiid", 6); var dynCall_iiiiijj = Module["dynCall_iiiiijj"] = createExportWrapper("dynCall_iiiiijj", 7); var dynCall_iiiiiijj = Module["dynCall_iiiiiijj"] = createExportWrapper("dynCall_iiiiiijj", 8); var _asyncify_start_unwind = createExportWrapper("asyncify_start_unwind", 1); var _asyncify_stop_unwind = createExportWrapper("asyncify_stop_unwind", 0); var _asyncify_start_rewind = createExportWrapper("asyncify_start_rewind", 1); var _asyncify_stop_rewind = createExportWrapper("asyncify_stop_rewind", 0); var _ff_h264_cabac_tables = Module["_ff_h264_cabac_tables"] = 192984; function invoke_iii(index, a1, a2) { var sp = stackSave(); try { return dynCall_iii(index, a1, a2); } catch (e) { stackRestore(sp); if (e !== e + 0) throw e; _setThrew(1, 0); } } function invoke_iiiii(index, a1, a2, a3, a4) { var sp = stackSave(); try { return dynCall_iiiii(index, a1, a2, a3, a4); } catch (e) { stackRestore(sp); if (e !== e + 0) throw e; _setThrew(1, 0); } } function invoke_iiii(index, a1, a2, a3) { var sp = stackSave(); try { return dynCall_iiii(index, a1, a2, a3); } catch (e) { stackRestore(sp); if (e !== e + 0) throw e; _setThrew(1, 0); } } // include: postamble.js // === Auto-generated postamble setup entry stuff === Module["addRunDependency"] = addRunDependency; Module["removeRunDependency"] = removeRunDependency; Module["ccall"] = ccall; Module["cwrap"] = cwrap; Module["addFunction"] = addFunction; Module["removeFunction"] = removeFunction; Module["UTF8ToString"] = UTF8ToString; Module["stringToUTF8"] = stringToUTF8; Module["FS_createPreloadedFile"] = FS_createPreloadedFile; Module["FS_unlink"] = FS_unlink; Module["FS_createPath"] = FS_createPath; Module["FS_createDevice"] = FS_createDevice; Module["FS"] = FS; Module["FS_createDataFile"] = FS_createDataFile; Module["FS_createLazyFile"] = FS_createLazyFile; Module["allocateUTF8"] = allocateUTF8; var missingLibrarySymbols = [ "writeI53ToI64Clamped", "writeI53ToI64Signaling", "writeI53ToU64Clamped", "writeI53ToU64Signaling", "convertI32PairToI53", "convertI32PairToI53Checked", "convertU32PairToI53", "getTempRet0", "setTempRet0", "inetPton4", "inetNtop4", "inetPton6", "inetNtop6", "readSockaddr", "writeSockaddr", "emscriptenLog", "runMainThreadEmAsm", "listenOnce", "autoResumeAudioContext", "dynCallLegacy", "getDynCaller", "dynCall", "asmjsMangle", "getNativeTypeSize", "addOnInit", "addOnPostCtor", "addOnPreMain", "addOnExit", "STACK_SIZE", "STACK_ALIGN", "POINTER_SIZE", "ASSERTIONS", "reallyNegative", "unSign", "strLen", "reSign", "formatString", "intArrayToString", "AsciiToString", "registerKeyEventCallback", "findEventTarget", "getBoundingClientRect", "fillMouseEventData", "registerMouseEventCallback", "registerWheelEventCallback", "registerUiEventCallback", "registerFocusEventCallback", "fillDeviceOrientationEventData", "registerDeviceOrientationEventCallback", "fillDeviceMotionEventData", "registerDeviceMotionEventCallback", "screenOrientation", "fillOrientationChangeEventData", "registerOrientationChangeEventCallback", "fillFullscreenChangeEventData", "registerFullscreenChangeEventCallback", "JSEvents_requestFullscreen", "JSEvents_resizeCanvasForFullscreen", "registerRestoreOldStyle", "hideEverythingExceptGivenElement", "restoreHiddenElements", "setLetterbox", "softFullscreenResizeWebGLRenderTarget", "doRequestFullscreen", "fillPointerlockChangeEventData", "registerPointerlockChangeEventCallback", "registerPointerlockErrorEventCallback", "requestPointerLock", "fillVisibilityChangeEventData", "registerVisibilityChangeEventCallback", "registerTouchEventCallback", "fillGamepadEventData", "registerGamepadEventCallback", "registerBeforeUnloadEventCallback", "fillBatteryEventData", "battery", "registerBatteryEventCallback", "setCanvasElementSize", "getCanvasSizeCallingThread", "getCanvasSizeMainThread", "getCanvasElementSize", "jsStackTrace", "getCallstack", "convertPCtoSourceLocation", "wasiRightsToMuslOFlags", "wasiOFlagsToMuslOFlags", "safeSetTimeout", "setImmediateWrapped", "safeRequestAnimationFrame", "clearImmediateWrapped", "registerPostMainLoop", "getPromise", "makePromise", "idsToPromises", "makePromiseCallback", "findMatchingCatch", "Browser_asyncPrepareDataCounter", "arraySum", "addDays", "getSocketFromFD", "getSocketAddress", "FS_mkdirTree", "_setNetworkCallback", "emscriptenWebGLGet", "emscriptenWebGLGetUniform", "emscriptenWebGLGetVertexAttrib", "__glGetActiveAttribOrUniform", "writeGLArray", "emscripten_webgl_destroy_context_before_on_calling_thread", "registerWebGlEventCallback", "GLFW_Window", "emscriptenWebGLGetIndexed", "ALLOC_NORMAL", "ALLOC_STACK", "allocate", "writeStringToMemory", "writeAsciiToMemory", "demangle", "stackTrace", "throwInternalError", "whenDependentTypesAreResolved", "getTypeName", "getFunctionName", "getFunctionArgsName", "heap32VectorToArray", "requireRegisteredType", "usesDestructorStack", "createJsInvokerSignature", "checkArgCount", "getRequiredArgCount", "createJsInvoker", "UnboundTypeError", "PureVirtualError", "throwUnboundTypeError", "ensureOverloadTable", "exposePublicSymbol", "replacePublicSymbol", "createNamedFunction", "getBasestPointer", "registerInheritedInstance", "unregisterInheritedInstance", "getInheritedInstance", "getInheritedInstanceCount", "getLiveInheritedInstances", "enumReadValueFromPointer", "runDestructors", "craftInvokerFunction", "embind__requireFunction", "genericPointerToWireType", "constNoSmartPtrRawPointerToWireType", "nonConstNoSmartPtrRawPointerToWireType", "init_RegisteredPointer", "RegisteredPointer", "RegisteredPointer_fromWireType", "runDestructor", "releaseClassHandle", "detachFinalizer", "attachFinalizer", "makeClassHandle", "init_ClassHandle", "ClassHandle", "throwInstanceAlreadyDeleted", "flushPendingDeletes", "setDelayFunction", "RegisteredClass", "shallowCopyInternalPointer", "downcastPointer", "upcastPointer", "validateThis", "char_0", "char_9", "makeLegalFunctionName", "getStringOrSymbol", "emval_get_global", "emval_returnValue", "emval_lookupTypes", "emval_addMethodCaller" ]; missingLibrarySymbols.forEach(missingLibrarySymbol); var unexportedSymbols = [ "run", "out", "err", "callMain", "abort", "wasmMemory", "wasmExports", "GROWABLE_HEAP_I8", "GROWABLE_HEAP_U8", "GROWABLE_HEAP_I16", "GROWABLE_HEAP_U16", "GROWABLE_HEAP_I32", "GROWABLE_HEAP_U32", "GROWABLE_HEAP_F32", "GROWABLE_HEAP_F64", "writeStackCookie", "checkStackCookie", "writeI53ToI64", "readI53FromI64", "readI53FromU64", "INT53_MAX", "INT53_MIN", "bigintToI53Checked", "stackSave", "stackRestore", "stackAlloc", "ptrToString", "zeroMemory", "exitJS", "getHeapMax", "growMemory", "ENV", "setStackLimits", "ERRNO_CODES", "strError", "DNS", "Protocols", "Sockets", "timers", "warnOnce", "readEmAsmArgsArray", "readEmAsmArgs", "runEmAsmFunction", "jstoi_q", "jstoi_s", "getExecutableName", "handleException", "keepRuntimeAlive", "runtimeKeepalivePush", "runtimeKeepalivePop", "callUserCallback", "maybeExit", "asyncLoad", "alignMemory", "mmapAlloc", "HandleAllocator", "wasmTable", "noExitRuntime", "addOnPreRun", "addOnPostRun", "getCFunc", "uleb128Encode", "sigToWasmTypes", "generateFuncType", "convertJsFunctionToWasm", "freeTableIndexes", "functionsInTableMap", "getEmptyTableSlot", "updateTableMap", "getFunctionAddress", "setValue", "getValue", "PATH", "PATH_FS", "UTF8Decoder", "UTF8ArrayToString", "stringToUTF8Array", "lengthBytesUTF8", "intArrayFromString", "stringToAscii", "UTF16Decoder", "UTF16ToString", "stringToUTF16", "lengthBytesUTF16", "UTF32ToString", "stringToUTF32", "lengthBytesUTF32", "stringToNewUTF8", "stringToUTF8OnStack", "writeArrayToMemory", "JSEvents", "specialHTMLTargets", "maybeCStringToJsString", "findCanvasEventTarget", "currentFullscreenStrategy", "restoreOldWindowedStyle", "setCanvasElementSizeCallingThread", "setOffscreenCanvasSizeOnTargetThread", "setCanvasElementSizeMainThread", "UNWIND_CACHE", "ExitStatus", "getEnvStrings", "checkWasiClock", "doReadv", "doWritev", "initRandomFill", "randomFill", "emSetImmediate", "emClearImmediate_deps", "emClearImmediate", "registerPreMainLoop", "promiseMap", "uncaughtExceptionCount", "exceptionLast", "exceptionCaught", "ExceptionInfo", "Browser", "getPreloadedImageData__data", "wget", "MONTH_DAYS_REGULAR", "MONTH_DAYS_LEAP", "MONTH_DAYS_REGULAR_CUMULATIVE", "MONTH_DAYS_LEAP_CUMULATIVE", "isLeapYear", "ydayFromDate", "SYSCALLS", "preloadPlugins", "FS_modeStringToFlags", "FS_getMode", "FS_stdin_getChar_buffer", "FS_stdin_getChar", "FS_readFile", "MEMFS", "TTY", "PIPEFS", "SOCKFS", "tempFixedLengthArray", "miniTempWebGLFloatBuffers", "miniTempWebGLIntBuffers", "heapObjectForWebGLType", "toTypedArrayIndex", "webgl_enable_ANGLE_instanced_arrays", "webgl_enable_OES_vertex_array_object", "webgl_enable_WEBGL_draw_buffers", "webgl_enable_WEBGL_multi_draw", "webgl_enable_EXT_polygon_offset_clamp", "webgl_enable_EXT_clip_control", "webgl_enable_WEBGL_polygon_mode", "GL", "computeUnpackAlignedImageSize", "colorChannelsInGlTextureFormat", "emscriptenWebGLGetTexPixelData", "webglGetUniformLocation", "webglPrepareUniformLocationsBeforeFirstUse", "webglGetLeftBracePos", "AL", "GLUT", "EGL", "GLEW", "IDBStore", "runAndAbortIfError", "Asyncify", "Fibers", "SDL", "SDL_gfx", "GLFW", "webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance", "webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance", "allocateUTF8OnStack", "print", "printErr", "PThread", "terminateWorker", "cleanupThread", "registerTLSInit", "spawnThread", "exitOnMainThread", "proxyToMainThread", "proxiedJSCallArgs", "invokeEntryPoint", "checkMailbox", "InternalError", "BindingError", "throwBindingError", "registeredTypes", "awaitingDependencies", "typeDependencies", "tupleRegistrations", "structRegistrations", "sharedRegisterType", "embind_charCodes", "embind_init_charCodes", "readLatin1String", "GenericWireTypeSize", "EmValType", "EmValOptionalType", "embindRepr", "registeredInstances", "registeredPointers", "registerType", "integerReadValueFromPointer", "floatReadValueFromPointer", "readPointer", "finalizationRegistry", "detachFinalizer_deps", "deletionQueue", "delayFunction", "emval_freelist", "emval_handles", "emval_symbols", "init_emval", "count_emval_handles", "Emval", "emval_methodCallers", "reflectConstruct", "Fetch", "fetchDeleteCachedData", "fetchLoadCachedData", "fetchCacheData", "fetchXHR" ]; unexportedSymbols.forEach(unexportedRuntimeSymbol); var calledRun; function callMain() { assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); assert(typeof onPreRuns === "undefined" || onPreRuns.length == 0, "cannot call main when preRun functions remain to be called"); var entryFunction = __emscripten_proxy_main; // With PROXY_TO_PTHREAD make sure we keep the runtime alive until the // proxied main calls exit (see exitOnMainThread() for where Pop is called). runtimeKeepalivePush(); var argc = 0; var argv = 0; try { var ret = entryFunction(argc, argv); // if we're not running an evented main loop, it's time to exit exitJS(ret, /* implicit = */ true); return ret; } catch (e) { return handleException(e); } } function stackCheckInit() { // This is normally called automatically during __wasm_call_ctors but need to // get these values before even running any of the ctors so we call it redundantly // here. // See $establishStackSpace for the equivalent code that runs on a thread assert(!ENVIRONMENT_IS_PTHREAD); _emscripten_stack_init(); // TODO(sbc): Move writeStackCookie to native to to avoid this. writeStackCookie(); } function run() { if (runDependencies > 0) { dependenciesFulfilled = run; return; } if ((ENVIRONMENT_IS_PTHREAD)) { readyPromiseResolve(Module); initRuntime(); return; } stackCheckInit(); preRun(); // a preRun added a dependency, run will be called later if (runDependencies > 0) { dependenciesFulfilled = run; return; } 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 assert(!calledRun); calledRun = true; Module["calledRun"] = true; if (ABORT) return; initRuntime(); preMain(); readyPromiseResolve(Module); Module["onRuntimeInitialized"]?.(); consumedModuleProp("onRuntimeInitialized"); var noInitialRun = Module["noInitialRun"]; legacyModuleProp("noInitialRun", "noInitialRun"); if (!noInitialRun) callMain(); postRun(); } if (Module["setStatus"]) { Module["setStatus"]("Running..."); setTimeout(() => { setTimeout(() => Module["setStatus"](""), 1); doRun(); }, 1); } else { doRun(); } checkStackCookie(); } function checkUnflushedContent() { // Compiler settings do not allow exiting the runtime, so flushing // the streams is not possible. but in ASSERTIONS mode we check // if there was something to flush, and if so tell the user they // should request that the runtime be exitable. // Normally we would not even include flush() at all, but in ASSERTIONS // builds we do so just for this check, and here we see if there is any // content to flush, that is, we check if there would have been // something a non-ASSERTIONS build would have not seen. // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0 // mode (which has its own special function for this; otherwise, all // the code is inside libc) var oldOut = out; var oldErr = err; var has = false; out = err = x => { has = true; }; try { // it doesn't matter if it fails _fflush(0); // also flush in the JS FS layer [ "stdout", "stderr" ].forEach(name => { var info = FS.analyzePath("/dev/" + name); if (!info) return; var stream = info.object; var rdev = stream.rdev; var tty = TTY.ttys[rdev]; if (tty?.output?.length) { has = true; } }); } catch (e) {} out = oldOut; err = oldErr; if (has) { warnOnce("stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the Emscripten FAQ), or make sure to emit a newline when you printf etc."); } } if (Module["preInit"]) { if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ]; while (Module["preInit"].length > 0) { Module["preInit"].pop()(); } } consumedModuleProp("preInit"); run(); // end include: postamble.js // 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 and extern so it won't get minified. moduleRtn = readyPromise; // Assertion for attempting to access module properties on the incoming // moduleArg. In the past we used this object as the prototype of the module // and assigned properties to it, but now we return a distinct object. This // keeps the instance private until it is ready (i.e the promise has been // resolved). for (const prop of Object.keys(Module)) { if (!(prop in moduleArg)) { Object.defineProperty(moduleArg, prop, { configurable: true, get() { abort(`Access to module property ('${prop}') is no longer possible via the module constructor argument; Instead, use the result of the module constructor.`); } }); } } return moduleRtn; } ); })(); if (typeof exports === 'object' && typeof module === 'object') { module.exports = WasmModule; // This default export looks redundant, but it allows TS to import this // commonjs style module. module.exports.default = WasmModule; } else if (typeof define === 'function' && define['amd']) define([], () => WasmModule); var isPthread = globalThis.self?.name?.startsWith('em-pthread'); // When running as a pthread, construct a new instance on startup isPthread && WasmModule();