diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cc6fd6c96..8415380ca6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: submodules: true - uses: ./.github/actions/prepare-playground with: - node-version: 20 + node-version: 22 - run: node --expose-gc node_modules/nx/bin/nx affected --target=${{ matrix.target }} env: MYSQL_DATABASE: test_db diff --git a/packages/php-wasm/cli/src/main.ts b/packages/php-wasm/cli/src/main.ts index 06f509627c..7df6568d1c 100644 --- a/packages/php-wasm/cli/src/main.ts +++ b/packages/php-wasm/cli/src/main.ts @@ -11,7 +11,7 @@ import { } from '@php-wasm/universal'; import type { SupportedPHPVersion } from '@php-wasm/universal'; -import { FileLockManagerForNode } from '@php-wasm/node'; +import { FileLockManagerForNode, SyscallsForNode } from '@php-wasm/node'; import { PHP } from '@php-wasm/universal'; import { loadNodeRuntime, useHostFilesystem } from '@php-wasm/node'; import { startBridge } from '@php-wasm/xdebug-bridge'; @@ -91,6 +91,7 @@ ${process.argv[0]} ${process.execArgv.join(' ')} ${process.argv[1]} await loadNodeRuntime(phpVersion, { emscriptenOptions: { fileLockManager: new FileLockManagerForNode(), + syscalls: new SyscallsForNode(), processId: 1, ENV: { ...envVariables, diff --git a/packages/php-wasm/compile/php/Dockerfile b/packages/php-wasm/compile/php/Dockerfile index ab8d7331a0..4e1dce8b5e 100644 --- a/packages/php-wasm/compile/php/Dockerfile +++ b/packages/php-wasm/compile/php/Dockerfile @@ -2234,6 +2234,7 @@ RUN mkdir /root/output COPY ./php/phpwasm-emscripten-library.js /root/phpwasm-emscripten-library.js COPY ./php/phpwasm-emscripten-library-dynamic-linking.js /root/phpwasm-emscripten-library-dynamic-linking.js COPY ./php/phpwasm-emscripten-library-file-locking-for-node.js /root/phpwasm-emscripten-library-file-locking-for-node.js +COPY ./php/phpwasm-emscripten-library-dns-lookup-for-node.js /root/phpwasm-emscripten-library-dns-lookup-for-node.js COPY ./php/phpwasm-emscripten-library-known-undefined-functions.js /root/phpwasm-emscripten-library-known-undefined-functions.js ARG WITH_SOURCEMAPS ARG WITH_DEBUG @@ -2243,10 +2244,10 @@ RUN set -euxo pipefail; \ source /root/emsdk/emsdk_env.sh; \ if [ "$WITH_JSPI" = "yes" ]; then \ # Both imports and exports are required for inter-module communication with wrapped methods, e.g., wasm_recv. - export ASYNCIFY_FLAGS=" -s ASYNCIFY=2 -sSUPPORT_LONGJMP=wasm -fwasm-exceptions -sJSPI_IMPORTS=js_open_process,js_fd_read,js_waitpid,js_process_status,js_create_input_device,wasm_setsockopt,wasm_shutdown,wasm_close,wasm_recv,__syscall_fcntl64,js_flock,js_release_file_locks,js_waitpid -sJSPI_EXPORTS=php_wasm_init,wasm_sleep,wasm_read,emscripten_sleep,wasm_sapi_handle_request,wasm_sapi_request_shutdown,wasm_poll_socket,wrap_select,__wrap_select,select,php_pollfd_for,fflush,wasm_popen,wasm_read,wasm_php_exec,run_cli,wasm_recv,__wasm_call_ctors,__errno_location,__funcs_on_exit -s EXPORTED_RUNTIME_METHODS=HEAPU32,HEAPU8,ccall,PROXYFS,wasmExports "; \ + export ASYNCIFY_FLAGS=" -s ASYNCIFY=2 -sSUPPORT_LONGJMP=wasm -fwasm-exceptions -sJSPI_IMPORTS=js_open_process,js_fd_read,js_waitpid,js_process_status,js_create_input_device,wasm_setsockopt,wasm_shutdown,wasm_close,wasm_recv,__syscall_fcntl64,__emscripten_lookup_name,js_flock,js_release_file_locks,js_waitpid -sJSPI_EXPORTS=php_wasm_init,wasm_sleep,wasm_read,emscripten_sleep,wasm_sapi_handle_request,wasm_sapi_request_shutdown,wasm_poll_socket,wrap_select,__wrap_select,select,php_pollfd_for,fflush,wasm_popen,wasm_read,wasm_php_exec,run_cli,wasm_recv,__wasm_call_ctors,__errno_location,__funcs_on_exit -s EXPORTED_RUNTIME_METHODS=HEAPU32,HEAPU8,ccall,PROXYFS,wasmExports,UTF8ToString,lengthBytesUTF8,stringToUTF8 "; \ echo '#define PLAYGROUND_JSPI 1' > /root/php_wasm_asyncify.h; \ else \ - export ASYNCIFY_FLAGS=" -s ASYNCIFY=1 -s ASYNCIFY_IGNORE_INDIRECT=1 -s EXPORTED_RUNTIME_METHODS=HEAPU32,HEAPU8,ccall,PROXYFS,wasmExports $(cat /root/.emcc-php-asyncify-flags) "; \ + export ASYNCIFY_FLAGS=" -s ASYNCIFY=1 -s ASYNCIFY_IGNORE_INDIRECT=1 -s EXPORTED_RUNTIME_METHODS=HEAPU32,HEAPU8,ccall,PROXYFS,wasmExports,UTF8ToString,lengthBytesUTF8,stringToUTF8 $(cat /root/.emcc-php-asyncify-flags) "; \ echo '' > /root/php_wasm_asyncify.h; \ fi; \ export EXPORTED_FUNCTIONS=$'["_exit", \n\ @@ -2287,7 +2288,7 @@ RUN set -euxo pipefail; \ ENVIRONMENT="$EMSCRIPTEN_ENVIRONMENT"; \ if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \ PLATFORM_SPECIFIC_ARGS="$PLATFORM_SPECIFIC_ARGS -DPHP_WASM_FILE_LOCKING_SUPPORT=1"; \ - PLATFORM_SPECIFIC_ARGS="$PLATFORM_SPECIFIC_ARGS --js-library /root/phpwasm-emscripten-library-file-locking-for-node.js -Wl,--wrap=getpid"; \ + PLATFORM_SPECIFIC_ARGS="$PLATFORM_SPECIFIC_ARGS --js-library /root/phpwasm-emscripten-library-file-locking-for-node.js --js-library /root/phpwasm-emscripten-library-dns-lookup-for-node.js -Wl,--wrap=getpid"; \ else \ # Prevents "document is not defined" errors in Web Workers when using MAIN_MODULE. # By default, Emscripten assumes MAIN_MODULE runs only in a web environment, diff --git a/packages/php-wasm/compile/php/phpwasm-emscripten-library-dns-lookup-for-node.js b/packages/php-wasm/compile/php/phpwasm-emscripten-library-dns-lookup-for-node.js new file mode 100644 index 0000000000..1c11832b8c --- /dev/null +++ b/packages/php-wasm/compile/php/phpwasm-emscripten-library-dns-lookup-for-node.js @@ -0,0 +1,60 @@ +/** + * Override Emscripten's _emscripten_lookup_name to resolve hostnames with + * Node.js DNS when running in the Node environment. + * + * JSPI vs Asyncify + * ---------------- + * In JSPI builds (ASYNCIFY == 2) we can await the async dns.lookup() Promise. + * In classic Asyncify builds (ASYNCIFY == 1) we block the calling thread with + * Atomics.wait on a SharedArrayBuffer to synchronise the async callback into a + * synchronous return value without rewinding the stack. + */ +'use strict'; + +const LibraryForNodeDnsLookup = { + // $-prefixed deps pull the runtime helpers into scope. + __emscripten_lookup_name__deps: ['$DNS', '$inetPton4', '$UTF8ToString'], + __emscripten_lookup_name__sig: 'ip', + // Ensure our implementation is used even if the core library defines one. + __emscripten_lookup_name__postset: + 'const original__emscripten_lookup_name = __emscripten_lookup_name; if (typeof __emscripten_lookup_name !== "undefined") { __emscripten_lookup_name = ___emscripten_lookup_name; }' +#if ASYNCIFY == 2 + + '___emscripten_lookup_name.isAsync = true;' +#endif + , + __emscripten_lookup_name: function __emscripten_lookup_name(namePtr) { +#if ASYNCIFY == 2 + return Asyncify.handleAsync(async () => { +#endif + if ( ! ENVIRONMENT_IS_NODE ) { + return original__emscripten_lookup_name(namePtr); + } + if ( ! PHPLoader.syscalls ) { + return original__emscripten_lookup_name(namePtr); + } + + const hostname = UTF8ToString(namePtr); + + let ipString = ''; + try { + ipString = ( +#if ASYNCIFY == 2 + await Promise.resolve( +#endif + PHPLoader.syscalls.gethostbyname(hostname) +#if ASYNCIFY == 2 + ) +#endif + ); + } catch (e) { + // Fall through to the default synthetic mapping if native DNS fails. + } + + return inetPton4(ipString); +#if ASYNCIFY == 2 + }); +#endif + }, +}; + +mergeInto(LibraryManager.library, LibraryForNodeDnsLookup); diff --git a/packages/php-wasm/node/asyncify/7_2_34/php_7_2.wasm b/packages/php-wasm/node/asyncify/7_2_34/php_7_2.wasm index a38ad4ece8..6781680898 100755 Binary files a/packages/php-wasm/node/asyncify/7_2_34/php_7_2.wasm and b/packages/php-wasm/node/asyncify/7_2_34/php_7_2.wasm differ diff --git a/packages/php-wasm/node/asyncify/7_3_33/php_7_3.wasm b/packages/php-wasm/node/asyncify/7_3_33/php_7_3.wasm index b1fafc2116..6befa28126 100755 Binary files a/packages/php-wasm/node/asyncify/7_3_33/php_7_3.wasm and b/packages/php-wasm/node/asyncify/7_3_33/php_7_3.wasm differ diff --git a/packages/php-wasm/node/asyncify/7_4_33/php_7_4.wasm b/packages/php-wasm/node/asyncify/7_4_33/php_7_4.wasm index bda2b02c41..7130b2530a 100755 Binary files a/packages/php-wasm/node/asyncify/7_4_33/php_7_4.wasm and b/packages/php-wasm/node/asyncify/7_4_33/php_7_4.wasm differ diff --git a/packages/php-wasm/node/asyncify/8_0_30/php_8_0.wasm b/packages/php-wasm/node/asyncify/8_0_30/php_8_0.wasm index 8be794495b..78060ffe63 100755 Binary files a/packages/php-wasm/node/asyncify/8_0_30/php_8_0.wasm and b/packages/php-wasm/node/asyncify/8_0_30/php_8_0.wasm differ diff --git a/packages/php-wasm/node/asyncify/8_1_33/php_8_1.wasm b/packages/php-wasm/node/asyncify/8_1_33/php_8_1.wasm index 0036d2328b..dc6b01483c 100755 Binary files a/packages/php-wasm/node/asyncify/8_1_33/php_8_1.wasm and b/packages/php-wasm/node/asyncify/8_1_33/php_8_1.wasm differ diff --git a/packages/php-wasm/node/asyncify/8_2_29/php_8_2.wasm b/packages/php-wasm/node/asyncify/8_2_29/php_8_2.wasm index b596890576..3b9aa5b0d0 100755 Binary files a/packages/php-wasm/node/asyncify/8_2_29/php_8_2.wasm and b/packages/php-wasm/node/asyncify/8_2_29/php_8_2.wasm differ diff --git a/packages/php-wasm/node/asyncify/8_3_28/php_8_3.wasm b/packages/php-wasm/node/asyncify/8_3_28/php_8_3.wasm index d72fdbfbaa..f393a8083a 100755 Binary files a/packages/php-wasm/node/asyncify/8_3_28/php_8_3.wasm and b/packages/php-wasm/node/asyncify/8_3_28/php_8_3.wasm differ diff --git a/packages/php-wasm/node/asyncify/8_4_15/php_8_4.wasm b/packages/php-wasm/node/asyncify/8_4_15/php_8_4.wasm index bd4ef24f89..0a4c2a074b 100755 Binary files a/packages/php-wasm/node/asyncify/8_4_15/php_8_4.wasm and b/packages/php-wasm/node/asyncify/8_4_15/php_8_4.wasm differ diff --git a/packages/php-wasm/node/asyncify/php_7_2.js b/packages/php-wasm/node/asyncify/php_7_2.js index 8105650b63..a4c7cf84d2 100644 --- a/packages/php-wasm/node/asyncify/php_7_2.js +++ b/packages/php-wasm/node/asyncify/php_7_2.js @@ -7,32019 +7,35460 @@ const require = createRequire(import.meta.url); import path from 'path'; const dependencyFilename = path.join(__dirname, '7_2_34', 'php_7_2.wasm'); -export { dependencyFilename }; -export const dependenciesTotalSize = 22632176; +export { dependencyFilename }; +export const dependenciesTotalSize = 22631883; const phpVersionString = '7.2.34'; export function init(RuntimeName, PHPLoader) { - // The rest of the code comes from the built php.js file and esm-suffix.js -// include: shell.js -// include: minimum_runtime_check.js -// end include: minimum_runtime_check.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 = typeof PHPLoader != 'undefined' ? PHPLoader : {}; - -// Determine the runtime environment we are in. You can customize this by -// setting the ENVIRONMENT setting at compile time (see settings.js). - -var ENVIRONMENT_IS_WEB=RuntimeName==="WEB"; -var ENVIRONMENT_IS_WORKER=RuntimeName==="WORKER"; -var ENVIRONMENT_IS_NODE=RuntimeName==="NODE"; -var ENVIRONMENT_IS_SHELL=RuntimeName==="SHELL"; - -// --pre-jses are emitted after the Module integration code, so that they can -// refer to Module (if they choose; they can also define Module) - - -var arguments_ = []; -var thisProgram = './this.program'; -var quit_ = (status, toThrow) => { - throw toThrow; -}; - -var _scriptName; - -if (typeof __filename != 'undefined') { // Node - _scriptName = __filename; -} else - /*no-op*/{} - -// `/` 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; -} + // The rest of the code comes from the built php.js file and esm-suffix.js + // include: shell.js + // include: minimum_runtime_check.js + // end include: minimum_runtime_check.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 = typeof PHPLoader != 'undefined' ? PHPLoader : {}; + + // Determine the runtime environment we are in. You can customize this by + // setting the ENVIRONMENT setting at compile time (see settings.js). + + var ENVIRONMENT_IS_WEB = RuntimeName === 'WEB'; + var ENVIRONMENT_IS_WORKER = RuntimeName === 'WORKER'; + var ENVIRONMENT_IS_NODE = RuntimeName === 'NODE'; + var ENVIRONMENT_IS_SHELL = RuntimeName === 'SHELL'; + + // --pre-jses are emitted after the Module integration code, so that they can + // refer to Module (if they choose; they can also define Module) + + var arguments_ = []; + var thisProgram = './this.program'; + var quit_ = (status, toThrow) => { + throw toThrow; + }; + + var _scriptName; + + if (typeof __filename != 'undefined') { + // Node + _scriptName = __filename; + } else /*no-op*/ { + } -// Hooks that are implemented differently in different runtime environments. -var readAsync, readBinary; - -if (ENVIRONMENT_IS_NODE) { - - // These modules will usually be used on Node.js. Load them eagerly to avoid - // the complexity of lazy-loading. - var fs = require('fs'); - - scriptDirectory = __dirname + '/'; - -// include: node_shell_read.js -readBinary = (filename) => { - // We need to re-wrap `file://` strings to URLs. - filename = isFileURI(filename) ? new URL(filename) : filename; - var ret = fs.readFileSync(filename); - return ret; -}; - -readAsync = async (filename, binary = true) => { - // See the comment in the `readBinary` function. - filename = isFileURI(filename) ? new URL(filename) : filename; - var ret = fs.readFileSync(filename, binary ? undefined : 'utf8'); - return ret; -}; -// end include: node_shell_read.js - if (process.argv.length > 1) { - thisProgram = process.argv[1].replace(/\\/g, '/'); - } - - arguments_ = process.argv.slice(2); - - // MODULARIZE will export the module in the proper place outside, we don't need to export here - if (typeof module != 'undefined') { - module['exports'] = Module; - } - - quit_ = (status, toThrow) => { - process.exitCode = status; - throw toThrow; - }; - -} 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. -{ -} + // `/` 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; + } -var out = console.log.bind(console); -var err = console.error.bind(console); - -// 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 dynamicLibraries = []; - -var wasmBinary; - -// Wasm globals - -//======================================== -// 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) { - // This build was created without ASSERTIONS defined. `assert()` should not - // ever be called in this configuration but in case there are callers in - // the wild leave this simple abort() implementation here for now. - abort(text); - } -} + // Hooks that are implemented differently in different runtime environments. + var readAsync, readBinary; + + if (ENVIRONMENT_IS_NODE) { + // These modules will usually be used on Node.js. Load them eagerly to avoid + // the complexity of lazy-loading. + var fs = require('fs'); + + scriptDirectory = __dirname + '/'; + + // include: node_shell_read.js + readBinary = (filename) => { + // We need to re-wrap `file://` strings to URLs. + filename = isFileURI(filename) ? new URL(filename) : filename; + var ret = fs.readFileSync(filename); + return ret; + }; + + readAsync = async (filename, binary = true) => { + // See the comment in the `readBinary` function. + filename = isFileURI(filename) ? new URL(filename) : filename; + var ret = fs.readFileSync(filename, binary ? undefined : 'utf8'); + return ret; + }; + // end include: node_shell_read.js + if (process.argv.length > 1) { + thisProgram = process.argv[1].replace(/\\/g, '/'); + } + + arguments_ = process.argv.slice(2); + + // MODULARIZE will export the module in the proper place outside, we don't need to export here + if (typeof module != 'undefined') { + module['exports'] = Module; + } + + quit_ = (status, toThrow) => { + process.exitCode = status; + throw toThrow; + }; + } 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. + { + } + + var out = console.log.bind(console); + var err = console.error.bind(console); + + // 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 dynamicLibraries = []; + + var wasmBinary; + + // Wasm globals + + //======================================== + // 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) { + // This build was created without ASSERTIONS defined. `assert()` should not + // ever be called in this configuration but in case there are callers in + // the wild leave this simple abort() implementation here for now. + abort(text); + } + } -/** - * Indicates whether filename is delivered via file protocol (as opposed to http/https) - * @noinline - */ -var isFileURI = (filename) => filename.startsWith('file://'); - -// include: runtime_common.js -// include: runtime_stack_check.js -// end include: runtime_stack_check.js -// include: runtime_exceptions.js -// end include: runtime_exceptions.js -// include: runtime_debug.js -// end include: runtime_debug.js -// Memory management -var -/** @type {!Int8Array} */ - HEAP8, -/** @type {!Uint8Array} */ - HEAPU8, -/** @type {!Int16Array} */ - HEAP16, -/** @type {!Uint16Array} */ - HEAPU16, -/** @type {!Int32Array} */ - HEAP32, -/** @type {!Uint32Array} */ - HEAPU32, -/** @type {!Float32Array} */ - HEAPF32, -/** @type {!Float64Array} */ - HEAPF64; - -// BigInt64Array type is not correctly defined in closure -var -/** not-@type {!BigInt64Array} */ - HEAP64, -/* BigUint64Array type is not correctly defined in closure + /** + * Indicates whether filename is delivered via file protocol (as opposed to http/https) + * @noinline + */ + var isFileURI = (filename) => filename.startsWith('file://'); + + // include: runtime_common.js + // include: runtime_stack_check.js + // end include: runtime_stack_check.js + // include: runtime_exceptions.js + // end include: runtime_exceptions.js + // include: runtime_debug.js + // end include: runtime_debug.js + // Memory management + var /** @type {!Int8Array} */ + HEAP8, + /** @type {!Uint8Array} */ + HEAPU8, + /** @type {!Int16Array} */ + HEAP16, + /** @type {!Uint16Array} */ + HEAPU16, + /** @type {!Int32Array} */ + HEAP32, + /** @type {!Uint32Array} */ + HEAPU32, + /** @type {!Float32Array} */ + HEAPF32, + /** @type {!Float64Array} */ + HEAPF64; + + // BigInt64Array type is not correctly defined in closure + var /** not-@type {!BigInt64Array} */ + HEAP64, + /* BigUint64Array type is not correctly defined in closure /** not-@type {!BigUint64Array} */ - HEAPU64; + HEAPU64; + + var runtimeInitialized = false; + + var runtimeExited = false; + + function updateMemoryViews() { + var b = wasmMemory.buffer; + HEAP8 = new Int8Array(b); + HEAP16 = new Int16Array(b); + Module['HEAPU8'] = HEAPU8 = new Uint8Array(b); + HEAPU16 = new Uint16Array(b); + HEAP32 = new Int32Array(b); + Module['HEAPU32'] = HEAPU32 = new Uint32Array(b); + HEAPF32 = new Float32Array(b); + HEAPF64 = new Float64Array(b); + HEAP64 = new BigInt64Array(b); + HEAPU64 = new BigUint64Array(b); + } -var runtimeInitialized = false; + // include: memoryprofiler.js + // end include: memoryprofiler.js + // end include: runtime_common.js + var __RELOC_FUNCS__ = []; + + function preRun() { + if (Module['preRun']) { + if (typeof Module['preRun'] == 'function') + Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length) { + addOnPreRun(Module['preRun'].shift()); + } + } + // Begin ATPRERUNS hooks + callRuntimeCallbacks(onPreRuns); + // End ATPRERUNS hooks + } -var runtimeExited = false; + function initRuntime() { + runtimeInitialized = true; + callRuntimeCallbacks(__RELOC_FUNCS__); + // Begin ATINITS hooks + callRuntimeCallbacks(onInits); + if (!Module['noFSInit'] && !FS.initialized) FS.init(); + TTY.init(); + SOCKFS.root = FS.mount(SOCKFS, {}, null); + PIPEFS.root = FS.mount(PIPEFS, {}, null); + // End ATINITS hooks -function updateMemoryViews() { - var b = wasmMemory.buffer; - HEAP8 = new Int8Array(b); - HEAP16 = new Int16Array(b); - Module['HEAPU8'] = HEAPU8 = new Uint8Array(b); - HEAPU16 = new Uint16Array(b); - HEAP32 = new Int32Array(b); - Module['HEAPU32'] = HEAPU32 = new Uint32Array(b); - HEAPF32 = new Float32Array(b); - HEAPF64 = new Float64Array(b); - HEAP64 = new BigInt64Array(b); - HEAPU64 = new BigUint64Array(b); -} + wasmExports['__wasm_call_ctors'](); -// include: memoryprofiler.js -// end include: memoryprofiler.js -// end include: runtime_common.js -var __RELOC_FUNCS__ = []; + // Begin ATPOSTCTORS hooks + callRuntimeCallbacks(onPostCtors); + FS.ignorePermissions = false; + // End ATPOSTCTORS hooks + } -function preRun() { - if (Module['preRun']) { - if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; - while (Module['preRun'].length) { - addOnPreRun(Module['preRun'].shift()); - } - } - // Begin ATPRERUNS hooks - callRuntimeCallbacks(onPreRuns); - // End ATPRERUNS hooks -} + function preMain() { + // Begin ATMAINS hooks + callRuntimeCallbacks(onMains); + // End ATMAINS hooks + } -function initRuntime() { - runtimeInitialized = true; + function exitRuntime() { + // PThreads reuse the runtime from the main thread. + ___funcs_on_exit(); // Native atexit() functions + // Begin ATEXITS hooks + callRuntimeCallbacks(onExits); + FS.quit(); + TTY.shutdown(); + // End ATEXITS hooks + runtimeExited = true; + } - callRuntimeCallbacks(__RELOC_FUNCS__); + function postRun() { + // PThreads reuse the runtime from the main thread. - // Begin ATINITS hooks - callRuntimeCallbacks(onInits); -if (!Module['noFSInit'] && !FS.initialized) FS.init(); -TTY.init(); -SOCKFS.root = FS.mount(SOCKFS, {}, null); -PIPEFS.root = FS.mount(PIPEFS, {}, null); - // End ATINITS hooks + if (Module['postRun']) { + if (typeof Module['postRun'] == 'function') + Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length) { + addOnPostRun(Module['postRun'].shift()); + } + } - wasmExports['__wasm_call_ctors'](); + // Begin ATPOSTRUNS hooks + callRuntimeCallbacks(onPostRuns); + // End ATPOSTRUNS hooks + } - // Begin ATPOSTCTORS hooks - callRuntimeCallbacks(onPostCtors); -FS.ignorePermissions = false; - // End ATPOSTCTORS hooks -} + /** @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; + + what += '. Build with -sASSERTIONS for more info.'; + + // 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); + + // 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 preMain() { - // Begin ATMAINS hooks - callRuntimeCallbacks(onMains); - // End ATMAINS hooks -} + var wasmBinaryFile; -function exitRuntime() { - // PThreads reuse the runtime from the main thread. - ___funcs_on_exit(); // Native atexit() functions - // Begin ATEXITS hooks - callRuntimeCallbacks(onExits); -FS.quit(); -TTY.shutdown(); - // End ATEXITS hooks - runtimeExited = true; -} + function findWasmBinary() { + return locateFile(dependencyFilename); + } -function postRun() { - // PThreads reuse the runtime from the main thread. + function getBinarySync(file) { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary); + } + if (readBinary) { + return readBinary(file); + } + // Throwing a plain string here, even though it not normally adviables since + // this gets turning into an `abort` in instantiateArrayBuffer. + throw 'both async and sync fetching of the wasm failed'; + } - if (Module['postRun']) { - if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; - while (Module['postRun'].length) { - addOnPostRun(Module['postRun'].shift()); - } - } + 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 { + // Fall back to getBinarySync below; + } + } + + // Otherwise, getBinarySync should be able to get it synchronously + return getBinarySync(binaryFile); + } - // Begin ATPOSTRUNS hooks - callRuntimeCallbacks(onPostRuns); - // End ATPOSTRUNS hooks -} + 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}`); -/** @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; - - what += '. Build with -sASSERTIONS for more info.'; - - // 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); - - // 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; -} + abort(reason); + } + } -var wasmBinaryFile; + async function instantiateAsync(binary, binaryFile, imports) { + if ( + !binary && + // Avoid instantiateStreaming() on Node.js environment for now, as while + // Node.js v18.1.0 implements it, it does not have a full fetch() + // implementation yet. + // + // Reference: + // https://github.com/emscripten-core/emscripten/pull/16917 + !ENVIRONMENT_IS_NODE + ) { + 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'); + // fall back of instantiateArrayBuffer below + } + } + return instantiateArrayBuffer(binaryFile, imports); + } -function findWasmBinary() { - return locateFile(dependencyFilename); -} + function getWasmImports() { + // prepare imports + var imports = { + env: wasmImports, + wasi_snapshot_preview1: wasmImports, + 'GOT.mem': new Proxy(wasmImports, GOTHandler), + 'GOT.func': new Proxy(wasmImports, GOTHandler), + }; + return imports; + } -function getBinarySync(file) { - if (file == wasmBinaryFile && wasmBinary) { - return new Uint8Array(wasmBinary); - } - if (readBinary) { - return readBinary(file); - } - // Throwing a plain string here, even though it not normally adviables since - // this gets turning into an `abort` in instantiateArrayBuffer. - throw 'both async and sync fetching of the wasm failed'; -} + // 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; + + // No relocation needed here.. but calling this just so that updateGOT is + // called. + var origExports = (wasmExports = relocateExports(wasmExports)); + + wasmExports = Asyncify.instrumentWasmExports(wasmExports); + + mergeLibSymbols(wasmExports, 'main'); + var metadata = getDylinkMetadata(module); + if (metadata.neededDynlibs) { + dynamicLibraries = + metadata.neededDynlibs.concat(dynamicLibraries); + } + + assignWasmExports(wasmExports); + + updateGOT(origExports); + + Module['wasmExports'] = wasmExports; + + LDSO.init(); + loadDylibs(); + + updateMemoryViews(); + + removeRunDependency('wasm-instantiate'); + return wasmExports; + } + addRunDependency('wasm-instantiate'); + + // Prefer streaming instantiation if available. + 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 + 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) => { + Module['instantiateWasm'](info, (inst, mod) => { + resolve(receiveInstance(inst, mod)); + }); + }); + } + + wasmBinaryFile ??= findWasmBinary(); + var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info); + var exports = receiveInstantiationResult(result); + return exports; + } -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 { - // Fall back to getBinarySync below; - } - } + // With MAIN_MODULE + ASYNCIFY the normal method of placing stub functions in + // wasmImports for as-yet-undefined symbols doesn't work since ASYNCIFY then + // wraps these stub functions and we can't then replace them directly. Instead + // the stub functions call into `asyncifyStubs` which gets populated by the + // dynamic linker as symbols are loaded. + var asyncifyStubs = {}; + // end include: preamble.js + + // Begin JS library code + + class ExitStatus { + name = 'ExitStatus'; + constructor(status) { + this.message = `Program terminated with exit(${status})`; + this.status = status; + } + } + ExitStatus = class PHPExitStatus extends Error { + constructor(status) { + super(status); + this.name = 'ExitStatus'; + this.message = 'Program terminated with exit(' + status + ')'; + this.status = status; + } + }; + + var GOT = {}; + + var currentModuleWeakSymbols = new Set([ + '__start___llvm_prf_data', + '__stop___llvm_prf_data', + '__start___llvm_prf_names', + '__stop___llvm_prf_names', + '__start___llvm_prf_vns', + '__stop___llvm_prf_vns', + '__start___llvm_prf_vtab', + '__stop___llvm_prf_vtab', + '__start___llvm_prf_cnts', + '__stop___llvm_prf_cnts', + '__start___llvm_prf_bits', + '__stop___llvm_prf_bits', + '__start___llvm_prf_vnds', + '__stop___llvm_prf_vnds', + ]); + var GOTHandler = { + get(obj, symName) { + var rtn = GOT[symName]; + if (!rtn) { + rtn = GOT[symName] = new WebAssembly.Global( + { value: 'i32', mutable: true }, + -1 + ); + } + if (!currentModuleWeakSymbols.has(symName)) { + // Any non-weak reference to a symbol marks it as `required`, which + // enabled `reportUndefinedSymbols` to report undefined symbol errors + // correctly. + rtn.required = true; + } + return rtn; + }, + }; + + var callRuntimeCallbacks = (callbacks) => { + while (callbacks.length > 0) { + // Pass the module as the first argument. + callbacks.shift()(Module); + } + }; + var onPostRuns = []; + var addOnPostRun = (cb) => onPostRuns.push(cb); + + var onPreRuns = []; + var addOnPreRun = (cb) => onPreRuns.push(cb); + + var runDependencies = 0; + + var dependenciesFulfilled = null; + var removeRunDependency = (id) => { + runDependencies--; + + Module['monitorRunDependencies']?.(runDependencies); + + if (runDependencies == 0) { + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); // can add another dependenciesFulfilled + } + } + }; + var addRunDependency = (id) => { + runDependencies++; + + Module['monitorRunDependencies']?.(runDependencies); + }; + + var dynCalls = {}; + var dynCallLegacy = (sig, ptr, args) => { + sig = sig.replace(/p/g, 'i'); + var f = dynCalls[sig]; + return f(ptr, ...args); + }; + var dynCall = (sig, ptr, args = [], promising = false) => { + var rtn = dynCallLegacy(sig, ptr, args); + + function convert(rtn) { + return rtn; + } + + return convert(rtn); + }; + + var UTF8Decoder = globalThis.TextDecoder && new TextDecoder(); + + var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + var maxIdx = idx + maxBytesToRead; + if (ignoreNul) return maxIdx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. + // As a tiny code save trick, compare idx against maxIdx using a negation, + // so that maxBytesToRead=undefined/NaN means Infinity. + while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; + return idx; + }; + + /** + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number=} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ + var UTF8ArrayToString = ( + heapOrArray, + idx = 0, + maxBytesToRead, + ignoreNul + ) => { + var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); + + // When using conditional TextDecoder, skip it for short strings as the overhead of the native call is not worth it. + if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { + return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); + } + var str = ''; + 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 & 0x80)) { + str += String.fromCharCode(u0); + continue; + } + var u1 = heapOrArray[idx++] & 63; + if ((u0 & 0xe0) == 0xc0) { + str += String.fromCharCode(((u0 & 31) << 6) | u1); + continue; + } + var u2 = heapOrArray[idx++] & 63; + if ((u0 & 0xf0) == 0xe0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + u0 = + ((u0 & 7) << 18) | + (u1 << 12) | + (u2 << 6) | + (heapOrArray[idx++] & 63); + } + + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode( + 0xd800 | (ch >> 10), + 0xdc00 | (ch & 0x3ff) + ); + } + } + return str; + }; + var getDylinkMetadata = (binary) => { + var offset = 0; + var end = 0; + + function getU8() { + return binary[offset++]; + } + + function getLEB() { + var ret = 0; + var mul = 1; + while (1) { + var byte = binary[offset++]; + ret += (byte & 0x7f) * mul; + mul *= 0x80; + if (!(byte & 0x80)) break; + } + return ret; + } + + function getString() { + var len = getLEB(); + offset += len; + return UTF8ArrayToString(binary, offset - len, len); + } + + function getStringList() { + var count = getLEB(); + var rtn = []; + while (count--) rtn.push(getString()); + return rtn; + } + + /** @param {string=} message */ + function failIf(condition, message) { + if (condition) throw new Error(message); + } + + if (binary instanceof WebAssembly.Module) { + var dylinkSection = WebAssembly.Module.customSections( + binary, + 'dylink.0' + ); + failIf(dylinkSection.length === 0, 'need dylink section'); + binary = new Uint8Array(dylinkSection[0]); + end = binary.length; + } else { + var int32View = new Uint32Array( + new Uint8Array(binary.subarray(0, 24)).buffer + ); + var magicNumberFound = int32View[0] == 0x6d736100; + failIf(!magicNumberFound, 'need to see wasm magic number'); // \0asm + // we should see the dylink custom section right after the magic number and wasm version + failIf(binary[8] !== 0, 'need the dylink section to be first'); + offset = 9; + var section_size = getLEB(); //section size + end = offset + section_size; + var name = getString(); + failIf(name !== 'dylink.0'); + } + + var customSection = { + neededDynlibs: [], + tlsExports: new Set(), + weakImports: new Set(), + runtimePaths: [], + }; + var WASM_DYLINK_MEM_INFO = 0x1; + var WASM_DYLINK_NEEDED = 0x2; + var WASM_DYLINK_EXPORT_INFO = 0x3; + var WASM_DYLINK_IMPORT_INFO = 0x4; + var WASM_DYLINK_RUNTIME_PATH = 0x5; + var WASM_SYMBOL_TLS = 0x100; + var WASM_SYMBOL_BINDING_MASK = 0x3; + var WASM_SYMBOL_BINDING_WEAK = 0x1; + while (offset < end) { + var subsectionType = getU8(); + var subsectionSize = getLEB(); + if (subsectionType === WASM_DYLINK_MEM_INFO) { + customSection.memorySize = getLEB(); + customSection.memoryAlign = getLEB(); + customSection.tableSize = getLEB(); + customSection.tableAlign = getLEB(); + } else if (subsectionType === WASM_DYLINK_NEEDED) { + customSection.neededDynlibs = getStringList(); + } else if (subsectionType === WASM_DYLINK_EXPORT_INFO) { + var count = getLEB(); + while (count--) { + var symname = getString(); + var flags = getLEB(); + if (flags & WASM_SYMBOL_TLS) { + customSection.tlsExports.add(symname); + } + } + } else if (subsectionType === WASM_DYLINK_IMPORT_INFO) { + var count = getLEB(); + while (count--) { + var modname = getString(); + var symname = getString(); + var flags = getLEB(); + if ( + (flags & WASM_SYMBOL_BINDING_MASK) == + WASM_SYMBOL_BINDING_WEAK + ) { + customSection.weakImports.add(symname); + } + } + } else if (subsectionType === WASM_DYLINK_RUNTIME_PATH) { + customSection.runtimePaths = getStringList(); + } else { + // unknown subsection + offset += subsectionSize; + } + } + + return customSection; + }; + + /** + * @param {number} ptr + * @param {string} type + */ + function getValue(ptr, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': + return HEAP8[ptr]; + case 'i8': + return HEAP8[ptr]; + case 'i16': + return HEAP16[ptr >> 1]; + case 'i32': + return HEAP32[ptr >> 2]; + case 'i64': + return HEAP64[ptr >> 3]; + case 'float': + return HEAPF32[ptr >> 2]; + case 'double': + return HEAPF64[ptr >> 3]; + case '*': + return HEAPU32[ptr >> 2]; + default: + abort(`invalid type for getValue: ${type}`); + } + } - // Otherwise, getBinarySync should be able to get it synchronously - return getBinarySync(binaryFile); -} + var newDSO = (name, handle, syms) => { + var dso = { + refcount: Infinity, + name, + exports: syms, + global: true, + }; + LDSO.loadedLibsByName[name] = dso; + if (handle != undefined) { + LDSO.loadedLibsByHandle[handle] = dso; + } + return dso; + }; + var LDSO = { + loadedLibsByName: {}, + loadedLibsByHandle: {}, + init() { + newDSO('__main__', 0, wasmImports); + }, + }; + + var alignMemory = (size, alignment) => { + return Math.ceil(size / alignment) * alignment; + }; + + var getMemory = (size) => { + // After the runtime is initialized, we must only use sbrk() normally. + if (runtimeInitialized) { + // Currently we don't support freeing of static data when modules are + // unloaded via dlclose. This function is tagged as `noleakcheck` to + // avoid having this reported as leak. + return _calloc(size, 1); + } + var ret = ___heap_base; + // Keep __heap_base stack aligned. + var end = ret + alignMemory(size, 16); + ___heap_base = end; + + // After allocating the memory from the start of the heap we need to ensure + // that once the program starts it doesn't use this region. In relocatable + // mode we can just update the __heap_base symbol that we are exporting to + // the main module. + // When not relocatable `__heap_base` is fixed and exported by the main + // module, but we can update the `sbrk_ptr` value instead. We call + // `_emscripten_get_sbrk_ptr` knowing that it is safe to call prior to + // runtime initialization (unlike, the higher level sbrk function) + var sbrk_ptr = _emscripten_get_sbrk_ptr(); + HEAPU32[sbrk_ptr >> 2] = end; + return ret; + }; + + var isInternalSym = (symName) => { + // TODO: find a way to mark these in the binary or avoid exporting them. + return ( + [ + 'memory', + '__memory_base', + '__table_base', + '__stack_pointer', + '__indirect_function_table', + '__cpp_exception', + '__c_longjmp', + '__wasm_apply_data_relocs', + '__dso_handle', + '__tls_size', + '__tls_align', + '__set_stack_limits', + '_emscripten_tls_init', + '__wasm_init_tls', + '__wasm_call_ctors', + '__start_em_asm', + '__stop_em_asm', + '__start_em_js', + '__stop_em_js', + ].includes(symName) || symName.startsWith('__em_js__') + ); + }; + + var wasmTableMirror = []; + + var getWasmTableEntry = (funcPtr) => { + var func = wasmTableMirror[funcPtr]; + if (!func) { + /** @suppress {checkTypes} */ + wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); + } + return func; + }; + + 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 + return wasmTable['grow'](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); + }; + + var uleb128EncodeWithLen = (arr) => { + const n = arr.length; + // Note: this LEB128 length encoding produces extra byte for n < 128, + // but we don't care as it's only used in a temporary representation. + return [(n % 128) | 128, n >> 7, ...arr]; + }; + + var wasmTypeCodes = { + i: 0x7f, // i32 + p: 0x7f, // i32 + j: 0x7e, // i64 + f: 0x7d, // f32 + d: 0x7c, // f64 + e: 0x6f, // externref + }; + var generateTypePack = (types) => + uleb128EncodeWithLen( + Array.from(types, (type) => { + var code = wasmTypeCodes[type]; + return code; + }) + ); + var convertJsFunctionToWasm = (func, sig) => { + // Rest of the module is static + var bytes = Uint8Array.of( + 0x00, + 0x61, + 0x73, + 0x6d, // magic ("\0asm") + 0x01, + 0x00, + 0x00, + 0x00, // version: 1 + 0x01, // Type section code + // The module is static, with the exception of the type section, which is + // generated based on the signature passed in. + ...uleb128EncodeWithLen([ + 0x01, // count: 1 + 0x60 /* form: func */, + // param types + ...generateTypePack(sig.slice(1)), + // return types (for now only supporting [] if `void` and single [T] otherwise) + ...generateTypePack(sig[0] === 'v' ? '' : sig[0]), + ]), + // The rest of the module is static + 0x02, + 0x07, // import section + // (import "e" "f" (func 0 (type 0))) + 0x01, + 0x01, + 0x65, + 0x01, + 0x66, + 0x00, + 0x00, + 0x07, + 0x05, // export section + // (export "f" (func 0 (type 0))) + 0x01, + 0x01, + 0x66, + 0x00, + 0x00 + ); + + // 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(bytes); + var instance = new WebAssembly.Instance(module, { e: { f: func } }); + var wrappedFunc = instance.exports['f']; + return wrappedFunc; + }; + /** @param {string=} sig */ + var addFunction = (func, sig) => { + // 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. + + 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; + } + var wrapped = convertJsFunctionToWasm(func, sig); + setWasmTableEntry(ret, wrapped); + } + + functionsInTableMap.set(func, ret); + + return ret; + }; + /** @param {boolean=} replace */ + var updateGOT = (exports, replace) => { + for (var symName in exports) { + if (isInternalSym(symName)) { + continue; + } + + var value = exports[symName]; + + var existingEntry = GOT[symName] && GOT[symName].value != -1; + if (replace || !existingEntry) { + var newValue; + if (typeof value == 'function') { + newValue = addFunction(value); + } else if (typeof value == 'number') { + newValue = value; + } else { + // The GOT can only contain addresses (i.e data addresses or function + // addresses so we currently ignore other types export here. + continue; + } + GOT[symName] ??= new WebAssembly.Global({ + value: 'i32', + mutable: true, + }); + GOT[symName].value = newValue; + } + } + }; + + var isImmutableGlobal = (val) => { + if (val instanceof WebAssembly.Global) { + try { + val.value = val.value; + } catch { + return true; + } + } + return false; + }; + var relocateExports = (exports, memoryBase = 0) => { + function relocateExport(name, value) { + // Detect immuable wasm global exports. These represent data addresses + // which are relative to `memoryBase` + if (isImmutableGlobal(value)) { + return value.value + memoryBase; + } + + // Return unmodified value (no relocation required). + return value; + } + + var relocated = {}; + for (var e in exports) { + relocated[e] = relocateExport(e, exports[e]); + } + return relocated; + }; + + var isSymbolDefined = (symName) => { + // Ignore 'stub' symbols that are auto-generated as part of the original + // `wasmImports` used to instantiate the main module. + var existing = wasmImports[symName]; + if (!existing || existing.stub) { + return false; + } + // Even if a symbol exists in wasmImports, and is not itself a stub, it + // could be an ASYNCIFY wrapper function that wraps a stub function. + if (symName in asyncifyStubs && !asyncifyStubs[symName]) { + return false; + } + return true; + }; + + var createNamedFunction = (name, func) => + Object.defineProperty(func, 'name', { value: name }); + + var stackSave = () => _emscripten_stack_get_current(); + + var stackRestore = (val) => __emscripten_stack_restore(val); + var createInvokeFunction = + (sig) => + (ptr, ...args) => { + var sp = stackSave(); + try { + return dynCall(sig, ptr, args); + } catch (e) { + stackRestore(sp); + // Create a try-catch guard that rethrows the Emscripten EH exception. + // Exceptions thrown from C++ will be a pointer (number) and longjmp + // will throw the number Infinity. Use the compact and fast "e !== e+0" + // test to check if e was not a Number. + if (e !== e + 0) throw e; + _setThrew(1, 0); + // In theory this if statement could be done on + // creating the function, but I just added this to + // save wasting code space as it only happens on exception. + if (sig[0] == 'j') return 0n; + } + }; + var resolveGlobalSymbol = (symName, direct = false) => { + var sym; + if (isSymbolDefined(symName)) { + sym = wasmImports[symName]; + } + // Asm.js-style exception handling: invoke wrapper generation + else if (symName.startsWith('invoke_')) { + // Create (and cache) new invoke_ functions on demand. + sym = wasmImports[symName] = createNamedFunction( + symName, + createInvokeFunction(symName.split('_')[1]) + ); + } + return { sym, name: symName }; + }; + + var onPostCtors = []; + var addOnPostCtor = (cb) => onPostCtors.push(cb); + + /** + * 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. + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ + var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => { + return ptr + ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead, ignoreNul) + : ''; + }; + + /** + * @param {string=} libName + * @param {Object=} localScope + * @param {number=} handle + */ + var loadWebAssemblyModule = ( + binary, + flags, + libName, + localScope, + handle + ) => { + var metadata = getDylinkMetadata(binary); + + // loadModule loads the wasm module after all its dependencies have been loaded. + // can be called both sync/async. + function loadModule() { + // alignments are powers of 2 + var memAlign = Math.pow(2, metadata.memoryAlign); + // prepare memory + var memoryBase = metadata.memorySize + ? alignMemory( + getMemory(metadata.memorySize + memAlign), + memAlign + ) + : 0; // TODO: add to cleanups + var tableBase = metadata.tableSize ? wasmTable.length : 0; + if (handle) { + HEAP8[handle + 8] = 1; + HEAPU32[(handle + 12) >> 2] = memoryBase; + HEAP32[(handle + 16) >> 2] = metadata.memorySize; + HEAPU32[(handle + 20) >> 2] = tableBase; + HEAP32[(handle + 24) >> 2] = metadata.tableSize; + } + + if (metadata.tableSize) { + wasmTable.grow(metadata.tableSize); + } + + // This is the export map that we ultimately return. We declare it here + // so it can be used within resolveSymbol. We resolve symbols against + // this local symbol map in the case there they are not present on the + // global Module object. We need this fallback because Modules sometime + // need to import their own symbols + var moduleExports; + + function resolveSymbol(sym) { + var resolved = resolveGlobalSymbol(sym).sym; + if (!resolved && localScope) { + resolved = localScope[sym]; + } + if (!resolved) { + resolved = moduleExports[sym]; + } + return resolved; + } + + // TODO kill ↓↓↓ (except "symbols local to this module", it will likely be + // not needed if we require that if A wants symbols from B it has to link + // to B explicitly: similarly to -Wl,--no-undefined) + // + // wasm dynamic libraries are pure wasm, so they cannot assist in + // their own loading. When side module A wants to import something + // provided by a side module B that is loaded later, we need to + // add a layer of indirection, but worse, we can't even tell what + // to add the indirection for, without inspecting what A's imports + // are. To do that here, we use a JS proxy (another option would + // be to inspect the binary directly). + var proxyHandler = { + get(stubs, prop) { + // symbols that should be local to this module + switch (prop) { + case '__memory_base': + return memoryBase; + case '__table_base': + return tableBase; + } + if (prop in wasmImports && !wasmImports[prop].stub) { + // No stub needed, symbol already exists in symbol table + var res = wasmImports[prop]; + // Asyncify wraps exports, and we need to look through those wrappers. + if (res.orig) { + res = res.orig; + } + return res; + } + // Return a stub function that will resolve the symbol + // when first called. + if (!(prop in stubs)) { + var resolved; + stubs[prop] = (...args) => { + resolved ||= resolveSymbol(prop); + return resolved(...args); + }; + } + return stubs[prop]; + }, + }; + var proxy = new Proxy({}, proxyHandler); + currentModuleWeakSymbols = metadata.weakImports; + var info = { + 'GOT.mem': new Proxy({}, GOTHandler), + 'GOT.func': new Proxy({}, GOTHandler), + env: proxy, + wasi_snapshot_preview1: proxy, + }; + + function postInstantiation(module, instance) { + // add new entries to functionsInTableMap + updateTableMap(tableBase, metadata.tableSize); + moduleExports = relocateExports(instance.exports, memoryBase); + updateGOT(moduleExports); + moduleExports = Asyncify.instrumentWasmExports(moduleExports); + if (!flags.allowUndefined) { + reportUndefinedSymbols(); + } + + function addEmAsm(addr, body) { + var args = []; + for (var arity = 0; ; arity++) { + var argName = '$' + arity; + if (!body.includes(argName)) break; + args.push(argName); + } + args = args.join(','); + var func = `(${args}) => { ${body} };`; + ASM_CONSTS[start] = eval(func); + } + + // Add any EM_ASM function that exist in the side module + if ('__start_em_asm' in moduleExports) { + var start = moduleExports['__start_em_asm']; + var stop = moduleExports['__stop_em_asm']; + + while (start < stop) { + var jsString = UTF8ToString(start); + addEmAsm(start, jsString); + start = HEAPU8.indexOf(0, start) + 1; + } + } + + function addEmJs(name, cSig, body) { + // The signature here is a C signature (e.g. "(int foo, char* bar)"). + // See `create_em_js` in emcc.py` for the build-time version of this + // code. + var jsArgs = []; + cSig = cSig.slice(1, -1); + if (cSig != 'void') { + cSig = cSig.split(','); + for (var arg of cSig) { + var jsArg = arg.split(' ').pop(); + jsArgs.push(jsArg.replace('*', '')); + } + } + var func = `(${jsArgs}) => ${body};`; + moduleExports[name] = eval(func); + } + + for (var name in moduleExports) { + if (name.startsWith('__em_js__')) { + var start = moduleExports[name]; + var jsString = UTF8ToString(start); + // EM_JS strings are stored in the data section in the form + // SIG<::>BODY. + var [sig, body] = jsString.split('<::>'); + addEmJs(name.replace('__em_js__', ''), sig, body); + delete moduleExports[name]; + } + } + + // initialize the module + var applyRelocs = moduleExports['__wasm_apply_data_relocs']; + if (applyRelocs) { + if (runtimeInitialized) { + applyRelocs(); + } else { + __RELOC_FUNCS__.push(applyRelocs); + } + } + var init = moduleExports['__wasm_call_ctors']; + if (init) { + if (runtimeInitialized) { + init(); + } else { + // we aren't ready to run compiled code yet + addOnPostCtor(init); + } + } + return moduleExports; + } + + if (flags.loadAsync) { + return (async () => { + var instance; + if (binary instanceof WebAssembly.Module) { + instance = new WebAssembly.Instance(binary, info); + } else { + // Destructuring assignment without declaration has to be wrapped + // with parens or parser will treat the l-value as an object + // literal instead. + ({ module: binary, instance } = + await WebAssembly.instantiate(binary, info)); + } + return postInstantiation(binary, instance); + })(); + } + + var module = + binary instanceof WebAssembly.Module + ? binary + : new WebAssembly.Module(binary); + var instance = new WebAssembly.Instance(module, info); + return postInstantiation(module, instance); + } + + // We need to set rpath in flags based on the current library's rpath. + // We can't mutate flags or else if a depends on b and c and b depends on d, + // then c will be loaded with b's rpath instead of a's. + flags = { + ...flags, + rpath: { parentLibPath: libName, paths: metadata.runtimePaths }, + }; + // now load needed libraries and the module itself. + if (flags.loadAsync) { + return metadata.neededDynlibs + .reduce( + (chain, dynNeeded) => + chain.then(() => + loadDynamicLibrary(dynNeeded, flags, localScope) + ), + Promise.resolve() + ) + .then(loadModule); + } + + for (var needed of metadata.neededDynlibs) { + loadDynamicLibrary(needed, flags, localScope); + } + return loadModule(); + }; + + var mergeLibSymbols = (exports, libName) => { + registerDynCallSymbols(exports); + // add symbols into global namespace TODO: weak linking etc. + for (var [sym, exp] of Object.entries(exports)) { + // When RTLD_GLOBAL is enabled, the symbols defined by this shared object + // will be made available for symbol resolution of subsequently loaded + // shared objects. + // + // We should copy the symbols (which include methods and variables) from + // SIDE_MODULE to MAIN_MODULE. + const setImport = (target) => { + if (target in asyncifyStubs) { + asyncifyStubs[target] = exp; + } + if (!isSymbolDefined(target)) { + wasmImports[target] = exp; + } + }; + setImport(sym); + + // Special case for handling of main symbol: If a side module exports + // `main` that also acts a definition for `__main_argc_argv` and vice + // versa. + const main_alias = '__main_argc_argv'; + if (sym == 'main') { + setImport(main_alias); + } + if (sym == main_alias) { + setImport('main'); + } + } + }; + + var asyncLoad = async (url) => { + var arrayBuffer = await readAsync(url); + return new Uint8Array(arrayBuffer); + }; + + var preloadPlugins = []; + var registerWasmPlugin = () => { + // Use string keys here for public methods to avoid minification since the + // plugin consumer also uses string keys. + var wasmPlugin = { + promiseChainEnd: Promise.resolve(), + canHandle: (name) => { + return !Module['noWasmDecoding'] && name.endsWith('.so'); + }, + handle: async (byteArray, name) => + // loadWebAssemblyModule can not load modules out-of-order, so rather + // than just running the promises in parallel, this makes a chain of + // promises to run in series. + (wasmPlugin.promiseChainEnd = wasmPlugin.promiseChainEnd.then( + async () => { + try { + var exports = await loadWebAssemblyModule( + byteArray, + { loadAsync: true, nodelete: true }, + name, + {} + ); + } catch (error) { + throw new Error( + `failed to instantiate wasm: ${name}: ${error}` + ); + } + preloadedWasm[name] = exports; + return byteArray; + } + )), + }; + preloadPlugins.push(wasmPlugin); + }; + var preloadedWasm = {}; + + 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 replaceORIGIN = (parentLibName, rpath) => { + if (rpath.startsWith('$ORIGIN')) { + // TODO: what to do if we only know the relative path of the file? It will return "." here. + var origin = PATH.dirname(parentLibName); + return rpath.replace('$ORIGIN', origin); + } + + return rpath; + }; + + var withStackSave = (f) => { + var stack = stackSave(); + var ret = f(); + stackRestore(stack); + return ret; + }; + + var stackAlloc = (sz) => __emscripten_stack_alloc(sz); + + 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 <= 0x7f) { + len++; + } else if (c <= 0x7ff) { + len += 2; + } else if (c >= 0xd800 && c <= 0xdfff) { + len += 4; + ++i; + } else { + len += 3; + } + } + return len; + }; + + var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { + // 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) { + // 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.codePointAt(i); + if (u <= 0x7f) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 0x7ff) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 0xc0 | (u >> 6); + heap[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xffff) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 0xe0 | (u >> 12); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + heap[outIdx++] = 0xf0 | (u >> 18); + heap[outIdx++] = 0x80 | ((u >> 12) & 63); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + // Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16. + // We need to manually skip over the second code unit for correct iteration. + i++; + } + } + // Null-terminate the pointer to the buffer. + heap[outIdx] = 0; + return outIdx - startIdx; + }; + var stringToUTF8 = (str, outPtr, maxBytesToWrite) => { + return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); + }; + + var stringToUTF8OnStack = (str) => { + var size = lengthBytesUTF8(str) + 1; + var ret = stackAlloc(size); + stringToUTF8(str, ret, size); + return ret; + }; + + var initRandomFill = () => { + return (view) => crypto.getRandomValues(view); + }; + 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 ''; // an invalid portion invalidates the whole thing + } + 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 = []; + + /** @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 (ENVIRONMENT_IS_NODE) { + // we will read data by chunks of BUFSIZE + var BUFSIZE = 256; + var buf = Buffer.alloc(BUFSIZE); + var bytesRead = 0; + + // For some reason we must suppress a closure warning here, even though + // fd definitely exists on process.stdin, and is even the proper way to + // get the fd of stdin, + // https://github.com/nodejs/help/issues/2136#issuecomment-523649904 + // This started to happen after moving this logic out of library_tty.js, + // so it is related to the surrounding code in some unclear manner. + /** @suppress {missingProperties} */ + var fd = process.stdin.fd; + + try { + bytesRead = fs.readSync(fd, buf, 0, BUFSIZE); + } catch (e) { + // Cross-platform differences: on Windows, reading EOF throws an + // exception, but on other OSes, reading EOF returns 0. Uniformize + // behavior by treating the EOF exception to return 0. + if (e.toString().includes('EOF')) bytesRead = 0; + else throw e; + } + + if (bytesRead > 0) { + result = buf.slice(0, bytesRead).toString('utf-8'); + } + } else { + } + if (!result) { + return null; + } + FS_stdin_getChar_buffer = intArrayFromString(result, true); + } + return FS_stdin_getChar_buffer.shift(); + }; + var TTY = { + ttys: [], + init() { + // https://github.com/emscripten-core/emscripten/pull/1555 + // if (ENVIRONMENT_IS_NODE) { + // // currently, FS.init does not distinguish if process.stdin is a file or TTY + // // device, it always assumes it's a TTY device. because of this, we're forcing + // // process.stdin to UTF8 encoding to at least make stdin reading compatible + // // with text files until FS.init can be refactored. + // process.stdin.setEncoding('utf8'); + // } + }, + shutdown() { + // https://github.com/emscripten-core/emscripten/pull/1555 + // if (ENVIRONMENT_IS_NODE) { + // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)? + // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation + // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists? + // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle + // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call + // process.stdin.pause(); + // } + }, + register(dev, ops) { + TTY.ttys[dev] = { input: [], output: [], ops: 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 /* ignored */) { + 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); // val == 0 would cut text output off in the middle. + } + }, + 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: [ + 0x03, 0x1c, 0x7f, 0x15, 0x04, 0x00, 0x01, 0x00, 0x11, + 0x13, 0x1a, 0x00, 0x12, 0x0f, 0x17, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + ], + }; + }, + 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) => HEAPU8.fill(0, ptr, ptr + size); + + 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.0 : 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); // Copy old data over to the new storage. + }, + 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) + ) + ); // Copy old data over to the new storage. + } + 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) { + // This error may happen quite a bit. To avoid overhead we reuse it (and + // suffer a lack of stack info). + if (!MEMFS.doesNotExistError) { + MEMFS.doesNotExistError = new FS.ErrnoError(44); + /** @suppress {checkTypes} */ + MEMFS.doesNotExistError.stack = ''; + } + throw MEMFS.doesNotExistError; + }, + mknod(parent, name, mode, dev) { + return MEMFS.createNode(parent, name, mode, dev); + }, + rename(old_node, new_dir, new_name) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) {} + if (new_node) { + if (FS.isDir(old_node.mode)) { + // 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, 0o777 | 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); + 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) { + // 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 === HEAP8.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) { + 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]; // Or fall back to manual write if not. + } + } + 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 === HEAP8.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 + ); + } + } + HEAP8.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 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 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 NODEFS = { + isWindows: false, + staticInit() { + NODEFS.isWindows = !!process.platform.match(/^win/); + var flags = process.binding('constants')['fs']; + NODEFS.flagsForNodeMap = { + 1024: flags['O_APPEND'], + 64: flags['O_CREAT'], + 128: flags['O_EXCL'], + 256: flags['O_NOCTTY'], + 0: flags['O_RDONLY'], + 2: flags['O_RDWR'], + 4096: flags['O_SYNC'], + 512: flags['O_TRUNC'], + 1: flags['O_WRONLY'], + 131072: flags['O_NOFOLLOW'], + }; + }, + convertNodeCode(e) { + var code = e.code; + return ERRNO_CODES[code]; + }, + tryFSOperation(f) { + try { + return f(); + } catch (e) { + if (!e.code) throw e; + // node under windows can return code 'UNKNOWN' here: + // https://github.com/emscripten-core/emscripten/issues/15468 + if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28); + throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); + } + }, + mount(mount) { + return NODEFS.createNode( + null, + '/', + NODEFS.getMode(mount.opts.root), + 0 + ); + }, + createNode(parent, name, mode, dev) { + if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { + throw new FS.ErrnoError(28); + } + var node = FS.createNode(parent, name, mode); + node.node_ops = NODEFS.node_ops; + node.stream_ops = NODEFS.stream_ops; + return node; + }, + getMode(path) { + return NODEFS.tryFSOperation(() => { + var mode = fs.lstatSync(path).mode; + if (NODEFS.isWindows) { + // Windows does not report the 'x' permission bit, so propagate read + // bits to execute bits. + mode |= (mode & 292) >> 2; + } + return mode; + }); + }, + realPath(node) { + var parts = []; + while (node.parent !== node) { + parts.push(node.name); + node = node.parent; + } + parts.push(node.mount.opts.root); + parts.reverse(); + return PATH.join(...parts); + }, + flagsForNode(flags) { + flags &= ~2097152; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~2048; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~32768; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~524288; // Some applications may pass it; it makes no sense for a single process. + flags &= ~65536; // Node.js doesn't need this passed in, it errors. + var newFlags = 0; + for (var k in NODEFS.flagsForNodeMap) { + if (flags & k) { + newFlags |= NODEFS.flagsForNodeMap[k]; + flags ^= k; + } + } + if (flags) { + throw new FS.ErrnoError(28); + } + return newFlags; + }, + getattr(func, node) { + var stat = NODEFS.tryFSOperation(func); + if (NODEFS.isWindows) { + // node.js v0.10.20 doesn't report blksize and blocks on Windows. Fake + // them with default blksize of 4096. + // See http://support.microsoft.com/kb/140365 + if (!stat.blksize) { + stat.blksize = 4096; + } + if (!stat.blocks) { + stat.blocks = + ((stat.size + stat.blksize - 1) / stat.blksize) | 0; + } + // Windows does not report the 'x' permission bit, so propagate read + // bits to execute bits. + stat.mode |= (stat.mode & 292) >> 2; + } + return { + dev: stat.dev, + ino: node.id, + mode: stat.mode, + nlink: stat.nlink, + uid: stat.uid, + gid: stat.gid, + rdev: stat.rdev, + size: stat.size, + atime: stat.atime, + mtime: stat.mtime, + ctime: stat.ctime, + blksize: stat.blksize, + blocks: stat.blocks, + }; + }, + setattr(arg, node, attr, chmod, utimes, truncate, stat) { + NODEFS.tryFSOperation(() => { + if (attr.mode !== undefined) { + var mode = attr.mode; + if (NODEFS.isWindows) { + // Windows only supports S_IREAD / S_IWRITE (S_IRUSR / S_IWUSR) + // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod + mode &= 384; + } + chmod(arg, mode); + // update the common node structure mode as well + node.mode = attr.mode; + } + if (typeof (attr.atime ?? attr.mtime) === 'number') { + // Unfortunately, we have to stat the current value if we don't want + // to change it. On top of that, since the times don't round trip + // this will only keep the value nearly unchanged not exactly + // unchanged. See: + // https://github.com/nodejs/node/issues/56492 + var atime = new Date(attr.atime ?? stat(arg).atime); + var mtime = new Date(attr.mtime ?? stat(arg).mtime); + utimes(arg, atime, mtime); + } + if (attr.size !== undefined) { + truncate(arg, attr.size); + } + }); + }, + node_ops: { + getattr(node) { + var path = NODEFS.realPath(node); + return NODEFS.getattr(() => fs.lstatSync(path), node); + }, + setattr(node, attr) { + var path = NODEFS.realPath(node); + if (attr.mode != null && attr.dontFollow) { + throw new FS.ErrnoError(52); + } + NODEFS.setattr( + path, + node, + attr, + fs.chmodSync, + fs.utimesSync, + fs.truncateSync, + fs.lstatSync + ); + }, + lookup(parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + var mode = NODEFS.getMode(path); + return NODEFS.createNode(parent, name, mode); + }, + mknod(parent, name, mode, dev) { + var node = NODEFS.createNode(parent, name, mode, dev); + // create the backing node for this in the fs root as well + var path = NODEFS.realPath(node); + NODEFS.tryFSOperation(() => { + if (FS.isDir(node.mode)) { + fs.mkdirSync(path, node.mode); + } else { + fs.writeFileSync(path, '', { mode: node.mode }); + } + }); + return node; + }, + rename(oldNode, newDir, newName) { + var oldPath = NODEFS.realPath(oldNode); + var newPath = PATH.join2(NODEFS.realPath(newDir), newName); + try { + FS.unlink(newPath); + } catch (e) {} + NODEFS.tryFSOperation(() => fs.renameSync(oldPath, newPath)); + oldNode.name = newName; + }, + unlink(parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + NODEFS.tryFSOperation(() => fs.unlinkSync(path)); + }, + rmdir(parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + NODEFS.tryFSOperation(() => fs.rmdirSync(path)); + }, + readdir(node) { + var path = NODEFS.realPath(node); + return NODEFS.tryFSOperation(() => fs.readdirSync(path)); + }, + symlink(parent, newName, oldPath) { + var newPath = PATH.join2(NODEFS.realPath(parent), newName); + NODEFS.tryFSOperation(() => fs.symlinkSync(oldPath, newPath)); + }, + readlink(node) { + var path = NODEFS.realPath(node); + return NODEFS.tryFSOperation(() => fs.readlinkSync(path)); + }, + statfs(path) { + var stats = NODEFS.tryFSOperation(() => fs.statfsSync(path)); + // Node.js doesn't provide frsize (fragment size). Set it to bsize (block size) + // as they're often the same in many file systems. May not be accurate for all. + stats.frsize = stats.bsize; + return stats; + }, + }, + stream_ops: { + getattr(stream) { + return NODEFS.getattr( + () => fs.fstatSync(stream.nfd), + stream.node + ); + }, + setattr(stream, attr) { + NODEFS.setattr( + stream.nfd, + stream.node, + attr, + fs.fchmodSync, + fs.futimesSync, + fs.ftruncateSync, + fs.fstatSync + ); + }, + open(stream) { + var path = NODEFS.realPath(stream.node); + NODEFS.tryFSOperation(() => { + stream.shared.refcount = 1; + stream.nfd = fs.openSync( + path, + NODEFS.flagsForNode(stream.flags) + ); + }); + }, + close(stream) { + NODEFS.tryFSOperation(() => { + if (stream.nfd && --stream.shared.refcount === 0) { + fs.closeSync(stream.nfd); + } + }); + }, + dup(stream) { + stream.shared.refcount++; + }, + read(stream, buffer, offset, length, position) { + return NODEFS.tryFSOperation(() => + fs.readSync( + stream.nfd, + new Int8Array(buffer.buffer, offset, length), + 0, + length, + position + ) + ); + }, + write(stream, buffer, offset, length, position) { + return NODEFS.tryFSOperation(() => + fs.writeSync( + stream.nfd, + new Int8Array(buffer.buffer, offset, length), + 0, + length, + position + ) + ); + }, + llseek(stream, offset, whence) { + var position = offset; + if (whence === 1) { + position += stream.position; + } else if (whence === 2) { + if (FS.isFile(stream.node.mode)) { + NODEFS.tryFSOperation(() => { + var stat = fs.fstatSync(stream.nfd); + position += stat.size; + }); + } + } + + 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 = mmapAlloc(length); + + NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position); + return { ptr, allocated: true }; + }, + msync(stream, buffer, offset, length, mmapFlags) { + NODEFS.stream_ops.write( + stream, + buffer, + 0, + length, + offset, + false + ); + // should we check if bytesWritten and length are the same? + return 0; + }, + }, + }; + + var PROXYFS = { + mount(mount) { + return PROXYFS.createNode( + null, + '/', + mount.opts.fs.lstat(mount.opts.root).mode, + 0 + ); + }, + createNode(parent, name, mode, dev) { + if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var node = FS.createNode(parent, name, mode); + node.node_ops = PROXYFS.node_ops; + node.stream_ops = PROXYFS.stream_ops; + return node; + }, + realPath(node) { + var parts = []; + while (node.parent !== node) { + parts.push(node.name); + node = node.parent; + } + parts.push(node.mount.opts.root); + parts.reverse(); + return PATH.join(...parts); + }, + node_ops: { + getattr(node) { + var path = PROXYFS.realPath(node); + var stat; + try { + stat = node.mount.opts.fs.lstat(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return { + dev: stat.dev, + ino: stat.ino, + mode: stat.mode, + nlink: stat.nlink, + uid: stat.uid, + gid: stat.gid, + rdev: stat.rdev, + size: stat.size, + atime: stat.atime, + mtime: stat.mtime, + ctime: stat.ctime, + blksize: stat.blksize, + blocks: stat.blocks, + }; + }, + setattr(node, attr) { + var path = PROXYFS.realPath(node); + try { + if (attr.mode !== undefined) { + node.mount.opts.fs.chmod(path, attr.mode); + // update the common node structure mode as well + node.mode = attr.mode; + } + if (attr.atime || attr.mtime) { + var atime = new Date(attr.atime || attr.mtime); + var mtime = new Date(attr.mtime || attr.atime); + node.mount.opts.fs.utime(path, atime, mtime); + } + if (attr.size !== undefined) { + node.mount.opts.fs.truncate(path, attr.size); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + lookup(parent, name) { + try { + var path = PATH.join2(PROXYFS.realPath(parent), name); + var mode = parent.mount.opts.fs.lstat(path).mode; + var node = PROXYFS.createNode(parent, name, mode); + return node; + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + mknod(parent, name, mode, dev) { + var node = PROXYFS.createNode(parent, name, mode, dev); + // create the backing node for this in the fs root as well + var path = PROXYFS.realPath(node); + try { + if (FS.isDir(node.mode)) { + node.mount.opts.fs.mkdir(path, node.mode); + } else { + node.mount.opts.fs.writeFile(path, '', { + mode: node.mode, + }); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return node; + }, + rename(oldNode, newDir, newName) { + var oldPath = PROXYFS.realPath(oldNode); + var newPath = PATH.join2(PROXYFS.realPath(newDir), newName); + try { + oldNode.mount.opts.fs.rename(oldPath, newPath); + oldNode.name = newName; + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + unlink(parent, name) { + var path = PATH.join2(PROXYFS.realPath(parent), name); + try { + parent.mount.opts.fs.unlink(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + rmdir(parent, name) { + var path = PATH.join2(PROXYFS.realPath(parent), name); + try { + parent.mount.opts.fs.rmdir(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + readdir(node) { + var path = PROXYFS.realPath(node); + try { + return node.mount.opts.fs.readdir(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + symlink(parent, newName, oldPath) { + var newPath = PATH.join2(PROXYFS.realPath(parent), newName); + try { + parent.mount.opts.fs.symlink(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + readlink(node) { + var path = PROXYFS.realPath(node); + try { + return node.mount.opts.fs.readlink(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + }, + stream_ops: { + open(stream) { + var path = PROXYFS.realPath(stream.node); + try { + stream.nfd = stream.node.mount.opts.fs.open( + path, + stream.flags + ); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + close(stream) { + try { + stream.node.mount.opts.fs.close(stream.nfd); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + read(stream, buffer, offset, length, position) { + try { + return stream.node.mount.opts.fs.read( + stream.nfd, + buffer, + offset, + length, + position + ); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + write(stream, buffer, offset, length, position) { + try { + return stream.node.mount.opts.fs.write( + stream.nfd, + buffer, + offset, + length, + position + ); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + llseek(stream, offset, whence) { + var position = offset; + if (whence === 1) { + position += stream.position; + } else if (whence === 2) { + if (FS.isFile(stream.node.mode)) { + try { + var stat = stream.node.node_ops.getattr( + stream.node + ); + position += stat.size; + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + } + } + + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + + return position; + }, + }, + }; + + var FS_createDataFile = (...args) => FS.createDataFile(...args); + + var getUniqueRunDependency = (id) => { + return id; + }; + + var FS_handledByPreloadPlugin = async (byteArray, fullname) => { + // Ensure plugins are ready. + if (typeof Browser != 'undefined') Browser.init(); + + for (var plugin of preloadPlugins) { + if (plugin['canHandle'](fullname)) { + return plugin['handle'](byteArray, fullname); + } + } + // In no plugin handled this file then return the original/unmodified + // byteArray. + return byteArray; + }; + var FS_preloadFile = async ( + parent, + name, + url, + canRead, + canWrite, + 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 + addRunDependency(dep); + + try { + var byteArray = url; + if (typeof url == 'string') { + byteArray = await asyncLoad(url); + } + + byteArray = await FS_handledByPreloadPlugin(byteArray, fullname); + preFinish?.(); + if (!dontCreateFile) { + FS_createDataFile( + parent, + name, + byteArray, + canRead, + canWrite, + canOwn + ); + } + } finally { + removeRunDependency(dep); + } + }; + var FS_createPreloadedFile = ( + parent, + name, + url, + canRead, + canWrite, + onload, + onerror, + dontCreateFile, + canOwn, + preFinish + ) => { + FS_preloadFile( + parent, + name, + url, + canRead, + canWrite, + dontCreateFile, + canOwn, + preFinish + ) + .then(onload) + .catch(onerror); + }; + var FS = { + root: null, + mounts: [], + devices: {}, + streams: [], + nextInode: 1, + nameTable: null, + currentPath: '/', + initialized: false, + ignorePermissions: true, + filesystems: null, + syncFSRequests: 0, + readFiles: {}, + ErrnoError: class { + 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) { + this.errno = errno; + } + }, + FSStream: class { + shared = {}; + get object() { + return this.node; + } + set object(val) { + this.node = val; + } + get isRead() { + return (this.flags & 2097155) !== 1; + } + get isWrite() { + return (this.flags & 2097155) !== 0; + } + get isAppend() { + return this.flags & 1024; + } + get flags() { + return this.shared.flags; + } + set flags(val) { + this.shared.flags = val; + } + get position() { + return this.shared.position; + } + set position(val) { + this.shared.position = val; + } + }, + FSNode: class { + node_ops = {}; + stream_ops = {}; + readMode = 292 | 73; + writeMode = 146; + mounted = null; + constructor(parent, name, mode, rdev) { + if (!parent) { + parent = this; // root node sets parent to itself + } + 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); + if (FS.isRoot(current)) { + path = + current_path + + '/' + + parts.slice(i + 1).join('/'); + // We're making progress here, don't let many consecutive ..'s + // lead to ELOOP + nlinks--; + continue linkloop; + } else { + current = current.parent; + } + continue; + } + + current_path = PATH.join2(current_path, parts[i]); + try { + current = FS.lookupNode(current, parts[i]); + } catch (e) { + // if 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) { + 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' || // opening for write + 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) { + // 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 & 0xff, + makedev: (ma, mi) => (ma << 8) | mi, + registerDevice(dev, ops) { + FS.devices[dev] = { stream_ops: ops }; + }, + getDevice: (dev) => FS.devices[dev], + getMounts(mount) { + var mounts = []; + var check = [mount]; + + while (check.length) { + var m = check.pop(); + + mounts.push(m); + + check.push(...m.mounts); + } + + return mounts; + }, + syncfs(populate, callback) { + if (typeof populate == 'function') { + callback = populate; + populate = false; + } + + FS.syncFSRequests++; + + if (FS.syncFSRequests > 1) { + err( + `warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work` + ); + } + + var mounts = FS.getMounts(FS.root.mount); + var completed = 0; + + function doCallback(errCode) { + FS.syncFSRequests--; + return callback(errCode); + } + + function done(errCode) { + if (errCode) { + if (!done.errored) { + done.errored = true; + return doCallback(errCode); + } + return; + } + if (++completed >= mounts.length) { + doCallback(null); + } + } + + // sync all mounts + for (var mount of mounts) { + if (mount.type.syncfs) { + mount.type.syncfs(mount, populate, done); + } else { + done(null); + } + } + }, + mount(type, opts, mountpoint) { + var root = mountpoint === '/'; + var pseudo = !mountpoint; + var node; + + if (root && FS.root) { + throw new FS.ErrnoError(10); + } else if (!root && !pseudo) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + + mountpoint = lookup.path; // use the absolute path + node = lookup.node; + + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(10); + } + } + + 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); + + for (var [hash, current] of Object.entries(FS.nameTable)) { + while (current) { + var next = current.name_next; + + if (mounts.includes(current.mount)) { + FS.destroyNode(current); + } + + current = next; + } + } + + // no longer a mountpoint + node.mounted = null; + + // remove this mount from the child mounts + var idx = node.mount.mounts.indexOf(mount); + node.mount.mounts.splice(idx, 1); + }, + lookup(parent, name) { + return parent.node_ops.lookup(parent, name); + }, + mknod(path, mode, dev) { + var 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 = 0o666) { + mode &= 4095; + mode |= 32768; + return FS.mknod(path, mode, 0); + }, + mkdir(path, mode = 0o777) { + 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 = 0o666; + } + 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) { + // not fatal + } + // 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, + // we ignore the uid / gid for now + }); + }, + 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: atime, + mtime: mtime, + }); + }, + open(path, flags, mode = 0o666) { + 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 | 0o777, 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 & 0o777); + } + 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) { + 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) { + 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) { + 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') { + abort(`Invalid encoding type "${opts.encoding}"`); + } + 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') { + buf = UTF8ArrayToString(buf); + } + FS.close(stream); + return buf; + }, + writeFile(path, data, opts = {}) { + opts.flags = opts.flags || 577; + var stream = FS.open(path, opts.flags, opts.mode); + if (typeof data == 'string') { + data = new Uint8Array(intArrayFromString(data, true)); + } + if (ArrayBuffer.isView(data)) { + FS.write( + stream, + data, + 0, + data.byteLength, + undefined, + opts.canOwn + ); + } else { + abort('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); + }, + staticInit() { + FS.nameTable = new Array(4096); + + FS.mount(MEMFS, {}, '/'); + + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + FS.createSpecialDirectories(); + + FS.filesystems = { + MEMFS: MEMFS, + NODEFS: NODEFS, + PROXYFS: PROXYFS, + }; + }, + init(input, output, error) { + 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 /* ignored */) { + 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 (globalThis.XMLHttpRequest) { + abort( + 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' + ); + } else { + // Command-line. + try { + obj.contents = readBinary(obj.url); + } 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 + ) + ) + abort( + "Couldn't load " + url + '. Status: ' + xhr.status + ); + var datalength = Number( + xhr.getResponseHeader('Content-length') + ); + var header; + var hasByteServing = + (header = xhr.getResponseHeader('Accept-Ranges')) && + header === 'bytes'; + var usesGzip = + (header = xhr.getResponseHeader('Content-Encoding')) && + header === 'gzip'; + + var 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) + abort( + 'invalid range (' + + from + + ', ' + + to + + ') or no bytes requested!' + ); + if (to > datalength - 1) + abort( + 'only ' + + datalength + + ' bytes available! programmer error!' + ); + + // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + if (datalength !== chunkSize) + xhr.setRequestHeader( + 'Range', + 'bytes=' + from + '-' + to + ); + + // 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 + ) + ) + abort( + "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') + abort('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 (globalThis.XMLHttpRequest) { + if (!ENVIRONMENT_IS_WORKER) + abort( + '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: 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 = {}; + for (const [key, fn] of Object.entries(node.stream_ops)) { + 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); + 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, HEAP8, ptr, length, position); + return { ptr, allocated: true }; + }; + node.stream_ops = stream_ops; + return node; + }, + }; + + var findLibraryFS = (libName, rpath) => { + // If we're preloading a dynamic library, the runtime is not ready to call + // __wasmfs_identify or __emscripten_find_dylib. So just quit out. + // + // This means that DT_NEEDED for the main module and transitive dependencies + // of it won't work with this code path. Similarly, it means that calling + // loadDynamicLibrary in a preRun hook can't use this code path. + if (!runtimeInitialized) { + return undefined; + } + if (PATH.isAbs(libName)) { + try { + FS.lookupPath(libName); + return libName; + } catch (e) { + return undefined; + } + } + var rpathResolved = (rpath?.paths || []).map((p) => + replaceORIGIN(rpath?.parentLibPath, p) + ); + return withStackSave(() => { + // In dylink.c we use: `char buf[2*NAME_MAX+2];` and NAME_MAX is 255. + // So we use the same size here. + var bufSize = 2 * 255 + 2; + var buf = stackAlloc(bufSize); + var rpathC = stringToUTF8OnStack(rpathResolved.join(':')); + var libNameC = stringToUTF8OnStack(libName); + var resLibNameC = __emscripten_find_dylib( + buf, + rpathC, + libNameC, + bufSize + ); + return resLibNameC ? UTF8ToString(resLibNameC) : undefined; + }); + }; + + var registerDynCallSymbols = (exports) => { + for (var [sym, exp] of Object.entries(exports)) { + if (sym.startsWith('dynCall_')) { + var sig = sym.substring(8); + if (!dynCalls.hasOwnProperty(sig)) { + dynCalls[sig] = exp; + } + } + } + }; + + /** + * @param {number=} handle + * @param {Object=} localScope + */ + function loadDynamicLibrary( + libName, + flags = { global: true, nodelete: true }, + localScope, + handle + ) { + // when loadDynamicLibrary did not have flags, libraries were loaded + // globally & permanently + + var dso = LDSO.loadedLibsByName[libName]; + if (dso) { + // the library is being loaded or has been loaded already. + if (!flags.global) { + if (localScope) { + Object.assign(localScope, dso.exports); + } + registerDynCallSymbols(dso.exports); + } else if (!dso.global) { + // The library was previously loaded only locally but not + // we have a request with global=true. + dso.global = true; + mergeLibSymbols(dso.exports, libName); + } + // same for "nodelete" + if (flags.nodelete && dso.refcount !== Infinity) { + dso.refcount = Infinity; + } + dso.refcount++; + if (handle) { + LDSO.loadedLibsByHandle[handle] = dso; + } + return flags.loadAsync ? Promise.resolve(true) : true; + } + + // allocate new DSO + dso = newDSO(libName, handle, 'loading'); + dso.refcount = flags.nodelete ? Infinity : 1; + dso.global = flags.global; + + // libName -> libData + function loadLibData() { + // for wasm, we can use fetch for async, but for fs mode we can only imitate it + if (handle) { + var data = HEAPU32[(handle + 28) >> 2]; + var dataSize = HEAPU32[(handle + 32) >> 2]; + if (data && dataSize) { + var libData = HEAP8.slice(data, data + dataSize); + return flags.loadAsync ? Promise.resolve(libData) : libData; + } + } + + var f = findLibraryFS(libName, flags.rpath); + if (f) { + var libData = FS.readFile(f, { encoding: 'binary' }); + return flags.loadAsync ? Promise.resolve(libData) : libData; + } + + var libFile = locateFile(libName); + if (flags.loadAsync) { + return asyncLoad(libFile); + } + + // load the binary synchronously + if (!readBinary) { + throw new Error( + `${libFile}: file not found, and synchronous loading of external files is not available` + ); + } + return readBinary(libFile); + } + + // libName -> exports + function getExports() { + // lookup preloaded cache first + var preloaded = preloadedWasm[libName]; + if (preloaded) { + return flags.loadAsync ? Promise.resolve(preloaded) : preloaded; + } + + // module not preloaded - load lib data and create new module from it + if (flags.loadAsync) { + return loadLibData().then((libData) => + loadWebAssemblyModule( + libData, + flags, + libName, + localScope, + handle + ) + ); + } + + return loadWebAssemblyModule( + loadLibData(), + flags, + libName, + localScope, + handle + ); + } + + // module for lib is loaded - update the dso & global namespace + function moduleLoaded(exports) { + if (dso.global) { + mergeLibSymbols(exports, libName); + } else if (localScope) { + Object.assign(localScope, exports); + registerDynCallSymbols(exports); + } + dso.exports = exports; + } + + if (flags.loadAsync) { + return getExports().then((exports) => { + moduleLoaded(exports); + return true; + }); + } + + moduleLoaded(getExports()); + return true; + } -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}`); + var reportUndefinedSymbols = () => { + for (var [symName, entry] of Object.entries(GOT)) { + if (entry.value == -1) { + var value = resolveGlobalSymbol(symName, true).sym; + if (!value && !entry.required) { + // Ignore undefined symbols that are imported as weak. + entry.value = 0; + continue; + } + if (typeof value == 'function') { + /** @suppress {checkTypes} */ + entry.value = addFunction(value, value.sig); + } else if (typeof value == 'number') { + entry.value = value; + } else { + throw new Error( + `bad export type for '${symName}': ${typeof value} (${value})` + ); + } + } + } + }; + + var loadDylibs = async () => { + if (!dynamicLibraries.length) { + reportUndefinedSymbols(); + return; + } + + addRunDependency('loadDylibs'); + + // Load binaries asynchronously + for (var lib of dynamicLibraries) { + await loadDynamicLibrary(lib, { + loadAsync: true, + global: true, + nodelete: true, + allowUndefined: true, + }); + } + // we got them all, wonderful + reportUndefinedSymbols(); + + removeRunDependency('loadDylibs'); + }; + + var noExitRuntime = false; + + /** + * @param {number} ptr + * @param {number} value + * @param {string} type + */ + function setValue(ptr, value, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': + HEAP8[ptr] = value; + break; + case 'i8': + HEAP8[ptr] = value; + break; + case 'i16': + HEAP16[ptr >> 1] = value; + break; + case 'i32': + HEAP32[ptr >> 2] = value; + break; + case 'i64': + HEAP64[ptr >> 3] = BigInt(value); + break; + case 'float': + HEAPF32[ptr >> 2] = value; + break; + case 'double': + HEAPF64[ptr >> 3] = value; + break; + case '*': + HEAPU32[ptr >> 2] = value; + break; + default: + abort(`invalid type for setValue: ${type}`); + } + } - abort(reason); - } -} + var ___assert_fail = (condition, filename, line, func) => + abort( + `Assertion failed: ${UTF8ToString(condition)}, at: ` + + [ + filename ? UTF8ToString(filename) : 'unknown filename', + line, + func ? UTF8ToString(func) : 'unknown function', + ] + ); + ___assert_fail.sig = 'vppip'; + + var ___asyncify_data = new WebAssembly.Global( + { value: 'i32', mutable: true }, + 0 + ); + + var ___asyncify_state = new WebAssembly.Global( + { value: 'i32', mutable: true }, + 0 + ); + + var ___call_sighandler = (fp, sig) => + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + sig + ); + ___call_sighandler.sig = 'vpi'; + + var exceptionLast = 0; + + 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) { + HEAPU32[(this.ptr + 4) >> 2] = type; + } + + get_type() { + return HEAPU32[(this.ptr + 4) >> 2]; + } + + set_destructor(destructor) { + HEAPU32[(this.ptr + 8) >> 2] = destructor; + } + + get_destructor() { + return HEAPU32[(this.ptr + 8) >> 2]; + } + + set_caught(caught) { + caught = caught ? 1 : 0; + HEAP8[this.ptr + 12] = caught; + } + + get_caught() { + return HEAP8[this.ptr + 12] != 0; + } + + set_rethrown(rethrown) { + rethrown = rethrown ? 1 : 0; + HEAP8[this.ptr + 13] = rethrown; + } + + get_rethrown() { + return HEAP8[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) { + HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr; + } + + get_adjusted_ptr() { + return HEAPU32[(this.ptr + 16) >> 2]; + } + } -async function instantiateAsync(binary, binaryFile, imports) { - if (!binary - // Avoid instantiateStreaming() on Node.js environment for now, as while - // Node.js v18.1.0 implements it, it does not have a full fetch() - // implementation yet. - // - // Reference: - // https://github.com/emscripten-core/emscripten/pull/16917 - && !ENVIRONMENT_IS_NODE - ) { - 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'); - // fall back of instantiateArrayBuffer below - }; - } - return instantiateArrayBuffer(binaryFile, imports); -} + var setTempRet0 = (val) => __emscripten_tempret_set(val); + var findMatchingCatch = (args) => { + var thrown = exceptionLast; + if (!thrown) { + // just pass through the null ptr + setTempRet0(0); + return 0; + } + var info = new ExceptionInfo(thrown); + info.set_adjusted_ptr(thrown); + var thrownType = info.get_type(); + if (!thrownType) { + // just pass through the thrown ptr + setTempRet0(0); + return thrown; + } + + // can_catch receives a **, add indirection + // The different catch blocks are denoted by different types. + // Due to inheritance, those types may not precisely match the + // type of the thrown object. Find one which matches, and + // return the type of the catch block which should be called. + for (var caughtType of args) { + if (caughtType === 0 || caughtType === thrownType) { + // Catch all clause matched or exactly the same type is caught + break; + } + var adjusted_ptr_addr = info.ptr + 16; + if (___cxa_can_catch(caughtType, thrownType, adjusted_ptr_addr)) { + setTempRet0(caughtType); + return thrown; + } + } + setTempRet0(thrownType); + return thrown; + }; + var ___cxa_find_matching_catch_2 = () => findMatchingCatch([]); + ___cxa_find_matching_catch_2.sig = 'p'; + + var ___resumeException = (ptr) => { + if (!exceptionLast) { + exceptionLast = ptr; + } + throw exceptionLast; + }; + ___resumeException.sig = 'vp'; + + 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) { + HEAPU32[buf >> 2] = stat.dev; + HEAPU32[(buf + 4) >> 2] = stat.mode; + HEAPU32[(buf + 8) >> 2] = stat.nlink; + HEAPU32[(buf + 12) >> 2] = stat.uid; + HEAPU32[(buf + 16) >> 2] = stat.gid; + HEAPU32[(buf + 20) >> 2] = stat.rdev; + HEAP64[(buf + 24) >> 3] = BigInt(stat.size); + HEAP32[(buf + 32) >> 2] = 4096; + HEAP32[(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 / 1000)); + HEAPU32[(buf + 48) >> 2] = (atime % 1000) * 1000 * 1000; + HEAP64[(buf + 56) >> 3] = BigInt(Math.floor(mtime / 1000)); + HEAPU32[(buf + 64) >> 2] = (mtime % 1000) * 1000 * 1000; + HEAP64[(buf + 72) >> 3] = BigInt(Math.floor(ctime / 1000)); + HEAPU32[(buf + 80) >> 2] = (ctime % 1000) * 1000 * 1000; + HEAP64[(buf + 88) >> 3] = BigInt(stat.ino); + return 0; + }, + writeStatFs(buf, stats) { + HEAPU32[(buf + 4) >> 2] = stats.bsize; + HEAPU32[(buf + 60) >> 2] = stats.bsize; + HEAP64[(buf + 8) >> 3] = BigInt(stats.blocks); + HEAP64[(buf + 16) >> 3] = BigInt(stats.bfree); + HEAP64[(buf + 24) >> 3] = BigInt(stats.bavail); + HEAP64[(buf + 32) >> 3] = BigInt(stats.files); + HEAP64[(buf + 40) >> 3] = BigInt(stats.ffree); + HEAPU32[(buf + 48) >> 2] = stats.fsid; + HEAPU32[(buf + 64) >> 2] = stats.flags; // ST_NOSUID + HEAPU32[(buf + 56) >> 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 = HEAPU8.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; + }, + }; + var ___syscall__newselect = function ( + nfds, + readfds, + writefds, + exceptfds, + timeout + ) { + try { + // readfds are supported, + // writefds checks socket open status + // exceptfds are supported, although on web, such exceptional conditions never arise in web sockets + // and so the exceptfds list will always return empty. + // timeout is supported, although on SOCKFS and PIPEFS these are ignored and always treated as 0 - fully async + + var total = 0; + + var srcReadLow = readfds ? HEAP32[readfds >> 2] : 0, + srcReadHigh = readfds ? HEAP32[(readfds + 4) >> 2] : 0; + var srcWriteLow = writefds ? HEAP32[writefds >> 2] : 0, + srcWriteHigh = writefds ? HEAP32[(writefds + 4) >> 2] : 0; + var srcExceptLow = exceptfds ? HEAP32[exceptfds >> 2] : 0, + srcExceptHigh = exceptfds ? HEAP32[(exceptfds + 4) >> 2] : 0; + + var dstReadLow = 0, + dstReadHigh = 0; + var dstWriteLow = 0, + dstWriteHigh = 0; + var dstExceptLow = 0, + dstExceptHigh = 0; + + var allLow = + (readfds ? HEAP32[readfds >> 2] : 0) | + (writefds ? HEAP32[writefds >> 2] : 0) | + (exceptfds ? HEAP32[exceptfds >> 2] : 0); + var allHigh = + (readfds ? HEAP32[(readfds + 4) >> 2] : 0) | + (writefds ? HEAP32[(writefds + 4) >> 2] : 0) | + (exceptfds ? HEAP32[(exceptfds + 4) >> 2] : 0); + + var check = (fd, low, high, val) => + fd < 32 ? low & val : high & val; + + for (var fd = 0; fd < nfds; fd++) { + var mask = 1 << (fd % 32); + if (!check(fd, allLow, allHigh, mask)) { + continue; // index isn't in the set + } + + var stream = SYSCALLS.getStreamFromFD(fd); + + var flags = SYSCALLS.DEFAULT_POLLMASK; + + if (stream.stream_ops?.poll) { + var timeoutInMillis = -1; + if (timeout) { + // select(2) is declared to accept "struct timeval { time_t tv_sec; suseconds_t tv_usec; }". + // However, musl passes the two values to the syscall as an array of long values. + // Note that sizeof(time_t) != sizeof(long) in wasm32. The former is 8, while the latter is 4. + // This means using "C_STRUCTS.timeval.tv_usec" leads to a wrong offset. + // So, instead, we use POINTER_SIZE. + var tv_sec = readfds ? HEAP32[timeout >> 2] : 0, + tv_usec = readfds ? HEAP32[(timeout + 4) >> 2] : 0; + timeoutInMillis = (tv_sec + tv_usec / 1000000) * 1000; + } + flags = stream.stream_ops.poll(stream, timeoutInMillis); + } + + if (flags & 1 && check(fd, srcReadLow, srcReadHigh, mask)) { + fd < 32 + ? (dstReadLow = dstReadLow | mask) + : (dstReadHigh = dstReadHigh | mask); + total++; + } + if (flags & 4 && check(fd, srcWriteLow, srcWriteHigh, mask)) { + fd < 32 + ? (dstWriteLow = dstWriteLow | mask) + : (dstWriteHigh = dstWriteHigh | mask); + total++; + } + if (flags & 2 && check(fd, srcExceptLow, srcExceptHigh, mask)) { + fd < 32 + ? (dstExceptLow = dstExceptLow | mask) + : (dstExceptHigh = dstExceptHigh | mask); + total++; + } + } + + if (readfds) { + HEAP32[readfds >> 2] = dstReadLow; + HEAP32[(readfds + 4) >> 2] = dstReadHigh; + } + if (writefds) { + HEAP32[writefds >> 2] = dstWriteLow; + HEAP32[(writefds + 4) >> 2] = dstWriteHigh; + } + if (exceptfds) { + HEAP32[exceptfds >> 2] = dstExceptLow; + HEAP32[(exceptfds + 4) >> 2] = dstExceptHigh; + } + + return total; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + }; + ___syscall__newselect.sig = 'iipppp'; + + var SOCKFS = { + websocketArgs: {}, + callbacks: {}, + on(event, callback) { + SOCKFS.callbacks[event] = callback; + }, + emit(event, param) { + SOCKFS.callbacks[event]?.(param); + }, + mount(mount) { + // The incomming Module['websocket'] can be used for configuring + // configuring subprotocol/url, etc + SOCKFS.websocketArgs = Module['websocket'] || {}; + // Add the Event registration mechanism to the exported websocket configuration + // object so we can register network callbacks from native JavaScript too. + // For more documentation see system/include/emscripten/emscripten.h + (Module['websocket'] ??= {})['on'] = SOCKFS.on; + + return FS.createNode(null, '/', 16895, 0); + }, + createSocket(family, type, protocol) { + // Emscripten only supports AF_INET + if (family != 2) { + throw new FS.ErrnoError(5); + } + type &= ~526336; // Some applications may pass it; it makes no sense for a single process. + // Emscripten only supports SOCK_STREAM and SOCK_DGRAM + if (type != 1 && type != 2) { + throw new FS.ErrnoError(28); + } + var streaming = type == 1; + if (streaming && protocol && protocol != 6) { + throw new FS.ErrnoError(66); // if SOCK_STREAM, must be tcp or 0. + } + + // create our internal socket structure + var sock = { + family, + type, + protocol, + server: null, + error: null, // Used in getsockopt for SOL_SOCKET/SO_ERROR test + peers: {}, + pending: [], + recv_queue: [], + sock_ops: SOCKFS.websocket_sock_ops, + }; + + // create the filesystem node to store the socket structure + var name = SOCKFS.nextname(); + var node = FS.createNode(SOCKFS.root, name, 49152, 0); + node.sock = sock; + + // and the wrapping stream that enables library functions such + // as read and write to indirectly interact with the socket + var stream = FS.createStream({ + path: name, + node, + flags: 2, + seekable: false, + stream_ops: SOCKFS.stream_ops, + }); + + // map the new stream to the socket structure (sockets have a 1:1 + // relationship with a stream) + sock.stream = stream; + + return sock; + }, + getSocket(fd) { + var stream = FS.getStream(fd); + if (!stream || !FS.isSocket(stream.node.mode)) { + return null; + } + return stream.node.sock; + }, + stream_ops: { + poll(stream) { + var sock = stream.node.sock; + return sock.sock_ops.poll(sock); + }, + ioctl(stream, request, varargs) { + var sock = stream.node.sock; + return sock.sock_ops.ioctl(sock, request, varargs); + }, + read(stream, buffer, offset, length, position /* ignored */) { + var sock = stream.node.sock; + var msg = sock.sock_ops.recvmsg(sock, length); + if (!msg) { + // socket is closed + return 0; + } + buffer.set(msg.buffer, offset); + return msg.buffer.length; + }, + write(stream, buffer, offset, length, position /* ignored */) { + var sock = stream.node.sock; + return sock.sock_ops.sendmsg(sock, buffer, offset, length); + }, + close(stream) { + var sock = stream.node.sock; + sock.sock_ops.close(sock); + }, + }, + nextname() { + if (!SOCKFS.nextname.current) { + SOCKFS.nextname.current = 0; + } + return `socket[${SOCKFS.nextname.current++}]`; + }, + websocket_sock_ops: { + createPeer(sock, addr, port) { + var ws; + + if (typeof addr == 'object') { + ws = addr; + addr = null; + port = null; + } + + if (ws) { + // for sockets that've already connected (e.g. we're the server) + // we can inspect the _socket property for the address + if (ws._socket) { + addr = ws._socket.remoteAddress; + port = ws._socket.remotePort; + } + // if we're just now initializing a connection to the remote, + // inspect the url property + else { + var result = /ws[s]?:\/\/([^:]+):(\d+)/.exec(ws.url); + if (!result) { + throw new Error( + 'WebSocket URL must be in the format ws(s)://address:port' + ); + } + addr = result[1]; + port = parseInt(result[2], 10); + } + } else { + // create the actual websocket object and connect + try { + // The default value is 'ws://' the replace is needed because the compiler replaces '//' comments with '#' + // comments without checking context, so we'd end up with ws:#, the replace swaps the '#' for '//' again. + var url = 'ws://'.replace('#', '//'); + // Make the WebSocket subprotocol (Sec-WebSocket-Protocol) default to binary if no configuration is set. + var subProtocols = 'binary'; // The default value is 'binary' + // The default WebSocket options + var opts = undefined; + + // Fetch runtime WebSocket URL config. + if ('function' === typeof SOCKFS.websocketArgs['url']) { + url = SOCKFS.websocketArgs['url'](...arguments); + } else if ( + 'string' === typeof SOCKFS.websocketArgs['url'] + ) { + url = SOCKFS.websocketArgs['url']; + } + // Fetch runtime WebSocket subprotocol config. + if (SOCKFS.websocketArgs['subprotocol']) { + subProtocols = SOCKFS.websocketArgs['subprotocol']; + } else if ( + SOCKFS.websocketArgs['subprotocol'] === null + ) { + subProtocols = 'null'; + } + + if (url === 'ws://' || url === 'wss://') { + // Is the supplied URL config just a prefix, if so complete it. + var parts = addr.split('/'); + url = + url + + parts[0] + + ':' + + port + + '/' + + parts.slice(1).join('/'); + } + + if (subProtocols !== 'null') { + // The regex trims the string (removes spaces at the beginning and end, then splits the string by + // , into an Array. Whitespace removal is important for Websockify and ws. + subProtocols = subProtocols + .replace(/^ +| +$/g, '') + .split(/ *, */); + + opts = subProtocols; + } + + // If node we use the ws library. + var WebSocketConstructor; + if (ENVIRONMENT_IS_NODE) { + WebSocketConstructor = + /** @type{(typeof WebSocket)} */ ( + require('ws') + ); + } else { + WebSocketConstructor = WebSocket; + } + if (Module['websocket']['decorator']) { + WebSocketConstructor = + Module['websocket']['decorator']( + WebSocketConstructor + ); + } + ws = new WebSocketConstructor(url, opts); + ws.binaryType = 'arraybuffer'; + } catch (e) { + throw new FS.ErrnoError(23); + } + } + + var peer = { + addr, + port, + socket: ws, + msg_send_queue: [], + }; + + SOCKFS.websocket_sock_ops.addPeer(sock, peer); + SOCKFS.websocket_sock_ops.handlePeerEvents(sock, peer); + + // if this is a bound dgram socket, send the port number first to allow + // us to override the ephemeral port reported to us by remotePort on the + // remote end. + if (sock.type === 2 && typeof sock.sport != 'undefined') { + peer.msg_send_queue.push( + new Uint8Array([ + 255, + 255, + 255, + 255, + 'p'.charCodeAt(0), + 'o'.charCodeAt(0), + 'r'.charCodeAt(0), + 't'.charCodeAt(0), + (sock.sport & 0xff00) >> 8, + sock.sport & 0xff, + ]) + ); + } + + return peer; + }, + getPeer(sock, addr, port) { + return sock.peers[addr + ':' + port]; + }, + addPeer(sock, peer) { + sock.peers[peer.addr + ':' + peer.port] = peer; + }, + removePeer(sock, peer) { + delete sock.peers[peer.addr + ':' + peer.port]; + }, + handlePeerEvents(sock, peer) { + var first = true; + + var handleOpen = function () { + sock.connecting = false; + SOCKFS.emit('open', sock.stream.fd); + + try { + var queued = peer.msg_send_queue.shift(); + while (queued) { + peer.socket.send(queued); + queued = peer.msg_send_queue.shift(); + } + } catch (e) { + // not much we can do here in the way of proper error handling as we've already + // lied and said this data was sent. shut it down. + peer.socket.close(); + } + }; + + function handleMessage(data) { + if (typeof data == 'string') { + var encoder = new TextEncoder(); // should be utf-8 + data = encoder.encode(data); // make a typed array from the string + } else { + if (data.byteLength == 0) { + // An empty ArrayBuffer will emit a pseudo disconnect event + // as recv/recvmsg will return zero which indicates that a socket + // has performed a shutdown although the connection has not been disconnected yet. + return; + } + data = new Uint8Array(data); // make a typed array view on the array buffer + } + + // if this is the port message, override the peer's port with it + var wasfirst = first; + first = false; + if ( + wasfirst && + data.length === 10 && + data[0] === 255 && + data[1] === 255 && + data[2] === 255 && + data[3] === 255 && + data[4] === 'p'.charCodeAt(0) && + data[5] === 'o'.charCodeAt(0) && + data[6] === 'r'.charCodeAt(0) && + data[7] === 't'.charCodeAt(0) + ) { + // update the peer's port and it's key in the peer map + var newport = (data[8] << 8) | data[9]; + SOCKFS.websocket_sock_ops.removePeer(sock, peer); + peer.port = newport; + SOCKFS.websocket_sock_ops.addPeer(sock, peer); + return; + } + + sock.recv_queue.push({ + addr: peer.addr, + port: peer.port, + data: data, + }); + SOCKFS.emit('message', sock.stream.fd); + } + + if (ENVIRONMENT_IS_NODE) { + peer.socket.on('open', handleOpen); + peer.socket.on('message', function (data, isBinary) { + if (!isBinary) { + return; + } + handleMessage(new Uint8Array(data).buffer); // copy from node Buffer -> ArrayBuffer + }); + peer.socket.on('close', function () { + SOCKFS.emit('close', sock.stream.fd); + }); + peer.socket.on('error', function (error) { + // Although the ws library may pass errors that may be more descriptive than + // ECONNREFUSED they are not necessarily the expected error code e.g. + // ENOTFOUND on getaddrinfo seems to be node.js specific, so using ECONNREFUSED + // is still probably the most useful thing to do. + sock.error = 14; // Used in getsockopt for SOL_SOCKET/SO_ERROR test. + SOCKFS.emit('error', [ + sock.stream.fd, + sock.error, + 'ECONNREFUSED: Connection refused', + ]); + // don't throw + }); + } else { + peer.socket.onopen = handleOpen; + peer.socket.onclose = function () { + SOCKFS.emit('close', sock.stream.fd); + }; + peer.socket.onmessage = function peer_socket_onmessage( + event + ) { + handleMessage(event.data); + }; + peer.socket.onerror = function (error) { + // The WebSocket spec only allows a 'simple event' to be thrown on error, + // so we only really know as much as ECONNREFUSED. + sock.error = 14; // Used in getsockopt for SOL_SOCKET/SO_ERROR test. + SOCKFS.emit('error', [ + sock.stream.fd, + sock.error, + 'ECONNREFUSED: Connection refused', + ]); + }; + } + }, + poll(sock) { + if (sock.type === 1 && sock.server) { + // listen sockets should only say they're available for reading + // if there are pending clients. + return sock.pending.length ? 64 | 1 : 0; + } + + var mask = 0; + var dest = + sock.type === 1 // we only care about the socket state for connection-based sockets + ? SOCKFS.websocket_sock_ops.getPeer( + sock, + sock.daddr, + sock.dport + ) + : null; + + if ( + sock.recv_queue.length || + !dest || // connection-less sockets are always ready to read + (dest && dest.socket.readyState === dest.socket.CLOSING) || + (dest && dest.socket.readyState === dest.socket.CLOSED) + ) { + // let recv return 0 once closed + mask |= 64 | 1; + } + + if ( + !dest || // connection-less sockets are always ready to write + (dest && dest.socket.readyState === dest.socket.OPEN) + ) { + mask |= 4; + } + + if ( + (dest && dest.socket.readyState === dest.socket.CLOSING) || + (dest && dest.socket.readyState === dest.socket.CLOSED) + ) { + // When an non-blocking connect fails mark the socket as writable. + // Its up to the calling code to then use getsockopt with SO_ERROR to + // retrieve the error. + // See https://man7.org/linux/man-pages/man2/connect.2.html + if (sock.connecting) { + mask |= 4; + } else { + mask |= 16; + } + } + + return mask; + }, + ioctl(sock, request, arg) { + switch (request) { + case 21531: + var bytes = 0; + if (sock.recv_queue.length) { + bytes = sock.recv_queue[0].data.length; + } + HEAP32[arg >> 2] = bytes; + return 0; + case 21537: + var on = HEAP32[arg >> 2]; + if (on) { + sock.stream.flags |= 2048; + } else { + sock.stream.flags &= ~2048; + } + return 0; + default: + return 28; + } + }, + close(sock) { + // if we've spawned a listen server, close it + if (sock.server) { + try { + sock.server.close(); + } catch (e) {} + sock.server = null; + } + // close any peer connections + for (var peer of Object.values(sock.peers)) { + try { + peer.socket.close(); + } catch (e) {} + SOCKFS.websocket_sock_ops.removePeer(sock, peer); + } + return 0; + }, + bind(sock, addr, port) { + if ( + typeof sock.saddr != 'undefined' || + typeof sock.sport != 'undefined' + ) { + throw new FS.ErrnoError(28); // already bound + } + sock.saddr = addr; + sock.sport = port; + // in order to emulate dgram sockets, we need to launch a listen server when + // binding on a connection-less socket + // note: this is only required on the server side + if (sock.type === 2) { + // close the existing server if it exists + if (sock.server) { + sock.server.close(); + sock.server = null; + } + // swallow error operation not supported error that occurs when binding in the + // browser where this isn't supported + try { + sock.sock_ops.listen(sock, 0); + } catch (e) { + if (!(e.name === 'ErrnoError')) throw e; + if (e.errno !== 138) throw e; + } + } + }, + connect(sock, addr, port) { + if (sock.server) { + throw new FS.ErrnoError(138); + } + + // TODO autobind + // if (!sock.addr && sock.type == 2) { + // } + + // early out if we're already connected / in the middle of connecting + if ( + typeof sock.daddr != 'undefined' && + typeof sock.dport != 'undefined' + ) { + var dest = SOCKFS.websocket_sock_ops.getPeer( + sock, + sock.daddr, + sock.dport + ); + if (dest) { + if (dest.socket.readyState === dest.socket.CONNECTING) { + throw new FS.ErrnoError(7); + } else { + throw new FS.ErrnoError(30); + } + } + } + + // add the socket to our peer list and set our + // destination address / port to match + var peer = SOCKFS.websocket_sock_ops.createPeer( + sock, + addr, + port + ); + sock.daddr = peer.addr; + sock.dport = peer.port; + + // because we cannot synchronously block to wait for the WebSocket + // connection to complete, we return here pretending that the connection + // was a success. + sock.connecting = true; + }, + listen(sock, backlog) { + if (!ENVIRONMENT_IS_NODE) { + throw new FS.ErrnoError(138); + } + if (sock.server) { + throw new FS.ErrnoError(28); // already listening + } + var WebSocketServer = require('ws').Server; + var host = sock.saddr; + if (Module['websocket']['serverDecorator']) { + WebSocketServer = + Module['websocket']['serverDecorator'](WebSocketServer); + } + sock.server = new WebSocketServer({ + host, + port: sock.sport, + // TODO support backlog + }); + SOCKFS.emit('listen', sock.stream.fd); // Send Event with listen fd. + + sock.server.on('connection', function (ws) { + if (sock.type === 1) { + var newsock = SOCKFS.createSocket( + sock.family, + sock.type, + sock.protocol + ); + + // create a peer on the new socket + var peer = SOCKFS.websocket_sock_ops.createPeer( + newsock, + ws + ); + newsock.daddr = peer.addr; + newsock.dport = peer.port; + + // push to queue for accept to pick up + sock.pending.push(newsock); + SOCKFS.emit('connection', newsock.stream.fd); + } else { + // create a peer on the listen socket so calling sendto + // with the listen socket and an address will resolve + // to the correct client + SOCKFS.websocket_sock_ops.createPeer(sock, ws); + SOCKFS.emit('connection', sock.stream.fd); + } + }); + sock.server.on('close', function () { + SOCKFS.emit('close', sock.stream.fd); + sock.server = null; + }); + sock.server.on('error', function (error) { + // Although the ws library may pass errors that may be more descriptive than + // ECONNREFUSED they are not necessarily the expected error code e.g. + // ENOTFOUND on getaddrinfo seems to be node.js specific, so using EHOSTUNREACH + // is still probably the most useful thing to do. This error shouldn't + // occur in a well written app as errors should get trapped in the compiled + // app's own getaddrinfo call. + sock.error = 23; // Used in getsockopt for SOL_SOCKET/SO_ERROR test. + SOCKFS.emit('error', [ + sock.stream.fd, + sock.error, + 'EHOSTUNREACH: Host is unreachable', + ]); + // don't throw + }); + }, + accept(listensock) { + if (!listensock.server || !listensock.pending.length) { + throw new FS.ErrnoError(28); + } + var newsock = listensock.pending.shift(); + newsock.stream.flags = listensock.stream.flags; + return newsock; + }, + getname(sock, peer) { + var addr, port; + if (peer) { + if (sock.daddr === undefined || sock.dport === undefined) { + throw new FS.ErrnoError(53); + } + addr = sock.daddr; + port = sock.dport; + } else { + // TODO saddr and sport will be set for bind()'d UDP sockets, but what + // should we be returning for TCP sockets that've been connect()'d? + addr = sock.saddr || 0; + port = sock.sport || 0; + } + return { addr, port }; + }, + sendmsg(sock, buffer, offset, length, addr, port) { + if (sock.type === 2) { + // connection-less sockets will honor the message address, + // and otherwise fall back to the bound destination address + if (addr === undefined || port === undefined) { + addr = sock.daddr; + port = sock.dport; + } + // if there was no address to fall back to, error out + if (addr === undefined || port === undefined) { + throw new FS.ErrnoError(17); + } + } else { + // connection-based sockets will only use the bound + addr = sock.daddr; + port = sock.dport; + } + + // find the peer for the destination address + var dest = SOCKFS.websocket_sock_ops.getPeer(sock, addr, port); + + // early out if not connected with a connection-based socket + if (sock.type === 1) { + if ( + !dest || + dest.socket.readyState === dest.socket.CLOSING || + dest.socket.readyState === dest.socket.CLOSED + ) { + throw new FS.ErrnoError(53); + } + } + + // create a copy of the incoming data to send, as the WebSocket API + // doesn't work entirely with an ArrayBufferView, it'll just send + // the entire underlying buffer + if (ArrayBuffer.isView(buffer)) { + offset += buffer.byteOffset; + buffer = buffer.buffer; + } + + var data = buffer.slice(offset, offset + length); + + // if we don't have a cached connectionless UDP datagram connection, or + // the TCP socket is still connecting, queue the message to be sent upon + // connect, and lie, saying the data was sent now. + if (!dest || dest.socket.readyState !== dest.socket.OPEN) { + // if we're not connected, open a new connection + if (sock.type === 2) { + if ( + !dest || + dest.socket.readyState === dest.socket.CLOSING || + dest.socket.readyState === dest.socket.CLOSED + ) { + dest = SOCKFS.websocket_sock_ops.createPeer( + sock, + addr, + port + ); + } + } + dest.msg_send_queue.push(data); + return length; + } + + try { + // send the actual data + dest.socket.send(data); + return length; + } catch (e) { + throw new FS.ErrnoError(28); + } + }, + recvmsg(sock, length, flags) { + // http://pubs.opengroup.org/onlinepubs/7908799/xns/recvmsg.html + if (sock.type === 1 && sock.server) { + // tcp servers should not be recv()'ing on the listen socket + throw new FS.ErrnoError(53); + } + + var queued = sock.recv_queue.shift(); + if (!queued) { + if (sock.type === 1) { + var dest = SOCKFS.websocket_sock_ops.getPeer( + sock, + sock.daddr, + sock.dport + ); + + if (!dest) { + // if we have a destination address but are not connected, error out + throw new FS.ErrnoError(53); + } + if ( + dest.socket.readyState === dest.socket.CLOSING || + dest.socket.readyState === dest.socket.CLOSED + ) { + // return null if the socket has closed + return null; + } + // else, our socket is in a valid state but truly has nothing available + throw new FS.ErrnoError(6); + } + throw new FS.ErrnoError(6); + } + + // queued.data will be an ArrayBuffer if it's unadulterated, but if it's + // requeued TCP data it'll be an ArrayBufferView + var queuedLength = queued.data.byteLength || queued.data.length; + var queuedOffset = queued.data.byteOffset || 0; + var queuedBuffer = queued.data.buffer || queued.data; + var bytesRead = Math.min(length, queuedLength); + var res = { + buffer: new Uint8Array( + queuedBuffer, + queuedOffset, + bytesRead + ), + addr: queued.addr, + port: queued.port, + }; + + // push back any unread data for TCP connections + if (flags & 2) { + bytesRead = 0; + } + if (sock.type === 1 && bytesRead < queuedLength) { + var bytesRemaining = queuedLength - bytesRead; + queued.data = new Uint8Array( + queuedBuffer, + queuedOffset + bytesRead, + bytesRemaining + ); + sock.recv_queue.unshift(queued); + } + + return res; + }, + }, + }; + + var getSocketFromFD = (fd) => { + var socket = SOCKFS.getSocket(fd); + if (!socket) throw new FS.ErrnoError(8); + return socket; + }; + + var inetPton4 = (str) => { + var b = str.split('.'); + for (var i = 0; i < 4; i++) { + var tmp = Number(b[i]); + if (isNaN(tmp)) return null; + b[i] = tmp; + } + return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0; + }; + + var inetPton6 = (str) => { + var words; + var w, offset, z, i; + /* http://home.deds.nl/~aeron/regex/ */ + var valid6regx = + /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i; + var parts = []; + if (!valid6regx.test(str)) { + return null; + } + if (str === '::') { + return [0, 0, 0, 0, 0, 0, 0, 0]; + } + // Z placeholder to keep track of zeros when splitting the string on ":" + if (str.startsWith('::')) { + str = str.replace('::', 'Z:'); // leading zeros case + } else { + str = str.replace('::', ':Z:'); + } + + if (str.indexOf('.') > 0) { + // parse IPv4 embedded stress + str = str.replace(new RegExp('[.]', 'g'), ':'); + words = str.split(':'); + words[words.length - 4] = + Number(words[words.length - 4]) + + Number(words[words.length - 3]) * 256; + words[words.length - 3] = + Number(words[words.length - 2]) + + Number(words[words.length - 1]) * 256; + words = words.slice(0, words.length - 2); + } else { + words = str.split(':'); + } + + offset = 0; + z = 0; + for (w = 0; w < words.length; w++) { + if (typeof words[w] == 'string') { + if (words[w] === 'Z') { + // compressed zeros - write appropriate number of zero words + for (z = 0; z < 8 - words.length + 1; z++) { + parts[w + z] = 0; + } + offset = z - 1; + } else { + // parse hex to field to 16-bit value and write it in network byte-order + parts[w + offset] = _htons(parseInt(words[w], 16)); + } + } else { + // parsed IPv4 words + parts[w + offset] = words[w]; + } + } + return [ + (parts[1] << 16) | parts[0], + (parts[3] << 16) | parts[2], + (parts[5] << 16) | parts[4], + (parts[7] << 16) | parts[6], + ]; + }; + + /** @param {number=} addrlen */ + var writeSockaddr = (sa, family, addr, port, addrlen) => { + switch (family) { + case 2: + addr = inetPton4(addr); + zeroMemory(sa, 16); + if (addrlen) { + HEAP32[addrlen >> 2] = 16; + } + HEAP16[sa >> 1] = family; + HEAP32[(sa + 4) >> 2] = addr; + HEAP16[(sa + 2) >> 1] = _htons(port); + break; + case 10: + addr = inetPton6(addr); + zeroMemory(sa, 28); + if (addrlen) { + HEAP32[addrlen >> 2] = 28; + } + HEAP32[sa >> 2] = family; + HEAP32[(sa + 8) >> 2] = addr[0]; + HEAP32[(sa + 12) >> 2] = addr[1]; + HEAP32[(sa + 16) >> 2] = addr[2]; + HEAP32[(sa + 20) >> 2] = addr[3]; + HEAP16[(sa + 2) >> 1] = _htons(port); + break; + default: + return 5; + } + return 0; + }; + + var DNS = { + address_map: { + id: 1, + addrs: {}, + names: {}, + }, + lookup_name(name) { + // If the name is already a valid ipv4 / ipv6 address, don't generate a fake one. + var res = inetPton4(name); + if (res !== null) { + return name; + } + res = inetPton6(name); + if (res !== null) { + return name; + } + + // See if this name is already mapped. + var addr; + + if (DNS.address_map.addrs[name]) { + addr = DNS.address_map.addrs[name]; + } else { + var id = DNS.address_map.id++; + + addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00); + + DNS.address_map.names[addr] = name; + DNS.address_map.addrs[name] = addr; + } + + return addr; + }, + lookup_addr(addr) { + if (DNS.address_map.names[addr]) { + return DNS.address_map.names[addr]; + } + + return null; + }, + }; + function ___syscall_accept4(fd, addr, addrlen, flags, d1, d2) { + try { + var sock = getSocketFromFD(fd); + var newsock = sock.sock_ops.accept(sock); + if (addr) { + var errno = writeSockaddr( + addr, + newsock.family, + DNS.lookup_name(newsock.daddr), + newsock.dport, + addrlen + ); + } + return newsock.stream.fd; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_accept4.sig = 'iippiii'; + + var inetNtop4 = (addr) => + (addr & 0xff) + + '.' + + ((addr >> 8) & 0xff) + + '.' + + ((addr >> 16) & 0xff) + + '.' + + ((addr >> 24) & 0xff); + + var inetNtop6 = (ints) => { + // ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4 + // Format for IPv4 compatible and mapped 128-bit IPv6 Addresses + // 128-bits are split into eight 16-bit words + // stored in network byte order (big-endian) + // | 80 bits | 16 | 32 bits | + // +-----------------------------------------------------------------+ + // | 10 bytes | 2 | 4 bytes | + // +--------------------------------------+--------------------------+ + // + 5 words | 1 | 2 words | + // +--------------------------------------+--------------------------+ + // |0000..............................0000|0000| IPv4 ADDRESS | (compatible) + // +--------------------------------------+----+---------------------+ + // |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped) + // +--------------------------------------+----+---------------------+ + var str = ''; + var word = 0; + var longest = 0; + var lastzero = 0; + var zstart = 0; + var len = 0; + var i = 0; + var parts = [ + ints[0] & 0xffff, + ints[0] >> 16, + ints[1] & 0xffff, + ints[1] >> 16, + ints[2] & 0xffff, + ints[2] >> 16, + ints[3] & 0xffff, + ints[3] >> 16, + ]; + + // Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses + + var hasipv4 = true; + var v4part = ''; + // check if the 10 high-order bytes are all zeros (first 5 words) + for (i = 0; i < 5; i++) { + if (parts[i] !== 0) { + hasipv4 = false; + break; + } + } + + if (hasipv4) { + // low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words) + v4part = inetNtop4(parts[6] | (parts[7] << 16)); + // IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word) + if (parts[5] === -1) { + str = '::ffff:'; + str += v4part; + return str; + } + // IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word) + if (parts[5] === 0) { + str = '::'; + //special case IPv6 addresses + if (v4part === '0.0.0.0') v4part = ''; // any/unspecified address + if (v4part === '0.0.0.1') v4part = '1'; // loopback address + str += v4part; + return str; + } + } + + // Handle all other IPv6 addresses + + // first run to find the longest contiguous zero words + for (word = 0; word < 8; word++) { + if (parts[word] === 0) { + if (word - lastzero > 1) { + len = 0; + } + lastzero = word; + len++; + } + if (len > longest) { + longest = len; + zstart = word - longest + 1; + } + } + + for (word = 0; word < 8; word++) { + if (longest > 1) { + // compress contiguous zeros - to produce "::" + if ( + parts[word] === 0 && + word >= zstart && + word < zstart + longest + ) { + if (word === zstart) { + str += ':'; + if (zstart === 0) str += ':'; //leading zeros case + } + continue; + } + } + // converts 16-bit words from big-endian to little-endian before converting to hex string + str += Number(_ntohs(parts[word] & 0xffff)).toString(16); + str += word < 7 ? ':' : ''; + } + return str; + }; + + var readSockaddr = (sa, salen) => { + // family / port offsets are common to both sockaddr_in and sockaddr_in6 + var family = HEAP16[sa >> 1]; + var port = _ntohs(HEAPU16[(sa + 2) >> 1]); + var addr; + + switch (family) { + case 2: + if (salen !== 16) { + return { errno: 28 }; + } + addr = HEAP32[(sa + 4) >> 2]; + addr = inetNtop4(addr); + break; + case 10: + if (salen !== 28) { + return { errno: 28 }; + } + addr = [ + HEAP32[(sa + 8) >> 2], + HEAP32[(sa + 12) >> 2], + HEAP32[(sa + 16) >> 2], + HEAP32[(sa + 20) >> 2], + ]; + addr = inetNtop6(addr); + break; + default: + return { errno: 5 }; + } + + return { family: family, addr: addr, port: port }; + }; + + var getSocketAddress = (addrp, addrlen) => { + var info = readSockaddr(addrp, addrlen); + if (info.errno) throw new FS.ErrnoError(info.errno); + info.addr = DNS.lookup_addr(info.addr) || info.addr; + return info; + }; + function ___syscall_bind(fd, addr, addrlen, d1, d2, d3) { + try { + var sock = getSocketFromFD(fd); + var info = getSocketAddress(addr, addrlen); + sock.sock_ops.bind(sock, info.addr, info.port); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_bind.sig = 'iippiii'; + + function ___syscall_chdir(path) { + try { + path = SYSCALLS.getStr(path); + FS.chdir(path); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_chdir.sig = 'ip'; + + function ___syscall_chmod(path, mode) { + try { + path = SYSCALLS.getStr(path); + FS.chmod(path, mode); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_chmod.sig = 'ipi'; + + function ___syscall_connect(fd, addr, addrlen, d1, d2, d3) { + try { + var sock = getSocketFromFD(fd); + var info = getSocketAddress(addr, addrlen); + sock.sock_ops.connect(sock, info.addr, info.port); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_connect.sig = 'iippiii'; + + function ___syscall_dup(fd) { + try { + var old = SYSCALLS.getStreamFromFD(fd); + return FS.dupStream(old).fd; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_dup.sig = 'ii'; + + function ___syscall_dup3(fd, newfd, flags) { + try { + var old = SYSCALLS.getStreamFromFD(fd); + if (old.fd === newfd) return -28; + // Check newfd is within range of valid open file descriptors. + if (newfd < 0 || newfd >= FS.MAX_OPEN_FDS) return -8; + var existing = FS.getStream(newfd); + if (existing) FS.close(existing); + return FS.dupStream(old, newfd).fd; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_dup3.sig = 'iiii'; + + function ___syscall_faccessat(dirfd, path, amode, flags) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + if (amode & ~7) { + // need a valid mode + return -28; + } + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + if (!node) { + return -44; + } + var perms = ''; + if (amode & 4) perms += 'r'; + if (amode & 2) perms += 'w'; + if (amode & 1) perms += 'x'; + if ( + perms /* otherwise, they've just passed F_OK */ && + FS.nodePermissions(node, perms) + ) { + return -2; + } + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_faccessat.sig = 'iipii'; + + var ___syscall_fadvise64 = (fd, offset, len, advice) => 0; + ___syscall_fadvise64.sig = 'iijji'; + + var INT53_MAX = 9007199254740992; + + var INT53_MIN = -9007199254740992; + var bigintToI53Checked = (num) => + num < INT53_MIN || num > INT53_MAX ? NaN : Number(num); + function ___syscall_fallocate(fd, mode, offset, len) { + offset = bigintToI53Checked(offset); + len = bigintToI53Checked(len); + + try { + if (isNaN(offset) || isNaN(len)) return -61; + if (mode != 0) { + return -138; + } + if (offset < 0 || len < 0) { + return -28; + } + // We only support mode == 0, which means we can implement fallocate + // in terms of ftruncate. + var oldSize = FS.fstat(fd).size; + var newSize = offset + len; + if (newSize > oldSize) { + FS.ftruncate(fd, newSize); + } + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fallocate.sig = 'iiijj'; + + function ___syscall_fchdir(fd) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + FS.chdir(stream.path); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fchdir.sig = 'ii'; + + function ___syscall_fchmod(fd, mode) { + try { + FS.fchmod(fd, mode); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fchmod.sig = 'iii'; + + function ___syscall_fchmodat2(dirfd, path, mode, flags) { + try { + var nofollow = flags & 256; + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + FS.chmod(path, mode, nofollow); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fchmodat2.sig = 'iipii'; + + function ___syscall_fchown32(fd, owner, group) { + try { + FS.fchown(fd, owner, group); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fchown32.sig = 'iiii'; + + function ___syscall_fchownat(dirfd, path, owner, group, flags) { + try { + path = SYSCALLS.getStr(path); + var nofollow = flags & 256; + flags = flags & ~256; + path = SYSCALLS.calculateAt(dirfd, path); + (nofollow ? FS.lchown : FS.chown)(path, owner, group); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fchownat.sig = 'iipiii'; + + var syscallGetVarargI = () => { + // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. + var ret = HEAP32[+SYSCALLS.varargs >> 2]; + SYSCALLS.varargs += 4; + return ret; + }; + var syscallGetVarargP = syscallGetVarargI; + + var allocateUTF8OnStack = (...args) => stringToUTF8OnStack(...args); + + var PHPWASM = { + O_APPEND: 1024, + O_NONBLOCK: 2048, + POLLHUP: 16, + SETFL_MASK: 3072, + init: function (phpWasmInitOptions) { + Module['ENV'] = Module['ENV'] || {}; + // Ensure a platform-level bin directory for a fallback `php` binary. + Module['ENV']['PATH'] = [ + Module['ENV']['PATH'], + '/internal/shared/bin', + ] + .filter(Boolean) + .join(':'); + + // The /request directory is required by the C module. It's where the + // stdout, stderr, and headers information are written for the JavaScript + // code to read later on. This is per-request state that is isolated to a + // single PHP process. + FS.mkdir('/request'); + // The /internal directory is shared amongst all PHP processes + // and contains the php.ini, constants definitions, etc. + FS.mkdir('/internal'); + + if (phpWasmInitOptions?.nativeInternalDirPath) { + FS.mount( + FS.filesystems.NODEFS, + { root: phpWasmInitOptions.nativeInternalDirPath }, + '/internal' + ); + } + + // The files from the shared directory are shared between all the + // PHP processes managed by PHPProcessManager. + FS.mkdirTree('/internal/shared'); + + // The files from the preload directory are preloaded using the + // auto_prepend_file php.ini directive. + FS.mkdirTree('/internal/shared/preload'); + // Platform-level bin directory for a fallback `php` binary. Without it, + // PHP may not populate the PHP_BINARY constant. + FS.mkdirTree('/internal/shared/bin'); + const originalOnRuntimeInitialized = Module['onRuntimeInitialized']; + Module['onRuntimeInitialized'] = () => { + const { node: phpBinaryNode } = FS.lookupPath( + '/internal/shared/bin/php', + { noent_okay: true } + ); + if (!phpBinaryNode) { + // Dummy PHP binary for PHP to populate the PHP_BINARY constant. + FS.writeFile( + '/internal/shared/bin/php', + new TextEncoder().encode('#!/bin/sh\nphp "$@"') + ); + // It must be executable to be used by PHP. + FS.chmod('/internal/shared/bin/php', 0o755); + } + originalOnRuntimeInitialized(); + }; + + // Create stdout and stderr devices. We can't just use Emscripten's + // default stdout and stderr devices because they stop processing data + // on the first null byte. However, when dealing with binary data, + // null bytes are valid and common. + FS.registerDevice(FS.makedev(64, 0), { + open: () => {}, + close: () => {}, + read: () => 0, + write: (stream, buffer, offset, length, pos) => { + const chunk = buffer.subarray(offset, offset + length); + PHPWASM.onStdout(chunk); + return length; + }, + }); + FS.mkdev('/request/stdout', FS.makedev(64, 0)); + + FS.registerDevice(FS.makedev(63, 0), { + open: () => {}, + close: () => {}, + read: () => 0, + write: (stream, buffer, offset, length, pos) => { + const chunk = buffer.subarray(offset, offset + length); + PHPWASM.onStderr(chunk); + return length; + }, + }); + FS.mkdev('/request/stderr', FS.makedev(63, 0)); + + FS.registerDevice(FS.makedev(62, 0), { + open: () => {}, + close: () => {}, + read: () => 0, + write: (stream, buffer, offset, length, pos) => { + const chunk = buffer.subarray(offset, offset + length); + PHPWASM.onHeaders(chunk); + return length; + }, + }); + FS.mkdev('/request/headers', FS.makedev(62, 0)); + + // Handle events. + PHPWASM.EventEmitter = ENVIRONMENT_IS_NODE + ? require('events').EventEmitter + : class EventEmitter { + constructor() { + this.listeners = {}; + } + emit(eventName, data) { + if (this.listeners[eventName]) { + this.listeners[eventName].forEach( + (callback) => { + callback(data); + } + ); + } + } + once(eventName, callback) { + const self = this; + function removedCallback() { + callback(...arguments); + self.removeListener(eventName, removedCallback); + } + this.on(eventName, removedCallback); + } + removeAllListeners(eventName) { + if (eventName) { + delete this.listeners[eventName]; + } else { + this.listeners = {}; + } + } + removeListener(eventName, callback) { + if (this.listeners[eventName]) { + const idx = + this.listeners[eventName].indexOf(callback); + if (idx !== -1) { + this.listeners[eventName].splice(idx, 1); + } + } + } + }; + + PHPWASM.processTable = {}; + + PHPWASM.input_devices = {}; + const originalWrite = TTY.stream_ops.write; + TTY.stream_ops.write = function (stream, ...rest) { + const retval = originalWrite(stream, ...rest); + // Implicit flush since PHP's fflush() doesn't seem to trigger the fsync event + // @TODO: Fix this at the wasm level + stream.tty.ops.fsync(stream.tty); + return retval; + }; + const originalPutChar = TTY.stream_ops.put_char; + TTY.stream_ops.put_char = function (tty, val) { + /** + * Buffer newlines that Emscripten normally ignores. + * + * Emscripten doesn't do it by default because its default + * print function is console.log that implicitly adds a newline. We are overwriting + * it with an environment-specific function that outputs exaclty what it was given, + * e.g. in Node.js it's process.stdout.write(). Therefore, we need to mak sure + * all the newlines make it to the output buffer. + */ + if (val === 10) tty.output.push(val); + return originalPutChar(tty, val); + }; + }, + onHeaders: function (chunk) { + if (Module['onHeaders']) { + Module['onHeaders'](chunk); + return; + } + console.log('headers', { chunk }); + }, + onStdout: function (chunk) { + if (Module['onStdout']) { + Module['onStdout'](chunk); + return; + } + if (ENVIRONMENT_IS_NODE) { + process.stdout.write(chunk); + } else { + console.log('stdout', { chunk }); + } + }, + onStderr: function (chunk) { + if (Module['onStderr']) { + Module['onStderr'](chunk); + return; + } + if (ENVIRONMENT_IS_NODE) { + process.stderr.write(chunk); + } else { + console.warn('stderr', { chunk }); + } + }, + getAllWebSockets: function (sock) { + const webSockets = /* @__PURE__ */ new Set(); + if (sock.server) { + sock.server.clients.forEach((ws) => { + webSockets.add(ws); + }); + } + for (const peer of PHPWASM.getAllPeers(sock)) { + webSockets.add(peer.socket); + } + return Array.from(webSockets); + }, + getAllPeers: function (sock) { + const peers = new Set(); + if (sock.server) { + sock.pending + .filter((pending) => pending.peers) + .forEach((pending) => { + for (const peer of Object.values(pending.peers)) { + peers.add(peer); + } + }); + } + if (sock.peers) { + for (const peer of Object.values(sock.peers)) { + peers.add(peer); + } + } + return Array.from(peers); + }, + awaitData: function (ws) { + return PHPWASM.awaitEvent(ws, 'message'); + }, + awaitConnection: function (ws) { + if (ws.OPEN === ws.readyState) { + return [Promise.resolve(), PHPWASM.noop]; + } + return PHPWASM.awaitEvent(ws, 'open'); + }, + awaitClose: function (ws) { + if ([ws.CLOSING, ws.CLOSED].includes(ws.readyState)) { + return [Promise.resolve(), PHPWASM.noop]; + } + return PHPWASM.awaitEvent(ws, 'close'); + }, + awaitError: function (ws) { + if ([ws.CLOSING, ws.CLOSED].includes(ws.readyState)) { + return [Promise.resolve(), PHPWASM.noop]; + } + return PHPWASM.awaitEvent(ws, 'error'); + }, + awaitEvent: function (ws, event) { + let resolve; + const listener = () => { + resolve(); + }; + const promise = new Promise(function (_resolve) { + resolve = _resolve; + ws.once(event, listener); + }); + const cancel = () => { + ws.removeListener(event, listener); + // Rejecting the promises bubbles up and kills the entire + // node process. Let's resolve them on the next tick instead + // to give the caller some space to unbind any handlers. + setTimeout(resolve); + }; + return [promise, cancel]; + }, + noop: function () {}, + spawnProcess: function (command, args, options) { + if (Module['spawnProcess']) { + const spawnedPromise = Module['spawnProcess']( + command, + args, + options + ); + return Promise.resolve(spawnedPromise).then(function (spawned) { + if (!spawned || !spawned.on) { + throw new Error( + 'spawnProcess() must return an EventEmitter but returned a different type.' + ); + } + return spawned; + }); + } + + if (ENVIRONMENT_IS_NODE) { + return require('child_process').spawn(command, args, { + ...options, + shell: true, + stdio: ['pipe', 'pipe', 'pipe'], + }); + } + const e = new Error( + 'popen(), proc_open() etc. are unsupported in the browser. Call php.setSpawnHandler() ' + + 'and provide a callback to handle spawning processes, or disable a popen(), proc_open() ' + + 'and similar functions via php.ini.' + ); + e.code = 'SPAWN_UNSUPPORTED'; + throw e; + }, + shutdownSocket: function (socketd, how) { + // This implementation only supports websockets at the moment + const sock = getSocketFromFD(socketd); + const peer = Object.values(sock.peers)[0]; + + if (!peer) { + return -1; + } + + try { + peer.socket.close(); + SOCKFS.websocket_sock_ops.removePeer(sock, peer); + return 0; + } catch (e) { + console.log('Socket shutdown error', e); + return -1; + } + }, + }; + + function _js_getpid() { + return PHPLoader.processId ?? 42; + } -function getWasmImports() { - // prepare imports - var imports = { - 'env': wasmImports, - 'wasi_snapshot_preview1': wasmImports, - 'GOT.mem': new Proxy(wasmImports, GOTHandler), - 'GOT.func': new Proxy(wasmImports, GOTHandler), - }; - return imports; -} + function _js_wasm_trace(format, ...args) { + if (PHPLoader.trace instanceof Function) { + PHPLoader.trace(_js_getpid(), format, ...args); + } + } -// 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; - - // No relocation needed here.. but calling this just so that updateGOT is - // called. - var origExports = wasmExports = relocateExports(wasmExports); - - wasmExports = Asyncify.instrumentWasmExports(wasmExports); - - mergeLibSymbols(wasmExports, 'main') - var metadata = getDylinkMetadata(module); - if (metadata.neededDynlibs) { - dynamicLibraries = metadata.neededDynlibs.concat(dynamicLibraries); - } + function _fd_close(fd) { + // We have to get the VFS path from the file descriptor + // before closing it. + const [vfsPath, vfsPathResolutionErrno] = + locking.get_vfs_path_from_fd(fd); + + const fdCloseResult = _builtin_fd_close(fd); + if (fdCloseResult !== 0 || !locking.maybeLockedFds.has(fd)) { + _js_wasm_trace('fd_close(%d) result %d', fd, fdCloseResult); + return fdCloseResult; + } + + if (vfsPathResolutionErrno !== 0) { + _js_wasm_trace( + 'fd_close(%d) get_vfs_path_from_fd error %d', + fd, + vfsPathResolutionErrno + ); + /* + * It looks like the file may have had an associated lock, + * but since we cannot look up the path, + * there is nothing more for us to do. + * + * NOTE: This seems possible for files that are locked and + * then unlinked before close. It is an opportunity for a + * lock to be orphaned in the lock manager. + * @TODO: Explore how to ensure cleanup in this case. + */ + return fdCloseResult; + } + + try { + const nativeFilePath = + locking.get_native_path_from_vfs_path(vfsPath); + PHPLoader.fileLockManager.releaseLocksForProcessFd( + PHPLoader.processId, + fd, + nativeFilePath + ); + _js_wasm_trace('fd_close(%d) release locks success', fd); + } catch (e) { + _js_wasm_trace("fd_close(%d) error '%s'", fd, e); + } finally { + locking.maybeLockedFds.delete(fd); + } + return fdCloseResult; + } + _fd_close.sig = 'ii'; + function _builtin_fd_close(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; + } + } - assignWasmExports(wasmExports); - - updateGOT(origExports); - - Module['wasmExports'] = wasmExports; - - LDSO.init(); - loadDylibs(); - - updateMemoryViews(); - - removeRunDependency('wasm-instantiate'); - return wasmExports; - } - addRunDependency('wasm-instantiate'); - - // Prefer streaming instantiation if available. - 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 - 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) => { - Module['instantiateWasm'](info, (inst, mod) => { - resolve(receiveInstance(inst, mod)); - }); - }); - } - - wasmBinaryFile ??= findWasmBinary(); - var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info); - var exports = receiveInstantiationResult(result); - return exports; -} + function _builtin_fcntl64(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. + HEAP16[(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; + } + } -// With MAIN_MODULE + ASYNCIFY the normal method of placing stub functions in -// wasmImports for as-yet-undefined symbols doesn't work since ASYNCIFY then -// wraps these stub functions and we can't then replace them directly. Instead -// the stub functions call into `asyncifyStubs` which gets populated by the -// dynamic linker as symbols are loaded. -var asyncifyStubs = {}; -// end include: preamble.js + var locking = { + maybeLockedFds: new Set(), + F_RDLCK: 0, + F_WRLCK: 1, + F_UNLCK: 2, + lockStateToFcntl: { + shared: 0, + exclusive: 1, + unlocked: 2, + }, + fcntlToLockState: { + 0: 'shared', + 1: 'exclusive', + 2: 'unlocked', + }, + is_path_to_shared_fs(path) { + _js_wasm_trace('is_path_to_shared_fs(%s)', path); + const { node } = FS.lookupPath(path, { noent_okay: true }); + if (node.mount.type !== PROXYFS) { + return !!node.isSharedFS; + } + + // This looks like a PROXYFS node. Let's try a lookup. + const nodePath = PROXYFS.realPath(node); + const backingFs = node?.mount?.opts?.fs; + if (backingFs) { + // Tolerate ENOENT because looking up a MEMFS node by path always fails. + const { node: backingNode } = backingFs.lookupPath(nodePath, { + noent_okay: true, + }); + return !!backingNode?.isSharedFS; + } + + return false; + }, + get_fd_access_mode(fd) { + const emscripten_F_GETFL = Number('3'); + const emscripten_O_ACCMODE = Number('2097155'); + + return ( + _builtin_fcntl64(fd, emscripten_F_GETFL) & emscripten_O_ACCMODE + ); + }, + get_vfs_path_from_fd(fd) { + try { + return [FS.readlink(`/proc/self/fd/${fd}`), 0]; + } catch (error) { + return [null, ERRNO_CODES.EBADF]; + } + }, + get_native_path_from_vfs_path(vfsPath) { + const { node } = FS.lookupPath(vfsPath, { + noent_okay: true, + }); + if (!node) { + throw new Error(`No node found for VFS path ${vfsPath}`); + } + if (node.mount.type === NODEFS) { + return NODEFS.realPath(node); + } else if (node.mount.type === PROXYFS) { + // TODO: Tolerate ENOENT here? + const { node: backingNode, path: backingPath } = + node.mount.opts.fs.lookupPath(vfsPath); + _js_wasm_trace( + 'backingNode for %s: %s', + vfsPath, + backingPath, + backingNode + ); + return backingNode.mount.type.realPath(backingNode); + } else { + throw new Error( + `Unsupported filesystem type for path ${vfsPath}` + ); + } + }, + check_lock_params(fd, l_type) { + const emscripten_O_RDONLY = Number('0'); + const emscripten_O_WRONLY = Number('1'); + + const accessMode = locking.get_fd_access_mode(fd); + if ( + (l_type === locking.F_WRLCK && + accessMode === emscripten_O_RDONLY) || + (l_type === locking.F_RDLCK && + accessMode === emscripten_O_WRONLY) + ) { + return ERRNO_CODES.EBADF; + } + + return 0; + }, + }; + + function ___syscall_fcntl64(fd, cmd, varargs) { + if (!PHPLoader.fileLockManager) { + return _builtin_fcntl64(fd, cmd, varargs); + } + // Necessary to use varargs accessor + SYSCALLS.varargs = varargs; + + // These constants are replaced by Emscripten during the build process + const emscripten_F_SETFL = Number('4'); + const emscripten_F_GETLK = Number('12'); + const emscripten_F_SETLK = Number('13'); + const emscripten_F_SETLKW = Number('14'); + const emscripten_SEEK_SET = Number('0'); + + // NOTE: With the exception of l_type, these offsets are not exposed to + // JS by Emscripten, so we hardcode them here. + const emscripten_flock_l_type_offset = 0; + const emscripten_flock_l_whence_offset = 2; + const emscripten_flock_l_start_offset = 8; + const emscripten_flock_l_len_offset = 16; + const emscripten_flock_l_pid_offset = 24; + + /** + * Read the flock struct at the given address. + * + * @param {bigint} flockStructAddress - the address of the flock struct + * @returns the flock struct + */ + function read_flock_struct(flockStructAddress) { + /* + * NOTE: Since we are using HEAP vars like HEAP16 and HEAP64, + * we need to adjust offsets to address the word size of each HEAP. + * + * For example, an offset of 64 bytes is the following for each HEAP: + * - HEAP8: 64 (the 64th byte) + * - HEAP16: 32 (the 32nd 16-bit word) + * - HEAP32: 16 (the 16th 32-bit word) + * - HEAP64: 8 (the 8th 64-bit word) + * + * We get a word offset by dividing the byte offset by the word size. + */ + return { + l_type: HEAP16[ + // Shift right by 1 to divide by 2^1. + (flockStructAddress + emscripten_flock_l_type_offset) >> 1 + ], + l_whence: + HEAP16[ + // Shift right by 1 to divide by 2^1. + (flockStructAddress + + emscripten_flock_l_whence_offset) >> + 1 + ], + l_start: + HEAP64[ + // Shift right by 3 to divide by 2^3. + (flockStructAddress + + emscripten_flock_l_start_offset) >> + 3 + ], + l_len: HEAP64[ + // Shift right by 3 to divide by 2^3. + (flockStructAddress + emscripten_flock_l_len_offset) >> 3 + ], + l_pid: HEAP32[ + // Shift right by 2 to divide by 2^2. + (flockStructAddress + emscripten_flock_l_pid_offset) >> 2 + ], + }; + } + + /** + * Update the flock struct at the given address with the given fields. + * + * @param {bigint} flockStructAddress - the address of the flock struct + * @param {object} fields - the fields to update + */ + function update_flock_struct(flockStructAddress, fields) { + /* + * NOTE: Since we are using HEAP vars like HEAP16 and HEAP64, + * we need to adjust offsets to address the word size of each HEAP. + * + * For example, an offset of 64 bytes is the following for each HEAP: + * - HEAP8: 64 (the 64th byte) + * - HEAP16: 32 (the 32nd 16-bit word) + * - HEAP32: 16 (the 16th 32-bit word) + * - HEAP64: 8 (the 8th 64-bit word) + * + * We get a word offset by dividing the byte offset by the word size. + */ + if (fields.l_type !== undefined) { + HEAP16[ + // Shift right by 1 to divide by 2^1. + (flockStructAddress + emscripten_flock_l_type_offset) >> 1 + ] = fields.l_type; + } + if (fields.l_whence !== undefined) { + HEAP16[ + // Shift right by 1 to divide by 2^1. + (flockStructAddress + emscripten_flock_l_whence_offset) >> 1 + ] = fields.l_whence; + } + if (fields.l_start !== undefined) { + HEAP64[ + // Shift right by 3 to divide by 2^3. + (flockStructAddress + emscripten_flock_l_start_offset) >> 3 + ] = fields.l_start; + } + if (fields.l_len !== undefined) { + HEAP64[ + // Shift right by 3 to divide by 2^3. + (flockStructAddress + emscripten_flock_l_len_offset) >> 3 + ] = fields.l_len; + } + if (fields.l_pid !== undefined) { + HEAP32[ + // Shift right by 2 to divide by 2^2. + (flockStructAddress + emscripten_flock_l_pid_offset) >> 2 + ] = fields.l_pid; + } + } + + /** + * Resolve the base address of the range depending on the whence and start offset. + * + * @param {number} fd - the file descriptor + * @param {number} whence - what the start offset is relative to + * @param {bigint} startOffset - the offset from the whence + * @returns The resolved offset and the errno. If there is an error, + * the resolved offset is null, and the errno is non-zero. + */ + function get_base_address(fd, whence, startOffset) { + let baseAddress; + switch (whence) { + case emscripten_SEEK_SET: + baseAddress = 0n; + break; + case emscripten_SEEK_CUR: + baseAddress = FS.lseek(fd, 0, whence); + break; + case emscripten_SEEK_END: + baseAddress = _wasm_get_end_offset(fd); + break; + default: + return [null, ERRNO_CODES.EINVAL]; + } + + if (baseAddress == -1) { + // We cannot resolve the offset within the file. + // Let's treat this as a problem with the file descriptor. + return [null, ERRNO_CODES.EBADF]; + } + + const resolvedOffset = baseAddress + startOffset; + if (resolvedOffset < 0) { + // This is not a valid offset. Report args as invalid. + return [null, ERRNO_CODES.EINVAL]; + } + + return [resolvedOffset, 0]; + } + + const pid = PHPLoader.processId; + switch (cmd) { + case emscripten_F_GETLK: { + _js_wasm_trace('fcntl(%d, F_GETLK)', fd); + let vfsPath; + let errno; + + [vfsPath, errno] = locking.get_vfs_path_from_fd(fd); + if (errno !== 0) { + _js_wasm_trace( + 'fcntl(%d, F_GETLK) %s get_vfs_path_from_fd errno %d', + fd, + vfsPath, + errno + ); + return -ERRNO_CODES.EBADF; + } + + const flockStructAddr = syscallGetVarargP(); + + if (!locking.is_path_to_shared_fs(vfsPath)) { + _js_wasm_trace( + "fcntl(%d, F_GETLK) locking is not implemented for non-NodeFS path '%s'", + fd, + vfsPath + ); + + // If not a NodeFS path, we can't lock it. + // Default to succeeding as Emscripten does. + update_flock_struct(flockStructAddr, { + l_type: F_UNLCK, + }); + return 0; + } + + const flockStruct = read_flock_struct(flockStructAddr); + + if (!(flockStruct.l_type in locking.fcntlToLockState)) { + return -ERRNO_CODES.EINVAL; + } + + errno = locking.check_lock_params(fd, flockStruct.l_type); + if (errno !== 0) { + _js_wasm_trace( + 'fcntl(%d, F_GETLK) %s check_lock_params errno %d', + fd, + vfsPath, + errno + ); + return -ERRNO_CODES.EINVAL; + } + + const requestedLockType = + locking.fcntlToLockState[flockStruct.l_type]; + let absoluteStartOffset; + [absoluteStartOffset, errno] = get_base_address( + fd, + flockStruct.l_whence, + flockStruct.l_start + ); + if (errno !== 0) { + _js_wasm_trace( + 'fcntl(%d, F_GETLK) %s get_base_address errno %d', + fd, + vfsPath, + errno + ); + return -ERRNO_CODES.EINVAL; + } + + try { + const nativeFilePath = + locking.get_native_path_from_vfs_path(vfsPath); + const conflictingLock = + PHPLoader.fileLockManager.findFirstConflictingByteRangeLock( + nativeFilePath, + { + type: requestedLockType, + start: absoluteStartOffset, + end: absoluteStartOffset + flockStruct.l_len, + pid, + } + ); + if (conflictingLock === undefined) { + _js_wasm_trace( + 'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock type=unlocked start=0x%x end=0x%x', + fd, + vfsPath, + absoluteStartOffset, + absoluteStartOffset + flockStruct.l_len + ); + + update_flock_struct(flockStructAddr, { + l_type: F_UNLCK, + }); + return 0; + } + + _js_wasm_trace( + 'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock type=%s start=0x%x end=0x%x conflictingLock %d', + fd, + vfsPath, + conflictingLock.type, + conflictingLock.start, + conflictingLock.end, + conflictingLock.pid + ); + + const fcntlLockState = + locking.lockStateToFcntl[conflictingLock.type]; + update_flock_struct(flockStructAddr, { + l_type: fcntlLockState, + l_whence: emscripten_SEEK_SET, + l_start: conflictingLock.start, + l_len: conflictingLock.end - conflictingLock.start, + l_pid: conflictingLock.pid, + }); + return 0; + } catch (e) { + _js_wasm_trace( + 'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock error %s', + fd, + vfsPath, + e + ); + return -ERRNO_CODES.EINVAL; + } + } + case emscripten_F_SETLK: { + _js_wasm_trace('fcntl(%d, F_SETLK)', fd); + let vfsPath; + let errno; + [vfsPath, errno] = locking.get_vfs_path_from_fd(fd); + if (errno !== 0) { + _js_wasm_trace( + 'fcntl(%d, F_SETLK) %s get_vfs_path_from_fd errno %d', + fd, + vfsPath, + errno + ); + return -errno; + } + + if (!locking.is_path_to_shared_fs(vfsPath)) { + _js_wasm_trace( + 'fcntl(%d, F_SETLK) locking is not implemented for non-NodeFS path %s', + fd, + vfsPath + ); + + // If not a NodeFS path, we can't lock it. + // Default to succeeding as Emscripten does. + return 0; + } + + var flockStructAddr = syscallGetVarargP(); + const flockStruct = read_flock_struct(flockStructAddr); + + let absoluteStartOffset; + [absoluteStartOffset, errno] = get_base_address( + fd, + flockStruct.l_whence, + flockStruct.l_start + ); + if (errno !== 0) { + _js_wasm_trace( + 'fcntl(%d, F_SETLK) %s get_base_address errno %d', + fd, + vfsPath, + errno + ); + return -errno; + } + + if (!(flockStruct.l_type in locking.fcntlToLockState)) { + _js_wasm_trace( + 'fcntl(%d, F_SETLK) %s invalid lock type %d', + fd, + vfsPath, + flockStruct.l_type + ); + return -ERRNO_CODES.EINVAL; + } + + errno = locking.check_lock_params(fd, flockStruct.l_type); + if (errno !== 0) { + _js_wasm_trace( + 'fcntl(%d, F_SETLK) %s check_lock_params errno %d', + fd, + vfsPath, + errno + ); + return -errno; + } + + locking.maybeLockedFds.add(fd); + + const requestedLockType = + locking.fcntlToLockState[flockStruct.l_type]; + const rangeLock = { + type: requestedLockType, + start: absoluteStartOffset, + end: absoluteStartOffset + flockStruct.l_len, + pid, + }; + + try { + const nativeFilePath = + locking.get_native_path_from_vfs_path(vfsPath); + _js_wasm_trace( + 'fcntl(%d, F_SETLK) %s calling lockFileByteRange for range lock %s', + fd, + vfsPath, + rangeLock + ); + + const succeeded = + PHPLoader.fileLockManager.lockFileByteRange( + nativeFilePath, + rangeLock + ); + + _js_wasm_trace( + 'fcntl(%d, F_SETLK) %s lockFileByteRange returned %d for range lock %s', + fd, + vfsPath, + succeeded, + rangeLock + ); + return succeeded ? 0 : -ERRNO_CODES.EAGAIN; + } catch (e) { + _js_wasm_trace( + 'fcntl(%d, F_SETLK) %s lockFileByteRange error %s for range lock %s', + fd, + vfsPath, + e, + rangeLock + ); + return -ERRNO_CODES.EINVAL; + } + } + // @TODO: Implement waiting for lock + case emscripten_F_SETLKW: { + // We do not yet support the blocking form of flock(). + // We respond with EDEADLK to indicate failure + // because it is a known errno for a failed F_SETLKW command. + return -ERRNO_CODES.EDEADLK; + } + case emscripten_F_SETFL: { + /** + * Overrides the core Emscripten implementation to reflect what + * fcntl does in linux kernel. This implementation is still missing + * a bunch of nuance, but, unlike the core Emscripten implementation, + * it overrides the stream flags while preserving non-stream flags. + * + * @see fcntl.c: + * https://github.com/torvalds/linux/blob/a79a588fc1761dc12a3064fc2f648ae66cea3c5a/fs/fcntl.c#L39 + */ + const arg = varargs ? syscallGetVarargI() : 0; + const stream = SYSCALLS.getStreamFromFD(fd); + + // Update the stream flags + stream.flags = + (arg & PHPWASM.SETFL_MASK) | + (stream.flags & ~PHPWASM.SETFL_MASK); + + return 0; + } + default: + return _builtin_fcntl64(fd, cmd, varargs); + } + } + ___syscall_fcntl64.sig = 'iiip'; + + function ___syscall_fdatasync(fd) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + return 0; // we can't do anything synchronously; the in-memory FS is already synced to + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fdatasync.sig = 'ii'; + + function ___syscall_fstat64(fd, buf) { + try { + return SYSCALLS.writeStat(buf, FS.fstat(fd)); + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fstat64.sig = 'iip'; + + function ___syscall_fstatfs64(fd, size, buf) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + SYSCALLS.writeStatFs(buf, FS.statfsStream(stream)); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_fstatfs64.sig = 'iipp'; + + function ___syscall_ftruncate64(fd, length) { + length = bigintToI53Checked(length); + + try { + if (isNaN(length)) return -61; + FS.ftruncate(fd, length); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_ftruncate64.sig = 'iij'; + + function ___syscall_getcwd(buf, size) { + try { + if (size === 0) return -28; + var cwd = FS.cwd(); + var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1; + if (size < cwdLengthInBytes) return -68; + stringToUTF8(cwd, buf, size); + return cwdLengthInBytes; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_getcwd.sig = 'ipp'; + + function ___syscall_getdents64(fd, dirp, count) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + stream.getdents ||= FS.readdir(stream.path); + + var struct_size = 280; + var pos = 0; + var off = FS.llseek(stream, 0, 1); + + var startIdx = Math.floor(off / struct_size); + var endIdx = Math.min( + stream.getdents.length, + startIdx + Math.floor(count / struct_size) + ); + for (var idx = startIdx; idx < endIdx; idx++) { + var id; + var type; + var name = stream.getdents[idx]; + if (name === '.') { + id = stream.node.id; + type = 4; // DT_DIR + } else if (name === '..') { + var lookup = FS.lookupPath(stream.path, { parent: true }); + id = lookup.node.id; + type = 4; // DT_DIR + } else { + var child; + try { + child = FS.lookupNode(stream.node, name); + } catch (e) { + // If the entry is not a directory, file, or symlink, nodefs + // lookupNode will raise EINVAL. Skip these and continue. + if (e?.errno === 28) { + continue; + } + throw e; + } + id = child.id; + type = FS.isChrdev(child.mode) + ? 2 // DT_CHR, character device. + : FS.isDir(child.mode) + ? 4 // DT_DIR, directory. + : FS.isLink(child.mode) + ? 10 // DT_LNK, symbolic link. + : 8; // DT_REG, regular file. + } + HEAP64[(dirp + pos) >> 3] = BigInt(id); + HEAP64[(dirp + pos + 8) >> 3] = BigInt((idx + 1) * struct_size); + HEAP16[(dirp + pos + 16) >> 1] = 280; + HEAP8[dirp + pos + 18] = type; + stringToUTF8(name, dirp + pos + 19, 256); + pos += struct_size; + } + FS.llseek(stream, idx * struct_size, 0); + return pos; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_getdents64.sig = 'iipp'; + + function ___syscall_getpeername(fd, addr, addrlen, d1, d2, d3) { + try { + var sock = getSocketFromFD(fd); + if (!sock.daddr) { + return -53; // The socket is not connected. + } + var errno = writeSockaddr( + addr, + sock.family, + DNS.lookup_name(sock.daddr), + sock.dport, + addrlen + ); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_getpeername.sig = 'iippiii'; + + function ___syscall_getsockname(fd, addr, addrlen, d1, d2, d3) { + try { + var sock = getSocketFromFD(fd); + // TODO: sock.saddr should never be undefined, see TODO in websocket_sock_ops.getname + var errno = writeSockaddr( + addr, + sock.family, + DNS.lookup_name(sock.saddr || '0.0.0.0'), + sock.sport, + addrlen + ); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_getsockname.sig = 'iippiii'; + + function ___syscall_getsockopt(fd, level, optname, optval, optlen, d1) { + try { + var sock = getSocketFromFD(fd); + // Minimal getsockopt aimed at resolving https://github.com/emscripten-core/emscripten/issues/2211 + // so only supports SOL_SOCKET with SO_ERROR. + if (level === 1) { + if (optname === 4) { + HEAP32[optval >> 2] = sock.error; + HEAP32[optlen >> 2] = 4; + sock.error = null; // Clear the error (The SO_ERROR option obtains and then clears this field). + return 0; + } + } + return -50; // The option is unknown at the level indicated. + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_getsockopt.sig = 'iiiippi'; + + function ___syscall_ioctl(fd, op, varargs) { + SYSCALLS.varargs = varargs; + try { + var stream = SYSCALLS.getStreamFromFD(fd); + switch (op) { + case 21509: { + if (!stream.tty) return -59; + return 0; + } + case 21505: { + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tcgets) { + var termios = stream.tty.ops.ioctl_tcgets(stream); + var argp = syscallGetVarargP(); + HEAP32[argp >> 2] = termios.c_iflag || 0; + HEAP32[(argp + 4) >> 2] = termios.c_oflag || 0; + HEAP32[(argp + 8) >> 2] = termios.c_cflag || 0; + HEAP32[(argp + 12) >> 2] = termios.c_lflag || 0; + for (var i = 0; i < 32; i++) { + HEAP8[argp + i + 17] = termios.c_cc[i] || 0; + } + return 0; + } + return 0; + } + case 21510: + case 21511: + case 21512: { + if (!stream.tty) return -59; + return 0; // no-op, not actually adjusting terminal settings + } + case 21506: + case 21507: + case 21508: { + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tcsets) { + var argp = syscallGetVarargP(); + var c_iflag = HEAP32[argp >> 2]; + var c_oflag = HEAP32[(argp + 4) >> 2]; + var c_cflag = HEAP32[(argp + 8) >> 2]; + var c_lflag = HEAP32[(argp + 12) >> 2]; + var c_cc = []; + for (var i = 0; i < 32; i++) { + c_cc.push(HEAP8[argp + i + 17]); + } + return stream.tty.ops.ioctl_tcsets(stream.tty, op, { + c_iflag, + c_oflag, + c_cflag, + c_lflag, + c_cc, + }); + } + return 0; // no-op, not actually adjusting terminal settings + } + case 21519: { + if (!stream.tty) return -59; + var argp = syscallGetVarargP(); + HEAP32[argp >> 2] = 0; + return 0; + } + case 21520: { + if (!stream.tty) return -59; + return -28; // not supported + } + case 21537: + case 21531: { + var argp = syscallGetVarargP(); + return FS.ioctl(stream, op, argp); + } + case 21523: { + // TODO: in theory we should write to the winsize struct that gets + // passed in, but for now musl doesn't read anything on it + if (!stream.tty) return -59; + if (stream.tty.ops.ioctl_tiocgwinsz) { + var winsize = stream.tty.ops.ioctl_tiocgwinsz( + stream.tty + ); + var argp = syscallGetVarargP(); + HEAP16[argp >> 1] = winsize[0]; + HEAP16[(argp + 2) >> 1] = winsize[1]; + } + return 0; + } + case 21524: { + // TODO: technically, this ioctl call should change the window size. + // but, since emscripten doesn't have any concept of a terminal window + // yet, we'll just silently throw it away as we do TIOCGWINSZ + if (!stream.tty) return -59; + return 0; + } + case 21515: { + if (!stream.tty) return -59; + return 0; + } + default: + return -28; // not supported + } + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_ioctl.sig = 'iiip'; + + function ___syscall_listen(fd, backlog) { + try { + var sock = getSocketFromFD(fd); + sock.sock_ops.listen(sock, backlog); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_listen.sig = 'iiiiiii'; + + function ___syscall_lstat64(path, buf) { + try { + path = SYSCALLS.getStr(path); + return SYSCALLS.writeStat(buf, FS.lstat(path)); + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_lstat64.sig = 'ipp'; + + function ___syscall_mkdirat(dirfd, path, mode) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + FS.mkdir(path, mode, 0); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_mkdirat.sig = 'iipi'; + + function ___syscall_mknodat(dirfd, path, mode, dev) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + // we don't want this in the JS API as it uses mknod to create all nodes. + switch (mode & 61440) { + case 32768: + case 8192: + case 24576: + case 4096: + case 49152: + break; + default: + return -28; + } + FS.mknod(path, mode, dev); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_mknodat.sig = 'iipii'; + + function ___syscall_newfstatat(dirfd, path, buf, flags) { + try { + path = SYSCALLS.getStr(path); + var nofollow = flags & 256; + var allowEmpty = flags & 4096; + flags = flags & ~6400; + path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); + return SYSCALLS.writeStat( + buf, + nofollow ? FS.lstat(path) : FS.stat(path) + ); + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_newfstatat.sig = 'iippi'; + + function ___syscall_openat(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; + } + } + ___syscall_openat.sig = 'iipip'; + + var PIPEFS = { + BUCKET_BUFFER_SIZE: 8192, + mount(mount) { + // Do not pollute the real root directory or its child nodes with pipes + // Looks like it is OK to create another pseudo-root node not linked to the FS.root hierarchy this way + return FS.createNode(null, '/', 16384 | 0o777, 0); + }, + createPipe() { + var pipe = { + buckets: [], + // refcnt 2 because pipe has a read end and a write end. We need to be + // able to read from the read end after write end is closed. + refcnt: 2, + timestamp: new Date(), + }; + + pipe.buckets.push({ + buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), + offset: 0, + roffset: 0, + }); + + var rName = PIPEFS.nextname(); + var wName = PIPEFS.nextname(); + var rNode = FS.createNode(PIPEFS.root, rName, 4096, 0); + var wNode = FS.createNode(PIPEFS.root, wName, 4096, 0); + + rNode.pipe = pipe; + wNode.pipe = pipe; + + var readableStream = FS.createStream({ + path: rName, + node: rNode, + flags: 0, + seekable: false, + stream_ops: PIPEFS.stream_ops, + }); + rNode.stream = readableStream; + + var writableStream = FS.createStream({ + path: wName, + node: wNode, + flags: 1, + seekable: false, + stream_ops: PIPEFS.stream_ops, + }); + wNode.stream = writableStream; + + return { + readable_fd: readableStream.fd, + writable_fd: writableStream.fd, + }; + }, + stream_ops: { + getattr(stream) { + var node = stream.node; + var timestamp = node.pipe.timestamp; + return { + dev: 14, + ino: node.id, + mode: 0o10600, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + size: 0, + atime: timestamp, + mtime: timestamp, + ctime: timestamp, + blksize: 4096, + blocks: 0, + }; + }, + poll(stream) { + var pipe = stream.node.pipe; + + if ((stream.flags & 2097155) === 1) { + return 256 | 4; + } + for (var bucket of pipe.buckets) { + if (bucket.offset - bucket.roffset > 0) { + return 64 | 1; + } + } + + return 0; + }, + dup(stream) { + stream.node.pipe.refcnt++; + }, + ioctl(stream, request, varargs) { + return 28; + }, + fsync(stream) { + return 28; + }, + read(stream, buffer, offset, length, position /* ignored */) { + var pipe = stream.node.pipe; + var currentLength = 0; + + for (var bucket of pipe.buckets) { + currentLength += bucket.offset - bucket.roffset; + } + + var data = buffer.subarray(offset, offset + length); + + if (length <= 0) { + return 0; + } + if (currentLength == 0) { + if (pipe.refcnt < 2) { + return 0; + } + throw new FS.ErrnoError(6); + } + var toRead = Math.min(currentLength, length); + + var totalRead = toRead; + var toRemove = 0; + + for (var bucket of pipe.buckets) { + var bucketSize = bucket.offset - bucket.roffset; + + if (toRead <= bucketSize) { + var tmpSlice = bucket.buffer.subarray( + bucket.roffset, + bucket.offset + ); + if (toRead < bucketSize) { + tmpSlice = tmpSlice.subarray(0, toRead); + bucket.roffset += toRead; + } else { + toRemove++; + } + data.set(tmpSlice); + break; + } else { + var tmpSlice = bucket.buffer.subarray( + bucket.roffset, + bucket.offset + ); + data.set(tmpSlice); + data = data.subarray(tmpSlice.byteLength); + toRead -= tmpSlice.byteLength; + toRemove++; + } + } + + if (toRemove && toRemove == pipe.buckets.length) { + // Do not generate excessive garbage in use cases such as + // write several bytes, read everything, write several bytes, read everything... + toRemove--; + pipe.buckets[toRemove].offset = 0; + pipe.buckets[toRemove].roffset = 0; + } + + pipe.buckets.splice(0, toRemove); + + return totalRead; + }, + write(stream, buffer, offset, length, position /* ignored */) { + var pipe = stream.node.pipe; + + var data = buffer.subarray(offset, offset + length); + + var dataLen = data.byteLength; + if (dataLen <= 0) { + return 0; + } + + var currBucket = null; + + if (pipe.buckets.length == 0) { + currBucket = { + buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), + offset: 0, + roffset: 0, + }; + pipe.buckets.push(currBucket); + } else { + currBucket = pipe.buckets[pipe.buckets.length - 1]; + } + + var freeBytesInCurrBuffer = + PIPEFS.BUCKET_BUFFER_SIZE - currBucket.offset; + if (freeBytesInCurrBuffer >= dataLen) { + currBucket.buffer.set(data, currBucket.offset); + currBucket.offset += dataLen; + return dataLen; + } else if (freeBytesInCurrBuffer > 0) { + currBucket.buffer.set( + data.subarray(0, freeBytesInCurrBuffer), + currBucket.offset + ); + currBucket.offset += freeBytesInCurrBuffer; + data = data.subarray( + freeBytesInCurrBuffer, + data.byteLength + ); + } + + var numBuckets = + (data.byteLength / PIPEFS.BUCKET_BUFFER_SIZE) | 0; + var remElements = data.byteLength % PIPEFS.BUCKET_BUFFER_SIZE; + + for (var i = 0; i < numBuckets; i++) { + var newBucket = { + buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), + offset: PIPEFS.BUCKET_BUFFER_SIZE, + roffset: 0, + }; + pipe.buckets.push(newBucket); + newBucket.buffer.set( + data.subarray(0, PIPEFS.BUCKET_BUFFER_SIZE) + ); + data = data.subarray( + PIPEFS.BUCKET_BUFFER_SIZE, + data.byteLength + ); + } + + if (remElements > 0) { + var newBucket = { + buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), + offset: data.byteLength, + roffset: 0, + }; + pipe.buckets.push(newBucket); + newBucket.buffer.set(data); + } + + return dataLen; + }, + close(stream) { + var pipe = stream.node.pipe; + pipe.refcnt--; + if (pipe.refcnt === 0) { + pipe.buckets = null; + } + }, + }, + nextname() { + if (!PIPEFS.nextname.current) { + PIPEFS.nextname.current = 0; + } + return 'pipe[' + PIPEFS.nextname.current++ + ']'; + }, + }; + function ___syscall_pipe(fdPtr) { + try { + if (fdPtr == 0) { + throw new FS.ErrnoError(21); + } + + var res = PIPEFS.createPipe(); + + HEAP32[fdPtr >> 2] = res.readable_fd; + HEAP32[(fdPtr + 4) >> 2] = res.writable_fd; + + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_pipe.sig = 'ip'; + + function ___syscall_poll(fds, nfds, timeout) { + try { + var nonzero = 0; + for (var i = 0; i < nfds; i++) { + var pollfd = fds + 8 * i; + var fd = HEAP32[pollfd >> 2]; + var events = HEAP16[(pollfd + 4) >> 1]; + var mask = 32; + var stream = FS.getStream(fd); + if (stream) { + mask = SYSCALLS.DEFAULT_POLLMASK; + if (stream.stream_ops?.poll) { + mask = stream.stream_ops.poll(stream, -1); + } + } + mask &= events | 8 | 16; + if (mask) nonzero++; + HEAP16[(pollfd + 6) >> 1] = mask; + } + return nonzero; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_poll.sig = 'ipii'; + + function ___syscall_readlinkat(dirfd, path, buf, bufsize) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + if (bufsize <= 0) return -28; + var ret = FS.readlink(path); + + var len = Math.min(bufsize, lengthBytesUTF8(ret)); + var endChar = HEAP8[buf + len]; + stringToUTF8(ret, buf, bufsize + 1); + // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!) + // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write. + HEAP8[buf + len] = endChar; + return len; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_readlinkat.sig = 'iippp'; + + function ___syscall_recvfrom(fd, buf, len, flags, addr, addrlen) { + try { + var sock = getSocketFromFD(fd); + var msg = sock.sock_ops.recvmsg( + sock, + len, + typeof flags !== 'undefined' ? flags : 0 + ); + if (!msg) return 0; // socket is closed + if (addr) { + var errno = writeSockaddr( + addr, + sock.family, + DNS.lookup_name(msg.addr), + msg.port, + addrlen + ); + } + HEAPU8.set(msg.buffer, buf); + return msg.buffer.byteLength; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_recvfrom.sig = 'iippipp'; + + function ___syscall_recvmsg(fd, message, flags, d1, d2, d3) { + try { + var sock = getSocketFromFD(fd); + var iov = HEAPU32[(message + 8) >> 2]; + var num = HEAP32[(message + 12) >> 2]; + // get the total amount of data we can read across all arrays + var total = 0; + for (var i = 0; i < num; i++) { + total += HEAP32[(iov + (8 * i + 4)) >> 2]; + } + // try to read total data + var msg = sock.sock_ops.recvmsg(sock, total); + if (!msg) return 0; // socket is closed + + // TODO honor flags: + // MSG_OOB + // Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific. + // MSG_PEEK + // Peeks at the incoming message. + // MSG_WAITALL + // Requests that the function block until the full amount of data requested can be returned. The function may return a smaller amount of data if a signal is caught, if the connection is terminated, if MSG_PEEK was specified, or if an error is pending for the socket. + + // write the source address out + var name = HEAPU32[message >> 2]; + if (name) { + var errno = writeSockaddr( + name, + sock.family, + DNS.lookup_name(msg.addr), + msg.port + ); + } + // write the buffer out to the scatter-gather arrays + var bytesRead = 0; + var bytesRemaining = msg.buffer.byteLength; + for (var i = 0; bytesRemaining > 0 && i < num; i++) { + var iovbase = HEAPU32[(iov + (8 * i + 0)) >> 2]; + var iovlen = HEAP32[(iov + (8 * i + 4)) >> 2]; + if (!iovlen) { + continue; + } + var length = Math.min(iovlen, bytesRemaining); + var buf = msg.buffer.subarray(bytesRead, bytesRead + length); + HEAPU8.set(buf, iovbase + bytesRead); + bytesRead += length; + bytesRemaining -= length; + } + + // TODO set msghdr.msg_flags + // MSG_EOR + // End of record was received (if supported by the protocol). + // MSG_OOB + // Out-of-band data was received. + // MSG_TRUNC + // Normal data was truncated. + // MSG_CTRUNC + + return bytesRead; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_recvmsg.sig = 'iipiiii'; + + function ___syscall_renameat(olddirfd, oldpath, newdirfd, newpath) { + try { + oldpath = SYSCALLS.getStr(oldpath); + newpath = SYSCALLS.getStr(newpath); + oldpath = SYSCALLS.calculateAt(olddirfd, oldpath); + newpath = SYSCALLS.calculateAt(newdirfd, newpath); + FS.rename(oldpath, newpath); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_renameat.sig = 'iipip'; + + function ___syscall_rmdir(path) { + try { + path = SYSCALLS.getStr(path); + FS.rmdir(path); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_rmdir.sig = 'ip'; + + function ___syscall_sendmsg(fd, message, flags, d1, d2, d3) { + try { + var sock = getSocketFromFD(fd); + var iov = HEAPU32[(message + 8) >> 2]; + var num = HEAP32[(message + 12) >> 2]; + // read the address and port to send to + var addr, port; + var name = HEAPU32[message >> 2]; + var namelen = HEAP32[(message + 4) >> 2]; + if (name) { + var info = getSocketAddress(name, namelen); + port = info.port; + addr = info.addr; + } + // concatenate scatter-gather arrays into one message buffer + var total = 0; + for (var i = 0; i < num; i++) { + total += HEAP32[(iov + (8 * i + 4)) >> 2]; + } + var view = new Uint8Array(total); + var offset = 0; + for (var i = 0; i < num; i++) { + var iovbase = HEAPU32[(iov + (8 * i + 0)) >> 2]; + var iovlen = HEAP32[(iov + (8 * i + 4)) >> 2]; + for (var j = 0; j < iovlen; j++) { + view[offset++] = HEAP8[iovbase + j]; + } + } + // write the buffer + return sock.sock_ops.sendmsg(sock, view, 0, total, addr, port); + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_sendmsg.sig = 'iipippi'; + + function ___syscall_sendto(fd, message, length, flags, addr, addr_len) { + try { + var sock = getSocketFromFD(fd); + if (!addr) { + // send, no address provided + return FS.write(sock.stream, HEAP8, message, length); + } + var dest = getSocketAddress(addr, addr_len); + // sendto an address + return sock.sock_ops.sendmsg( + sock, + HEAP8, + message, + length, + dest.addr, + dest.port + ); + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_sendto.sig = 'iippipp'; + + function ___syscall_socket(domain, type, protocol) { + try { + var sock = SOCKFS.createSocket(domain, type, protocol); + return sock.stream.fd; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_socket.sig = 'iiiiiii'; + + function ___syscall_stat64(path, buf) { + try { + path = SYSCALLS.getStr(path); + return SYSCALLS.writeStat(buf, FS.stat(path)); + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_stat64.sig = 'ipp'; + + function ___syscall_statfs64(path, size, buf) { + try { + SYSCALLS.writeStatFs(buf, FS.statfs(SYSCALLS.getStr(path))); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_statfs64.sig = 'ippp'; + + function ___syscall_symlinkat(target, dirfd, linkpath) { + try { + target = SYSCALLS.getStr(target); + linkpath = SYSCALLS.getStr(linkpath); + linkpath = SYSCALLS.calculateAt(dirfd, linkpath); + FS.symlink(target, linkpath); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_symlinkat.sig = 'ipip'; + + function ___syscall_truncate64(path, length) { + length = bigintToI53Checked(length); + + try { + if (isNaN(length)) return -61; + path = SYSCALLS.getStr(path); + FS.truncate(path, length); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_truncate64.sig = 'ipj'; + + function ___syscall_unlinkat(dirfd, path, flags) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path); + if (!flags) { + FS.unlink(path); + } else if (flags === 512) { + FS.rmdir(path); + } else { + return -28; + } + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_unlinkat.sig = 'iipi'; + + var readI53FromI64 = (ptr) => { + return HEAPU32[ptr >> 2] + HEAP32[(ptr + 4) >> 2] * 4294967296; + }; + + function ___syscall_utimensat(dirfd, path, times, flags) { + try { + path = SYSCALLS.getStr(path); + path = SYSCALLS.calculateAt(dirfd, path, true); + var now = Date.now(), + atime, + mtime; + if (!times) { + atime = now; + mtime = now; + } else { + var seconds = readI53FromI64(times); + var nanoseconds = HEAP32[(times + 8) >> 2]; + if (nanoseconds == 1073741823) { + atime = now; + } else if (nanoseconds == 1073741822) { + atime = null; + } else { + atime = seconds * 1000 + nanoseconds / (1000 * 1000); + } + times += 16; + seconds = readI53FromI64(times); + nanoseconds = HEAP32[(times + 8) >> 2]; + if (nanoseconds == 1073741823) { + mtime = now; + } else if (nanoseconds == 1073741822) { + mtime = null; + } else { + mtime = seconds * 1000 + nanoseconds / (1000 * 1000); + } + } + // null here means UTIME_OMIT was passed. If both were set to UTIME_OMIT then + // we can skip the call completely. + if ((mtime ?? atime) !== null) { + FS.utime(path, atime, mtime); + } + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_utimensat.sig = 'iippi'; + + var __abort_js = () => abort(''); + __abort_js.sig = 'v'; + + var dlSetError = (msg) => { + var sp = stackSave(); + var cmsg = stringToUTF8OnStack(msg); + ___dl_seterr(cmsg, 0); + stackRestore(sp); + }; + + var dlopenInternal = (handle, jsflags) => { + // void *dlopen(const char *file, int mode); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html + var filename = UTF8ToString(handle + 36); + var flags = HEAP32[(handle + 4) >> 2]; + filename = PATH.normalize(filename); + var searchpaths = []; + + var global = Boolean(flags & 256); + var localScope = global ? null : {}; + + // We don't care about RTLD_NOW and RTLD_LAZY. + var combinedFlags = { + global, + nodelete: Boolean(flags & 4096), + loadAsync: jsflags.loadAsync, + }; + + if (jsflags.loadAsync) { + return loadDynamicLibrary( + filename, + combinedFlags, + localScope, + handle + ); + } + + try { + return loadDynamicLibrary( + filename, + combinedFlags, + localScope, + handle + ); + } catch (e) { + dlSetError(`could not load dynamic lib: ${filename}\n${e}`); + return 0; + } + }; + function __dlopen_js(handle) { + var jsflags = { loadAsync: false }; + return dlopenInternal(handle, jsflags); + } + __dlopen_js.sig = 'pp'; + + var __dlsym_js = (handle, symbol, symbolIndex) => { + // void *dlsym(void *restrict handle, const char *restrict name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html + symbol = UTF8ToString(symbol); + var result; + var newSymIndex; + + var lib = LDSO.loadedLibsByHandle[handle]; + newSymIndex = Object.keys(lib.exports).indexOf(symbol); + if (newSymIndex == -1 || lib.exports[symbol].stub) { + dlSetError( + `Tried to lookup unknown symbol "${symbol}" in dynamic lib: ${lib.name}` + ); + return 0; + } + result = lib.exports[symbol]; + + if (typeof result == 'function') { + // Asyncify wraps exports, and we need to look through those wrappers. + if (result.orig) { + result = result.orig; + } + var addr = getFunctionAddress(result); + if (addr) { + result = addr; + } else { + // Insert the function into the wasm table. If its a direct wasm + // function the second argument will not be needed. If its a JS + // function we rely on the `sig` attribute being set based on the + // `__sig` specified in library JS file. + result = addFunction(result, result.sig); + HEAPU32[symbolIndex >> 2] = newSymIndex; + } + } + return result; + }; + __dlsym_js.sig = 'pppp'; + + 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; + } + quit_(1, e); + }; + + var runtimeKeepaliveCounter = 0; + var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; + var _proc_exit = (code) => { + EXITSTATUS = code; + if (!keepRuntimeAlive()) { + Module['onExit']?.(code); + ABORT = true; + } + quit_(code, new ExitStatus(code)); + }; + _proc_exit.sig = 'vi'; + + /** @param {boolean|number=} implicit */ + var exitJS = (status, implicit) => { + EXITSTATUS = status; + + if (!keepRuntimeAlive()) { + exitRuntime(); + } + + _proc_exit(status); + }; + var _exit = exitJS; + _exit.sig = 'vi'; + + var maybeExit = () => { + if (runtimeExited) { + return; + } + if (!keepRuntimeAlive()) { + try { + _exit(EXITSTATUS); + } catch (e) { + handleException(e); + } + } + }; + var callUserCallback = (func) => { + if (runtimeExited || ABORT) { + return; + } + try { + func(); + maybeExit(); + } catch (e) { + handleException(e); + } + }; + + var runtimeKeepalivePush = () => { + runtimeKeepaliveCounter += 1; + }; + runtimeKeepalivePush.sig = 'v'; + + var runtimeKeepalivePop = () => { + runtimeKeepaliveCounter -= 1; + }; + runtimeKeepalivePop.sig = 'v'; + + var __emscripten_dlopen_js = (handle, onsuccess, onerror, user_data) => { + /** @param {Object=} e */ + function errorCallback(e) { + var filename = UTF8ToString(handle + 36); + dlSetError(`'Could not load dynamic lib: ${filename}\n${e}`); + runtimeKeepalivePop(); + callUserCallback(() => + (( + a1, + a2 + ) => {}) /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + user_data + ) + ); + } + function successCallback() { + runtimeKeepalivePop(); + callUserCallback(() => + (( + a1, + a2 + ) => {}) /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + user_data + ) + ); + } + + runtimeKeepalivePush(); + var promise = dlopenInternal(handle, { loadAsync: true }); + if (promise) { + promise.then(successCallback, errorCallback); + } else { + errorCallback(); + } + }; + __emscripten_dlopen_js.sig = 'vpppp'; + + var getExecutableName = () => thisProgram || './this.program'; + + var __emscripten_get_progname = (str, len) => + stringToUTF8(getExecutableName(), str, len); + __emscripten_get_progname.sig = 'vpi'; + + var jsStackTrace = () => new Error().stack.toString(); + /** @param {number=} flags */ + var getCallstack = (flags) => { + var callstack = jsStackTrace(); + + // Process all lines: + var lines = callstack.split('\n'); + callstack = ''; + // Extract components of form: + // ' Object._main@http://server.com:4324:12' + var firefoxRe = new RegExp('\\s*(.*?)@(.*?):([0-9]+):([0-9]+)'); + // Extract components of form: + // ' at Object._main (http://server.com/file.html:4324:12)' + var chromeRe = new RegExp('\\s*at (.*?) \\\((.*):(.*):(.*)\\\)'); + + for (var line of lines) { + var symbolName = ''; + var file = ''; + var lineno = 0; + var column = 0; + + var parts = chromeRe.exec(line); + if (parts?.length == 5) { + symbolName = parts[1]; + file = parts[2]; + lineno = parts[3]; + column = parts[4]; + } else { + parts = firefoxRe.exec(line); + if (parts?.length >= 4) { + symbolName = parts[1]; + file = parts[2]; + lineno = parts[3]; + // Old Firefox doesn't carry column information, but in new FF30, it + // is present. See https://bugzil.la/762556 + column = parts[4] | 0; + } else { + // Was not able to extract this line for demangling/sourcemapping + // purposes. Output it as-is. + callstack += line + '\n'; + continue; + } + } + + // Find the symbols in the callstack that corresponds to the functions that + // report callstack information, and remove everything up to these from the + // output. + if ( + symbolName == '_emscripten_log' || + symbolName == '_emscripten_get_callstack' + ) { + callstack = ''; + continue; + } + + if (flags & 24) { + if (flags & 64) { + file = file.substring( + file.replace(/\\/g, '/').lastIndexOf('/') + 1 + ); + } + callstack += ` at ${symbolName} (${file}:${lineno}:${column})\n`; + } + } + // Trim extra whitespace at the end of the output. + callstack = callstack.replace(/\s+$/, ''); + return callstack; + }; + + var __emscripten_log_formatted = (flags, str) => { + str = UTF8ToString(str); + + if (flags & 24) { + str = str.replace(/\s+$/, ''); // Ensure the message and the callstack are joined cleanly with exactly one newline. + str += (str.length > 0 ? '\n' : '') + getCallstack(flags); + } + + if (flags & 1) { + if (flags & 4) { + console.error(str); + } else if (flags & 2) { + console.warn(str); + } else if (flags & 512) { + console.info(str); + } else if (flags & 256) { + console.debug(str); + } else { + console.log(str); + } + } else if (flags & 6) { + err(str); + } else { + out(str); + } + }; + __emscripten_log_formatted.sig = 'vip'; + + var __emscripten_lookup_name = (name) => { + // uint32_t _emscripten_lookup_name(const char *name); + var nameString = UTF8ToString(name); + return inetPton4(DNS.lookup_name(nameString)); + }; + __emscripten_lookup_name.sig = 'ip'; + + var __emscripten_runtime_keepalive_clear = () => { + noExitRuntime = false; + runtimeKeepaliveCounter = 0; + }; + __emscripten_runtime_keepalive_clear.sig = 'v'; + + var __emscripten_system = (command) => { + if (ENVIRONMENT_IS_NODE) { + if (!command) return 1; // shell is available + + var cmdstr = UTF8ToString(command); + if (!cmdstr.length) return 0; // this is what glibc seems to do (shell works test?) + + var cp = require('child_process'); + var ret = cp.spawnSync(cmdstr, [], { + shell: true, + stdio: 'inherit', + }); + + var _W_EXITCODE = (ret, sig) => (ret << 8) | sig; + + // this really only can happen if process is killed by signal + if (ret.status === null) { + // sadly node doesn't expose such function + var signalToNumber = (sig) => { + // implement only the most common ones, and fallback to SIGINT + switch (sig) { + case 'SIGHUP': + return 1; + case 'SIGQUIT': + return 3; + case 'SIGFPE': + return 8; + case 'SIGKILL': + return 9; + case 'SIGALRM': + return 14; + case 'SIGTERM': + return 15; + default: + return 2; + } + }; + return _W_EXITCODE(0, signalToNumber(ret.signal)); + } + + return _W_EXITCODE(ret.status, 0); + } + // int system(const char *command); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html + // Can't call external programs. + if (!command) return 0; // no shell available + return -52; + }; + __emscripten_system.sig = 'ip'; + + var __emscripten_throw_longjmp = () => { + throw Infinity; + }; + __emscripten_throw_longjmp.sig = 'v'; + + function __gmtime_js(time, tmPtr) { + time = bigintToI53Checked(time); + + var date = new Date(time * 1000); + HEAP32[tmPtr >> 2] = date.getUTCSeconds(); + HEAP32[(tmPtr + 4) >> 2] = date.getUTCMinutes(); + HEAP32[(tmPtr + 8) >> 2] = date.getUTCHours(); + HEAP32[(tmPtr + 12) >> 2] = date.getUTCDate(); + HEAP32[(tmPtr + 16) >> 2] = date.getUTCMonth(); + HEAP32[(tmPtr + 20) >> 2] = date.getUTCFullYear() - 1900; + HEAP32[(tmPtr + 24) >> 2] = date.getUTCDay(); + var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); + var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24)) | 0; + HEAP32[(tmPtr + 28) >> 2] = yday; + } + __gmtime_js.sig = 'vjp'; + + 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 * 1000); + HEAP32[tmPtr >> 2] = date.getSeconds(); + HEAP32[(tmPtr + 4) >> 2] = date.getMinutes(); + HEAP32[(tmPtr + 8) >> 2] = date.getHours(); + HEAP32[(tmPtr + 12) >> 2] = date.getDate(); + HEAP32[(tmPtr + 16) >> 2] = date.getMonth(); + HEAP32[(tmPtr + 20) >> 2] = date.getFullYear() - 1900; + HEAP32[(tmPtr + 24) >> 2] = date.getDay(); + + var yday = ydayFromDate(date) | 0; + HEAP32[(tmPtr + 28) >> 2] = yday; + HEAP32[(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; + HEAP32[(tmPtr + 32) >> 2] = dst; + } + __localtime_js.sig = 'vjp'; + + var __mktime_js = function (tmPtr) { + var ret = (() => { + var date = new Date( + HEAP32[(tmPtr + 20) >> 2] + 1900, + HEAP32[(tmPtr + 16) >> 2], + HEAP32[(tmPtr + 12) >> 2], + HEAP32[(tmPtr + 8) >> 2], + HEAP32[(tmPtr + 4) >> 2], + HEAP32[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 = HEAP32[(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. + HEAP32[(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) * 60000 + ); + } + + HEAP32[(tmPtr + 24) >> 2] = date.getDay(); + var yday = ydayFromDate(date) | 0; + HEAP32[(tmPtr + 28) >> 2] = yday; + // To match expected behavior, update fields from date + HEAP32[tmPtr >> 2] = date.getSeconds(); + HEAP32[(tmPtr + 4) >> 2] = date.getMinutes(); + HEAP32[(tmPtr + 8) >> 2] = date.getHours(); + HEAP32[(tmPtr + 12) >> 2] = date.getDate(); + HEAP32[(tmPtr + 16) >> 2] = date.getMonth(); + HEAP32[(tmPtr + 20) >> 2] = date.getYear(); + + var timeMs = date.getTime(); + if (isNaN(timeMs)) { + return -1; + } + // Return time in microseconds + return timeMs / 1000; + })(); + return BigInt(ret); + }; + __mktime_js.sig = 'jp'; + + function __mmap_js(len, prot, flags, fd, offset, allocated, addr) { + offset = bigintToI53Checked(offset); + + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var res = FS.mmap(stream, len, offset, prot, flags); + var ptr = res.ptr; + HEAP32[allocated >> 2] = res.allocated; + HEAPU32[addr >> 2] = ptr; + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + __mmap_js.sig = 'ipiiijpp'; + + function __msync_js(addr, len, prot, flags, fd, offset) { + offset = bigintToI53Checked(offset); + + try { + if (isNaN(offset)) return -61; + SYSCALLS.doMsync( + addr, + SYSCALLS.getStreamFromFD(fd), + len, + flags, + offset + ); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + __msync_js.sig = 'ippiiij'; + + function __munmap_js(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; + } + } + __munmap_js.sig = 'ippiiij'; + + var timers = {}; + + var _emscripten_get_now = () => performance.now(); + _emscripten_get_now.sig = 'd'; + var __setitimer_js = (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(() => { + delete timers[which]; + callUserCallback(() => + __emscripten_timeout(which, _emscripten_get_now()) + ); + }, timeout_ms); + timers[which] = { id, timeout_ms }; + return 0; + }; + __setitimer_js.sig = 'iid'; + + var __timegm_js = function (tmPtr) { + var ret = (() => { + var time = Date.UTC( + HEAP32[(tmPtr + 20) >> 2] + 1900, + HEAP32[(tmPtr + 16) >> 2], + HEAP32[(tmPtr + 12) >> 2], + HEAP32[(tmPtr + 8) >> 2], + HEAP32[(tmPtr + 4) >> 2], + HEAP32[tmPtr >> 2], + 0 + ); + var date = new Date(time); + + HEAP32[(tmPtr + 24) >> 2] = date.getUTCDay(); + var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); + var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24)) | 0; + HEAP32[(tmPtr + 28) >> 2] = yday; + + return date.getTime() / 1000; + })(); + return BigInt(ret); + }; + __timegm_js.sig = 'jp'; + + 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 + HEAPU32[timezone >> 2] = stdTimezoneOffset * 60; + + HEAP32[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); + 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); + } + }; + __tzset_js.sig = 'vpppp'; + + var _emscripten_set_main_loop_timing = (mode, value) => { + MainLoop.timingMode = mode; + MainLoop.timingValue = value; + + if (!MainLoop.func) { + return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. + } + + if (!MainLoop.running) { + runtimeKeepalivePush(); + MainLoop.running = true; + } + if (mode == 0) { + MainLoop.scheduler = function MainLoop_scheduler_setTimeout() { + var timeUntilNextTick = + Math.max( + 0, + MainLoop.tickStartTime + value - _emscripten_get_now() + ) | 0; + setTimeout(MainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop + }; + MainLoop.method = 'timeout'; + } else if (mode == 1) { + MainLoop.scheduler = function MainLoop_scheduler_rAF() { + MainLoop.requestAnimationFrame(MainLoop.runner); + }; + MainLoop.method = 'rAF'; + } else if (mode == 2) { + if (!MainLoop.setImmediate) { + if (globalThis.setImmediate) { + MainLoop.setImmediate = setImmediate; + } else { + // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) + var setImmediates = []; + var emscriptenMainLoopMessageId = 'setimmediate'; + /** @param {Event} event */ + var MainLoop_setImmediate_messageHandler = (event) => { + // When called in current thread or Worker, the main loop ID is structured slightly different to accommodate for --proxy-to-worker runtime listening to Worker events, + // so check for both cases. + if ( + event.data === emscriptenMainLoopMessageId || + event.data.target === emscriptenMainLoopMessageId + ) { + event.stopPropagation(); + setImmediates.shift()(); + } + }; + addEventListener( + 'message', + MainLoop_setImmediate_messageHandler, + true + ); + MainLoop.setImmediate = + /** @type{function(function(): ?, ...?): number} */ ( + (func) => { + setImmediates.push(func); + if (ENVIRONMENT_IS_WORKER) { + Module['setImmediates'] ??= []; + Module['setImmediates'].push(func); + postMessage({ + target: emscriptenMainLoopMessageId, + }); // In --proxy-to-worker, route the message via proxyClient.js + } else + postMessage( + emscriptenMainLoopMessageId, + '*' + ); // On the main thread, can just send the message to itself. + } + ); + } + } + MainLoop.scheduler = function MainLoop_scheduler_setImmediate() { + MainLoop.setImmediate(MainLoop.runner); + }; + MainLoop.method = 'immediate'; + } + return 0; + }; + _emscripten_set_main_loop_timing.sig = 'iii'; + + /** + * @param {number=} arg + * @param {boolean=} noSetTiming + */ + var setMainLoop = ( + iterFunc, + fps, + simulateInfiniteLoop, + arg, + noSetTiming + ) => { + MainLoop.func = iterFunc; + MainLoop.arg = arg; + + var thisMainLoopId = MainLoop.currentlyRunningMainloop; + function checkIsRunning() { + if (thisMainLoopId < MainLoop.currentlyRunningMainloop) { + runtimeKeepalivePop(); + maybeExit(); + return false; + } + return true; + } + + // We create the loop runner here but it is not actually running until + // _emscripten_set_main_loop_timing is called (which might happen a + // later time). This member signifies that the current runner has not + // yet been started so that we can call runtimeKeepalivePush when it + // gets it timing set for the first time. + MainLoop.running = false; + MainLoop.runner = function MainLoop_runner() { + if (ABORT) return; + if (MainLoop.queue.length > 0) { + var start = Date.now(); + var blocker = MainLoop.queue.shift(); + blocker.func(blocker.arg); + if (MainLoop.remainingBlockers) { + var remaining = MainLoop.remainingBlockers; + var next = + remaining % 1 == 0 + ? remaining - 1 + : Math.floor(remaining); + if (blocker.counted) { + MainLoop.remainingBlockers = next; + } else { + // not counted, but move the progress along a tiny bit + next = next + 0.5; // do not steal all the next one's progress + MainLoop.remainingBlockers = (8 * remaining + next) / 9; + } + } + MainLoop.updateStatus(); + + // catches pause/resume main loop from blocker execution + if (!checkIsRunning()) return; + + setTimeout(MainLoop.runner, 0); + return; + } + + // catch pauses from non-main loop sources + if (!checkIsRunning()) return; + + // Implement very basic swap interval control + MainLoop.currentFrameNumber = (MainLoop.currentFrameNumber + 1) | 0; + if ( + MainLoop.timingMode == 1 && + MainLoop.timingValue > 1 && + MainLoop.currentFrameNumber % MainLoop.timingValue != 0 + ) { + // Not the scheduled time to render this frame - skip. + MainLoop.scheduler(); + return; + } else if (MainLoop.timingMode == 0) { + MainLoop.tickStartTime = _emscripten_get_now(); + } + + MainLoop.runIter(iterFunc); + + // catch pauses from the main loop itself + if (!checkIsRunning()) return; + + MainLoop.scheduler(); + }; + + if (!noSetTiming) { + if (fps > 0) { + _emscripten_set_main_loop_timing(0, 1000.0 / fps); + } else { + // Do rAF by rendering each frame (no decimating) + _emscripten_set_main_loop_timing(1, 1); + } + + MainLoop.scheduler(); + } + + if (simulateInfiniteLoop) { + throw 'unwind'; + } + }; + + var MainLoop = { + running: false, + scheduler: null, + method: '', + currentlyRunningMainloop: 0, + func: null, + arg: 0, + timingMode: 0, + timingValue: 0, + currentFrameNumber: 0, + queue: [], + preMainLoop: [], + postMainLoop: [], + pause() { + MainLoop.scheduler = null; + // Incrementing this signals the previous main loop that it's now become old, and it must return. + MainLoop.currentlyRunningMainloop++; + }, + resume() { + MainLoop.currentlyRunningMainloop++; + var timingMode = MainLoop.timingMode; + var timingValue = MainLoop.timingValue; + var func = MainLoop.func; + MainLoop.func = null; + // do not set timing and call scheduler, we will do it on the next lines + setMainLoop(func, 0, false, MainLoop.arg, true); + _emscripten_set_main_loop_timing(timingMode, timingValue); + MainLoop.scheduler(); + }, + updateStatus() { + if (Module['setStatus']) { + var message = Module['statusMessage'] || 'Please wait...'; + var remaining = MainLoop.remainingBlockers ?? 0; + var expected = MainLoop.expectedBlockers ?? 0; + if (remaining) { + if (remaining < expected) { + Module['setStatus']( + `{message} ({expected - remaining}/{expected})` + ); + } else { + Module['setStatus'](message); + } + } else { + Module['setStatus'](''); + } + } + }, + init() { + Module['preMainLoop'] && + MainLoop.preMainLoop.push(Module['preMainLoop']); + Module['postMainLoop'] && + MainLoop.postMainLoop.push(Module['postMainLoop']); + }, + runIter(func) { + if (ABORT) return; + for (var pre of MainLoop.preMainLoop) { + if (pre() === false) { + return; // |return false| skips a frame + } + } + callUserCallback(func); + for (var post of MainLoop.postMainLoop) { + post(); + } + }, + nextRAF: 0, + fakeRequestAnimationFrame(func) { + // try to keep 60fps between calls to here + var now = Date.now(); + if (MainLoop.nextRAF === 0) { + MainLoop.nextRAF = now + 1000 / 60; + } else { + while (now + 2 >= MainLoop.nextRAF) { + // fudge a little, to avoid timer jitter causing us to do lots of delay:0 + MainLoop.nextRAF += 1000 / 60; + } + } + var delay = Math.max(MainLoop.nextRAF - now, 0); + setTimeout(func, delay); + }, + requestAnimationFrame(func) { + if (globalThis.requestAnimationFrame) { + requestAnimationFrame(func); + } else { + MainLoop.fakeRequestAnimationFrame(func); + } + }, + }; + + var AL = { + QUEUE_INTERVAL: 25, + QUEUE_LOOKAHEAD: 0.1, + DEVICE_NAME: 'Emscripten OpenAL', + CAPTURE_DEVICE_NAME: 'Emscripten OpenAL capture', + ALC_EXTENSIONS: { + ALC_SOFT_pause_device: true, + ALC_SOFT_HRTF: true, + }, + AL_EXTENSIONS: { + AL_EXT_float32: true, + AL_SOFT_loop_points: true, + AL_SOFT_source_length: true, + AL_EXT_source_distance_model: true, + AL_SOFT_source_spatialize: true, + }, + _alcErr: 0, + alcErr: 0, + deviceRefCounts: {}, + alcStringCache: {}, + paused: false, + stringCache: {}, + contexts: {}, + currentCtx: null, + buffers: { + 0: { + id: 0, + refCount: 0, + audioBuf: null, + frequency: 0, + bytesPerSample: 2, + channels: 1, + length: 0, + }, + }, + paramArray: [], + _nextId: 1, + newId: () => (AL.freeIds.length > 0 ? AL.freeIds.pop() : AL._nextId++), + freeIds: [], + scheduleContextAudio: (ctx) => { + // If we are animating using the requestAnimationFrame method, then the main loop does not run when in the background. + // To give a perfect glitch-free audio stop when switching from foreground to background, we need to avoid updating + // audio altogether when in the background, so detect that case and kill audio buffer streaming if so. + if ( + MainLoop.timingMode === 1 && + document['visibilityState'] != 'visible' + ) { + return; + } + + for (var i in ctx.sources) { + AL.scheduleSourceAudio(ctx.sources[i]); + } + }, + scheduleSourceAudio: (src, lookahead) => { + // See comment on scheduleContextAudio above. + if ( + MainLoop.timingMode === 1 && + document['visibilityState'] != 'visible' + ) { + return; + } + if (src.state !== 4114) { + return; + } + + var currentTime = AL.updateSourceTime(src); + + var startTime = src.bufStartTime; + var startOffset = src.bufOffset; + var bufCursor = src.bufsProcessed; + + // Advance past any audio that is already scheduled + for (var i = 0; i < src.audioQueue.length; i++) { + var audioSrc = src.audioQueue[i]; + startTime = audioSrc._startTime + audioSrc._duration; + startOffset = 0.0; + bufCursor += audioSrc._skipCount + 1; + } + + if (!lookahead) { + lookahead = AL.QUEUE_LOOKAHEAD; + } + var lookaheadTime = currentTime + lookahead; + var skipCount = 0; + while (startTime < lookaheadTime) { + if (bufCursor >= src.bufQueue.length) { + if (src.looping) { + bufCursor %= src.bufQueue.length; + } else { + break; + } + } + + var buf = src.bufQueue[bufCursor % src.bufQueue.length]; + // If the buffer contains no data, skip it + if (buf.length === 0) { + skipCount++; + // If we've gone through the whole queue and everything is 0 length, just give up + if (skipCount === src.bufQueue.length) { + break; + } + } else { + var audioSrc = src.context.audioCtx.createBufferSource(); + audioSrc.buffer = buf.audioBuf; + audioSrc.playbackRate.value = src.playbackRate; + if (buf.audioBuf._loopStart || buf.audioBuf._loopEnd) { + audioSrc.loopStart = buf.audioBuf._loopStart; + audioSrc.loopEnd = buf.audioBuf._loopEnd; + } + + var duration = 0.0; + // If the source is a looping static buffer, use native looping for gapless playback + if (src.type === 4136 && src.looping) { + duration = Number.POSITIVE_INFINITY; + audioSrc.loop = true; + if (buf.audioBuf._loopStart) { + audioSrc.loopStart = buf.audioBuf._loopStart; + } + if (buf.audioBuf._loopEnd) { + audioSrc.loopEnd = buf.audioBuf._loopEnd; + } + } else { + duration = + (buf.audioBuf.duration - startOffset) / + src.playbackRate; + } + + audioSrc._startOffset = startOffset; + audioSrc._duration = duration; + audioSrc._skipCount = skipCount; + skipCount = 0; + + audioSrc.connect(src.gain); + + if (typeof audioSrc.start != 'undefined') { + // Sample the current time as late as possible to mitigate drift + startTime = Math.max( + startTime, + src.context.audioCtx.currentTime + ); + audioSrc.start(startTime, startOffset); + } else if (typeof audioSrc.noteOn != 'undefined') { + startTime = Math.max( + startTime, + src.context.audioCtx.currentTime + ); + audioSrc.noteOn(startTime); + } + audioSrc._startTime = startTime; + src.audioQueue.push(audioSrc); + + startTime += duration; + } + + startOffset = 0.0; + bufCursor++; + } + }, + updateSourceTime: (src) => { + var currentTime = src.context.audioCtx.currentTime; + if (src.state !== 4114) { + return currentTime; + } + + // if the start time is unset, determine it based on the current offset. + // This will be the case when a source is resumed after being paused, and + // allows us to pretend that the source actually started playing some time + // in the past such that it would just now have reached the stored offset. + if (!isFinite(src.bufStartTime)) { + src.bufStartTime = + currentTime - src.bufOffset / src.playbackRate; + src.bufOffset = 0.0; + } + + var nextStartTime = 0.0; + while (src.audioQueue.length) { + var audioSrc = src.audioQueue[0]; + src.bufsProcessed += audioSrc._skipCount; + nextStartTime = audioSrc._startTime + audioSrc._duration; // n.b. audioSrc._duration already factors in playbackRate, so no divide by src.playbackRate on it. + + if (currentTime < nextStartTime) { + break; + } + + src.audioQueue.shift(); + src.bufStartTime = nextStartTime; + src.bufOffset = 0.0; + src.bufsProcessed++; + } + + if (src.bufsProcessed >= src.bufQueue.length && !src.looping) { + // The source has played its entire queue and is non-looping, so just mark it as stopped. + AL.setSourceState(src, 4116); + } else if (src.type === 4136 && src.looping) { + // If the source is a looping static buffer, determine the buffer offset based on the loop points + var buf = src.bufQueue[0]; + if (buf.length === 0) { + src.bufOffset = 0.0; + } else { + var delta = + (currentTime - src.bufStartTime) * src.playbackRate; + var loopStart = buf.audioBuf._loopStart || 0.0; + var loopEnd = + buf.audioBuf._loopEnd || buf.audioBuf.duration; + if (loopEnd <= loopStart) { + loopEnd = buf.audioBuf.duration; + } + + if (delta < loopEnd) { + src.bufOffset = delta; + } else { + src.bufOffset = + loopStart + + ((delta - loopStart) % (loopEnd - loopStart)); + } + } + } else if (src.audioQueue[0]) { + // The source is still actively playing, so we just need to calculate where we are in the current buffer + // so it can be remembered if the source gets paused. + src.bufOffset = + (currentTime - src.audioQueue[0]._startTime) * + src.playbackRate; + } else { + // The source hasn't finished yet, but there is no scheduled audio left for it. This can be because + // the source has just been started/resumed, or due to an underrun caused by a long blocking operation. + // We need to determine what state we would be in by this point in time so that when we next schedule + // audio playback, it will be just as if no underrun occurred. + + if (src.type !== 4136 && src.looping) { + // if the source is a looping buffer queue, let's first calculate the queue duration, so we can + // quickly fast forward past any full loops of the queue and only worry about the remainder. + var srcDuration = AL.sourceDuration(src) / src.playbackRate; + if (srcDuration > 0.0) { + src.bufStartTime += + Math.floor( + (currentTime - src.bufStartTime) / srcDuration + ) * srcDuration; + } + } + + // Since we've already skipped any full-queue loops if there were any, we just need to find + // out where in the queue the remaining time puts us, which won't require stepping through the + // entire queue more than once. + for (var i = 0; i < src.bufQueue.length; i++) { + if (src.bufsProcessed >= src.bufQueue.length) { + if (src.looping) { + src.bufsProcessed %= src.bufQueue.length; + } else { + AL.setSourceState(src, 4116); + break; + } + } + + var buf = src.bufQueue[src.bufsProcessed]; + if (buf.length > 0) { + nextStartTime = + src.bufStartTime + + buf.audioBuf.duration / src.playbackRate; + + if (currentTime < nextStartTime) { + src.bufOffset = + (currentTime - src.bufStartTime) * + src.playbackRate; + break; + } + + src.bufStartTime = nextStartTime; + } + + src.bufOffset = 0.0; + src.bufsProcessed++; + } + } + + return currentTime; + }, + cancelPendingSourceAudio: (src) => { + AL.updateSourceTime(src); + + for (var i = 1; i < src.audioQueue.length; i++) { + var audioSrc = src.audioQueue[i]; + audioSrc.stop(); + } + + if (src.audioQueue.length > 1) { + src.audioQueue.length = 1; + } + }, + stopSourceAudio: (src) => { + for (var i = 0; i < src.audioQueue.length; i++) { + src.audioQueue[i].stop(); + } + src.audioQueue.length = 0; + }, + setSourceState: (src, state) => { + if (state === 4114) { + if (src.state === 4114 || src.state == 4116) { + src.bufsProcessed = 0; + src.bufOffset = 0.0; + } else { + } + + AL.stopSourceAudio(src); + + src.state = 4114; + src.bufStartTime = Number.NEGATIVE_INFINITY; + AL.scheduleSourceAudio(src); + } else if (state === 4115) { + if (src.state === 4114) { + // Store off the current offset to restore with on resume. + AL.updateSourceTime(src); + AL.stopSourceAudio(src); + + src.state = 4115; + } + } else if (state === 4116) { + if (src.state !== 4113) { + src.state = 4116; + src.bufsProcessed = src.bufQueue.length; + src.bufStartTime = Number.NEGATIVE_INFINITY; + src.bufOffset = 0.0; + AL.stopSourceAudio(src); + } + } else if (state === 4113) { + if (src.state !== 4113) { + src.state = 4113; + src.bufsProcessed = 0; + src.bufStartTime = Number.NEGATIVE_INFINITY; + src.bufOffset = 0.0; + AL.stopSourceAudio(src); + } + } + }, + initSourcePanner: (src) => { + if (src.type === 0x1030 /* AL_UNDETERMINED */) { + return; + } + + // Find the first non-zero buffer in the queue to determine the proper format + var templateBuf = AL.buffers[0]; + for (var i = 0; i < src.bufQueue.length; i++) { + if (src.bufQueue[i].id !== 0) { + templateBuf = src.bufQueue[i]; + break; + } + } + // Create a panner if AL_SOURCE_SPATIALIZE_SOFT is set to true, or alternatively if it's set to auto and the source is mono + if ( + src.spatialize === 1 || + (src.spatialize === 2 /* AL_AUTO_SOFT */ && + templateBuf.channels === 1) + ) { + if (src.panner) { + return; + } + src.panner = src.context.audioCtx.createPanner(); + + AL.updateSourceGlobal(src); + AL.updateSourceSpace(src); + + src.panner.connect(src.context.gain); + src.gain.disconnect(); + src.gain.connect(src.panner); + } else { + if (!src.panner) { + return; + } + + src.panner.disconnect(); + src.gain.disconnect(); + src.gain.connect(src.context.gain); + src.panner = null; + } + }, + updateContextGlobal: (ctx) => { + for (var i in ctx.sources) { + AL.updateSourceGlobal(ctx.sources[i]); + } + }, + updateSourceGlobal: (src) => { + var panner = src.panner; + if (!panner) { + return; + } + + panner.refDistance = src.refDistance; + panner.maxDistance = src.maxDistance; + panner.rolloffFactor = src.rolloffFactor; + + panner.panningModel = src.context.hrtf ? 'HRTF' : 'equalpower'; + + // Use the source's distance model if AL_SOURCE_DISTANCE_MODEL is enabled + var distanceModel = src.context.sourceDistanceModel + ? src.distanceModel + : src.context.distanceModel; + switch (distanceModel) { + case 0: + panner.distanceModel = 'inverse'; + panner.refDistance = 3.40282e38 /* FLT_MAX */; + break; + case 0xd001 /* AL_INVERSE_DISTANCE */: + case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */: + panner.distanceModel = 'inverse'; + break; + case 0xd003 /* AL_LINEAR_DISTANCE */: + case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */: + panner.distanceModel = 'linear'; + break; + case 0xd005 /* AL_EXPONENT_DISTANCE */: + case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */: + panner.distanceModel = 'exponential'; + break; + } + }, + updateListenerSpace: (ctx) => { + var listener = ctx.audioCtx.listener; + if (listener.positionX) { + listener.positionX.value = ctx.listener.position[0]; + listener.positionY.value = ctx.listener.position[1]; + listener.positionZ.value = ctx.listener.position[2]; + } else { + listener.setPosition( + ctx.listener.position[0], + ctx.listener.position[1], + ctx.listener.position[2] + ); + } + if (listener.forwardX) { + listener.forwardX.value = ctx.listener.direction[0]; + listener.forwardY.value = ctx.listener.direction[1]; + listener.forwardZ.value = ctx.listener.direction[2]; + listener.upX.value = ctx.listener.up[0]; + listener.upY.value = ctx.listener.up[1]; + listener.upZ.value = ctx.listener.up[2]; + } else { + listener.setOrientation( + ctx.listener.direction[0], + ctx.listener.direction[1], + ctx.listener.direction[2], + ctx.listener.up[0], + ctx.listener.up[1], + ctx.listener.up[2] + ); + } + + // Update sources that are relative to the listener + for (var i in ctx.sources) { + AL.updateSourceSpace(ctx.sources[i]); + } + }, + updateSourceSpace: (src) => { + if (!src.panner) { + return; + } + var panner = src.panner; + + var posX = src.position[0]; + var posY = src.position[1]; + var posZ = src.position[2]; + var dirX = src.direction[0]; + var dirY = src.direction[1]; + var dirZ = src.direction[2]; + + var listener = src.context.listener; + var lPosX = listener.position[0]; + var lPosY = listener.position[1]; + var lPosZ = listener.position[2]; + + // WebAudio does spatialization in world-space coordinates, meaning both the buffer sources and + // the listener position are in the same absolute coordinate system relative to a fixed origin. + // By default, OpenAL works this way as well, but it also provides a "listener relative" mode, where + // a buffer source's coordinate are interpreted not in absolute world space, but as being relative + // to the listener object itself, so as the listener moves the source appears to move with it + // with no update required. Since web audio does not support this mode, we must transform the source + // coordinates from listener-relative space to absolute world space. + // + // We do this via affine transformation matrices applied to the source position and source direction. + // A change-of-basis converts from listener-space displacements to world-space displacements, + // which must be done for both the source position and direction. Lastly, the source position must be + // added to the listener position to get the final source position, since the source position represents + // a displacement from the listener. + if (src.relative) { + // Negate the listener direction since forward is -Z. + var lBackX = -listener.direction[0]; + var lBackY = -listener.direction[1]; + var lBackZ = -listener.direction[2]; + var lUpX = listener.up[0]; + var lUpY = listener.up[1]; + var lUpZ = listener.up[2]; + + var inverseMagnitude = (x, y, z) => { + var length = Math.sqrt(x * x + y * y + z * z); + + if (length < Number.EPSILON) { + return 0.0; + } + + return 1.0 / length; + }; + + // Normalize the Back vector + var invMag = inverseMagnitude(lBackX, lBackY, lBackZ); + lBackX *= invMag; + lBackY *= invMag; + lBackZ *= invMag; + + // ...and the Up vector + invMag = inverseMagnitude(lUpX, lUpY, lUpZ); + lUpX *= invMag; + lUpY *= invMag; + lUpZ *= invMag; + + // Calculate the Right vector as the cross product of the Up and Back vectors + var lRightX = lUpY * lBackZ - lUpZ * lBackY; + var lRightY = lUpZ * lBackX - lUpX * lBackZ; + var lRightZ = lUpX * lBackY - lUpY * lBackX; + + // Back and Up might not be exactly perpendicular, so the cross product also needs normalization + invMag = inverseMagnitude(lRightX, lRightY, lRightZ); + lRightX *= invMag; + lRightY *= invMag; + lRightZ *= invMag; + + // Recompute Up from the now orthonormal Right and Back vectors so we have a fully orthonormal basis + lUpX = lBackY * lRightZ - lBackZ * lRightY; + lUpY = lBackZ * lRightX - lBackX * lRightZ; + lUpZ = lBackX * lRightY - lBackY * lRightX; + + var oldX = dirX; + var oldY = dirY; + var oldZ = dirZ; + + // Use our 3 vectors to apply a change-of-basis matrix to the source direction + dirX = oldX * lRightX + oldY * lUpX + oldZ * lBackX; + dirY = oldX * lRightY + oldY * lUpY + oldZ * lBackY; + dirZ = oldX * lRightZ + oldY * lUpZ + oldZ * lBackZ; + + oldX = posX; + oldY = posY; + oldZ = posZ; + + // ...and to the source position + posX = oldX * lRightX + oldY * lUpX + oldZ * lBackX; + posY = oldX * lRightY + oldY * lUpY + oldZ * lBackY; + posZ = oldX * lRightZ + oldY * lUpZ + oldZ * lBackZ; + + // The change-of-basis corrects the orientation, but the origin is still the listener. + // Translate the source position by the listener position to finish. + posX += lPosX; + posY += lPosY; + posZ += lPosZ; + } + + if (panner.positionX) { + // Assigning to panner.positionX/Y/Z unnecessarily seems to cause performance issues + // See https://github.com/emscripten-core/emscripten/issues/15847 + + if (posX != panner.positionX.value) + panner.positionX.value = posX; + if (posY != panner.positionY.value) + panner.positionY.value = posY; + if (posZ != panner.positionZ.value) + panner.positionZ.value = posZ; + } else { + panner.setPosition(posX, posY, posZ); + } + if (panner.orientationX) { + // Assigning to panner.orientation/Y/Z unnecessarily seems to cause performance issues + // See https://github.com/emscripten-core/emscripten/issues/15847 + + if (dirX != panner.orientationX.value) + panner.orientationX.value = dirX; + if (dirY != panner.orientationY.value) + panner.orientationY.value = dirY; + if (dirZ != panner.orientationZ.value) + panner.orientationZ.value = dirZ; + } else { + panner.setOrientation(dirX, dirY, dirZ); + } + + var oldShift = src.dopplerShift; + var velX = src.velocity[0]; + var velY = src.velocity[1]; + var velZ = src.velocity[2]; + var lVelX = listener.velocity[0]; + var lVelY = listener.velocity[1]; + var lVelZ = listener.velocity[2]; + if ( + (posX === lPosX && posY === lPosY && posZ === lPosZ) || + (velX === lVelX && velY === lVelY && velZ === lVelZ) + ) { + src.dopplerShift = 1.0; + } else { + // Doppler algorithm from 1.1 spec + var speedOfSound = src.context.speedOfSound; + var dopplerFactor = src.context.dopplerFactor; + + var slX = lPosX - posX; + var slY = lPosY - posY; + var slZ = lPosZ - posZ; + + var magSl = Math.sqrt(slX * slX + slY * slY + slZ * slZ); + var vls = (slX * lVelX + slY * lVelY + slZ * lVelZ) / magSl; + var vss = (slX * velX + slY * velY + slZ * velZ) / magSl; + + vls = Math.min(vls, speedOfSound / dopplerFactor); + vss = Math.min(vss, speedOfSound / dopplerFactor); + + src.dopplerShift = + (speedOfSound - dopplerFactor * vls) / + (speedOfSound - dopplerFactor * vss); + } + if (src.dopplerShift !== oldShift) { + AL.updateSourceRate(src); + } + }, + updateSourceRate: (src) => { + if (src.state === 4114) { + // clear scheduled buffers + AL.cancelPendingSourceAudio(src); + + var audioSrc = src.audioQueue[0]; + if (!audioSrc) { + return; // It is possible that AL.scheduleContextAudio() has not yet fed the next buffer, if so, skip. + } + + var duration; + if (src.type === 4136 && src.looping) { + duration = Number.POSITIVE_INFINITY; + } else { + // audioSrc._duration is expressed after factoring in playbackRate, so when changing playback rate, need + // to recompute/rescale the rate to the new playback speed. + duration = + (audioSrc.buffer.duration - audioSrc._startOffset) / + src.playbackRate; + } + + audioSrc._duration = duration; + audioSrc.playbackRate.value = src.playbackRate; + + // reschedule buffers with the new playbackRate + AL.scheduleSourceAudio(src); + } + }, + sourceDuration: (src) => { + var length = 0.0; + for (var i = 0; i < src.bufQueue.length; i++) { + var audioBuf = src.bufQueue[i].audioBuf; + length += audioBuf ? audioBuf.duration : 0.0; + } + return length; + }, + sourceTell: (src) => { + AL.updateSourceTime(src); + + var offset = 0.0; + for (var i = 0; i < src.bufsProcessed; i++) { + if (src.bufQueue[i].audioBuf) { + offset += src.bufQueue[i].audioBuf.duration; + } + } + offset += src.bufOffset; + + return offset; + }, + sourceSeek: (src, offset) => { + var playing = src.state == 4114; + if (playing) { + AL.setSourceState(src, 4113); + } + + if (src.bufQueue[src.bufsProcessed].audioBuf !== null) { + src.bufsProcessed = 0; + while ( + offset > src.bufQueue[src.bufsProcessed].audioBuf.duration + ) { + offset -= src.bufQueue[src.bufsProcessed].audioBuf.duration; + src.bufsProcessed++; + } + + src.bufOffset = offset; + } + + if (playing) { + AL.setSourceState(src, 4114); + } + }, + getGlobalParam: (funcname, param) => { + if (!AL.currentCtx) { + return null; + } + + switch (param) { + case 49152: + return AL.currentCtx.dopplerFactor; + case 49155: + return AL.currentCtx.speedOfSound; + case 53248: + return AL.currentCtx.distanceModel; + default: + AL.currentCtx.err = 40962; + return null; + } + }, + setGlobalParam: (funcname, param, value) => { + if (!AL.currentCtx) { + return; + } + + switch (param) { + case 49152: + if (!Number.isFinite(value) || value < 0.0) { + // Strictly negative values are disallowed + AL.currentCtx.err = 40963; + return; + } + + AL.currentCtx.dopplerFactor = value; + AL.updateListenerSpace(AL.currentCtx); + break; + case 49155: + if (!Number.isFinite(value) || value <= 0.0) { + // Negative or zero values are disallowed + AL.currentCtx.err = 40963; + return; + } + + AL.currentCtx.speedOfSound = value; + AL.updateListenerSpace(AL.currentCtx); + break; + case 53248: + switch (value) { + case 0: + case 0xd001 /* AL_INVERSE_DISTANCE */: + case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */: + case 0xd003 /* AL_LINEAR_DISTANCE */: + case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */: + case 0xd005 /* AL_EXPONENT_DISTANCE */: + case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */: + AL.currentCtx.distanceModel = value; + AL.updateContextGlobal(AL.currentCtx); + break; + default: + AL.currentCtx.err = 40963; + return; + } + break; + default: + AL.currentCtx.err = 40962; + return; + } + }, + getListenerParam: (funcname, param) => { + if (!AL.currentCtx) { + return null; + } + + switch (param) { + case 4100: + return AL.currentCtx.listener.position; + case 4102: + return AL.currentCtx.listener.velocity; + case 4111: + return AL.currentCtx.listener.direction.concat( + AL.currentCtx.listener.up + ); + case 4106: + return AL.currentCtx.gain.gain.value; + default: + AL.currentCtx.err = 40962; + return null; + } + }, + setListenerParam: (funcname, param, value) => { + if (!AL.currentCtx) { + return; + } + if (value === null) { + AL.currentCtx.err = 40962; + return; + } + + var listener = AL.currentCtx.listener; + switch (param) { + case 4100: + if ( + !Number.isFinite(value[0]) || + !Number.isFinite(value[1]) || + !Number.isFinite(value[2]) + ) { + AL.currentCtx.err = 40963; + return; + } + + listener.position[0] = value[0]; + listener.position[1] = value[1]; + listener.position[2] = value[2]; + AL.updateListenerSpace(AL.currentCtx); + break; + case 4102: + if ( + !Number.isFinite(value[0]) || + !Number.isFinite(value[1]) || + !Number.isFinite(value[2]) + ) { + AL.currentCtx.err = 40963; + return; + } + + listener.velocity[0] = value[0]; + listener.velocity[1] = value[1]; + listener.velocity[2] = value[2]; + AL.updateListenerSpace(AL.currentCtx); + break; + case 4106: + if (!Number.isFinite(value) || value < 0.0) { + AL.currentCtx.err = 40963; + return; + } + + AL.currentCtx.gain.gain.value = value; + break; + case 4111: + if ( + !Number.isFinite(value[0]) || + !Number.isFinite(value[1]) || + !Number.isFinite(value[2]) || + !Number.isFinite(value[3]) || + !Number.isFinite(value[4]) || + !Number.isFinite(value[5]) + ) { + AL.currentCtx.err = 40963; + return; + } + + listener.direction[0] = value[0]; + listener.direction[1] = value[1]; + listener.direction[2] = value[2]; + listener.up[0] = value[3]; + listener.up[1] = value[4]; + listener.up[2] = value[5]; + AL.updateListenerSpace(AL.currentCtx); + break; + default: + AL.currentCtx.err = 40962; + return; + } + }, + getBufferParam: (funcname, bufferId, param) => { + if (!AL.currentCtx) { + return; + } + var buf = AL.buffers[bufferId]; + if (!buf || bufferId === 0) { + AL.currentCtx.err = 40961; + return; + } + + switch (param) { + case 0x2001 /* AL_FREQUENCY */: + return buf.frequency; + case 0x2002 /* AL_BITS */: + return buf.bytesPerSample * 8; + case 0x2003 /* AL_CHANNELS */: + return buf.channels; + case 0x2004 /* AL_SIZE */: + return buf.length * buf.bytesPerSample * buf.channels; + case 0x2015 /* AL_LOOP_POINTS_SOFT */: + if (buf.length === 0) { + return [0, 0]; + } + return [ + (buf.audioBuf._loopStart || 0.0) * buf.frequency, + (buf.audioBuf._loopEnd || buf.length) * buf.frequency, + ]; + default: + AL.currentCtx.err = 40962; + return null; + } + }, + setBufferParam: (funcname, bufferId, param, value) => { + if (!AL.currentCtx) { + return; + } + var buf = AL.buffers[bufferId]; + if (!buf || bufferId === 0) { + AL.currentCtx.err = 40961; + return; + } + if (value === null) { + AL.currentCtx.err = 40962; + return; + } + + switch (param) { + case 0x2004 /* AL_SIZE */: + if (value !== 0) { + AL.currentCtx.err = 40963; + return; + } + + // Per the spec, setting AL_SIZE to 0 is a legal NOP. + break; + case 0x2015 /* AL_LOOP_POINTS_SOFT */: + if ( + value[0] < 0 || + value[0] > buf.length || + value[1] < 0 || + value[1] > buf.Length || + value[0] >= value[1] + ) { + AL.currentCtx.err = 40963; + return; + } + if (buf.refCount > 0) { + AL.currentCtx.err = 40964; + return; + } + + if (buf.audioBuf) { + buf.audioBuf._loopStart = value[0] / buf.frequency; + buf.audioBuf._loopEnd = value[1] / buf.frequency; + } + break; + default: + AL.currentCtx.err = 40962; + return; + } + }, + getSourceParam: (funcname, sourceId, param) => { + if (!AL.currentCtx) { + return null; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { + AL.currentCtx.err = 40961; + return null; + } + + switch (param) { + case 0x202 /* AL_SOURCE_RELATIVE */: + return src.relative; + case 0x1001 /* AL_CONE_INNER_ANGLE */: + return src.coneInnerAngle; + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + return src.coneOuterAngle; + case 0x1003 /* AL_PITCH */: + return src.pitch; + case 4100: + return src.position; + case 4101: + return src.direction; + case 4102: + return src.velocity; + case 0x1007 /* AL_LOOPING */: + return src.looping; + case 0x1009 /* AL_BUFFER */: + if (src.type === 4136) { + return src.bufQueue[0].id; + } + return 0; + case 4106: + return src.gain.gain.value; + case 0x100d /* AL_MIN_GAIN */: + return src.minGain; + case 0x100e /* AL_MAX_GAIN */: + return src.maxGain; + case 0x1010 /* AL_SOURCE_STATE */: + return src.state; + case 0x1015 /* AL_BUFFERS_QUEUED */: + if (src.bufQueue.length === 1 && src.bufQueue[0].id === 0) { + return 0; + } + return src.bufQueue.length; + case 0x1016 /* AL_BUFFERS_PROCESSED */: + if ( + (src.bufQueue.length === 1 && + src.bufQueue[0].id === 0) || + src.looping + ) { + return 0; + } + return src.bufsProcessed; + case 0x1020 /* AL_REFERENCE_DISTANCE */: + return src.refDistance; + case 0x1021 /* AL_ROLLOFF_FACTOR */: + return src.rolloffFactor; + case 0x1022 /* AL_CONE_OUTER_GAIN */: + return src.coneOuterGain; + case 0x1023 /* AL_MAX_DISTANCE */: + return src.maxDistance; + case 0x1024 /* AL_SEC_OFFSET */: + return AL.sourceTell(src); + case 0x1025 /* AL_SAMPLE_OFFSET */: + var offset = AL.sourceTell(src); + if (offset > 0.0) { + offset *= src.bufQueue[0].frequency; + } + return offset; + case 0x1026 /* AL_BYTE_OFFSET */: + var offset = AL.sourceTell(src); + if (offset > 0.0) { + offset *= + src.bufQueue[0].frequency * + src.bufQueue[0].bytesPerSample; + } + return offset; + case 0x1027 /* AL_SOURCE_TYPE */: + return src.type; + case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: + return src.spatialize; + case 0x2009 /* AL_BYTE_LENGTH_SOFT */: + var length = 0; + var bytesPerFrame = 0; + for (var i = 0; i < src.bufQueue.length; i++) { + length += src.bufQueue[i].length; + if (src.bufQueue[i].id !== 0) { + bytesPerFrame = + src.bufQueue[i].bytesPerSample * + src.bufQueue[i].channels; + } + } + return length * bytesPerFrame; + case 0x200a /* AL_SAMPLE_LENGTH_SOFT */: + var length = 0; + for (var i = 0; i < src.bufQueue.length; i++) { + length += src.bufQueue[i].length; + } + return length; + case 0x200b /* AL_SEC_LENGTH_SOFT */: + return AL.sourceDuration(src); + case 53248: + return src.distanceModel; + default: + AL.currentCtx.err = 40962; + return null; + } + }, + setSourceParam: (funcname, sourceId, param, value) => { + if (!AL.currentCtx) { + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { + AL.currentCtx.err = 40961; + return; + } + if (value === null) { + AL.currentCtx.err = 40962; + return; + } + + switch (param) { + case 0x202 /* AL_SOURCE_RELATIVE */: + if (value === 1) { + src.relative = true; + AL.updateSourceSpace(src); + } else if (value === 0) { + src.relative = false; + AL.updateSourceSpace(src); + } else { + AL.currentCtx.err = 40963; + return; + } + break; + case 0x1001 /* AL_CONE_INNER_ANGLE */: + if (!Number.isFinite(value)) { + AL.currentCtx.err = 40963; + return; + } + + src.coneInnerAngle = value; + if (src.panner) { + src.panner.coneInnerAngle = value % 360.0; + } + break; + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + if (!Number.isFinite(value)) { + AL.currentCtx.err = 40963; + return; + } + + src.coneOuterAngle = value; + if (src.panner) { + src.panner.coneOuterAngle = value % 360.0; + } + break; + case 0x1003 /* AL_PITCH */: + if (!Number.isFinite(value) || value <= 0.0) { + AL.currentCtx.err = 40963; + return; + } + + if (src.pitch === value) { + break; + } + + src.pitch = value; + AL.updateSourceRate(src); + break; + case 4100: + if ( + !Number.isFinite(value[0]) || + !Number.isFinite(value[1]) || + !Number.isFinite(value[2]) + ) { + AL.currentCtx.err = 40963; + return; + } + + src.position[0] = value[0]; + src.position[1] = value[1]; + src.position[2] = value[2]; + AL.updateSourceSpace(src); + break; + case 4101: + if ( + !Number.isFinite(value[0]) || + !Number.isFinite(value[1]) || + !Number.isFinite(value[2]) + ) { + AL.currentCtx.err = 40963; + return; + } + + src.direction[0] = value[0]; + src.direction[1] = value[1]; + src.direction[2] = value[2]; + AL.updateSourceSpace(src); + break; + case 4102: + if ( + !Number.isFinite(value[0]) || + !Number.isFinite(value[1]) || + !Number.isFinite(value[2]) + ) { + AL.currentCtx.err = 40963; + return; + } + + src.velocity[0] = value[0]; + src.velocity[1] = value[1]; + src.velocity[2] = value[2]; + AL.updateSourceSpace(src); + break; + case 0x1007 /* AL_LOOPING */: + if (value === 1) { + src.looping = true; + AL.updateSourceTime(src); + if (src.type === 4136 && src.audioQueue.length > 0) { + var audioSrc = src.audioQueue[0]; + audioSrc.loop = true; + audioSrc._duration = Number.POSITIVE_INFINITY; + } + } else if (value === 0) { + src.looping = false; + var currentTime = AL.updateSourceTime(src); + if (src.type === 4136 && src.audioQueue.length > 0) { + var audioSrc = src.audioQueue[0]; + audioSrc.loop = false; + audioSrc._duration = + src.bufQueue[0].audioBuf.duration / + src.playbackRate; + audioSrc._startTime = + currentTime - src.bufOffset / src.playbackRate; + } + } else { + AL.currentCtx.err = 40963; + return; + } + break; + case 0x1009 /* AL_BUFFER */: + if (src.state === 4114 || src.state === 4115) { + AL.currentCtx.err = 40964; + return; + } + + if (value === 0) { + for (var i in src.bufQueue) { + src.bufQueue[i].refCount--; + } + src.bufQueue.length = 1; + src.bufQueue[0] = AL.buffers[0]; + + src.bufsProcessed = 0; + src.type = 0x1030 /* AL_UNDETERMINED */; + } else { + var buf = AL.buffers[value]; + if (!buf) { + AL.currentCtx.err = 40963; + return; + } + + for (var i in src.bufQueue) { + src.bufQueue[i].refCount--; + } + src.bufQueue.length = 0; + + buf.refCount++; + src.bufQueue = [buf]; + src.bufsProcessed = 0; + src.type = 4136; + } + + AL.initSourcePanner(src); + AL.scheduleSourceAudio(src); + break; + case 4106: + if (!Number.isFinite(value) || value < 0.0) { + AL.currentCtx.err = 40963; + return; + } + src.gain.gain.value = value; + break; + case 0x100d /* AL_MIN_GAIN */: + if ( + !Number.isFinite(value) || + value < 0.0 || + value > Math.min(src.maxGain, 1.0) + ) { + AL.currentCtx.err = 40963; + return; + } + src.minGain = value; + break; + case 0x100e /* AL_MAX_GAIN */: + if ( + !Number.isFinite(value) || + value < Math.max(0.0, src.minGain) || + value > 1.0 + ) { + AL.currentCtx.err = 40963; + return; + } + src.maxGain = value; + break; + case 0x1020 /* AL_REFERENCE_DISTANCE */: + if (!Number.isFinite(value) || value < 0.0) { + AL.currentCtx.err = 40963; + return; + } + src.refDistance = value; + if (src.panner) { + src.panner.refDistance = value; + } + break; + case 0x1021 /* AL_ROLLOFF_FACTOR */: + if (!Number.isFinite(value) || value < 0.0) { + AL.currentCtx.err = 40963; + return; + } + src.rolloffFactor = value; + if (src.panner) { + src.panner.rolloffFactor = value; + } + break; + case 0x1022 /* AL_CONE_OUTER_GAIN */: + if (!Number.isFinite(value) || value < 0.0 || value > 1.0) { + AL.currentCtx.err = 40963; + return; + } + src.coneOuterGain = value; + if (src.panner) { + src.panner.coneOuterGain = value; + } + break; + case 0x1023 /* AL_MAX_DISTANCE */: + if (!Number.isFinite(value) || value < 0.0) { + AL.currentCtx.err = 40963; + return; + } + src.maxDistance = value; + if (src.panner) { + src.panner.maxDistance = value; + } + break; + case 0x1024 /* AL_SEC_OFFSET */: + if (value < 0.0 || value > AL.sourceDuration(src)) { + AL.currentCtx.err = 40963; + return; + } + + AL.sourceSeek(src, value); + break; + case 0x1025 /* AL_SAMPLE_OFFSET */: + var srcLen = AL.sourceDuration(src); + if (srcLen > 0.0) { + var frequency; + for (var bufId in src.bufQueue) { + if (bufId) { + frequency = src.bufQueue[bufId].frequency; + break; + } + } + value /= frequency; + } + if (value < 0.0 || value > srcLen) { + AL.currentCtx.err = 40963; + return; + } + + AL.sourceSeek(src, value); + break; + case 0x1026 /* AL_BYTE_OFFSET */: + var srcLen = AL.sourceDuration(src); + if (srcLen > 0.0) { + var bytesPerSec; + for (var bufId in src.bufQueue) { + if (bufId) { + var buf = src.bufQueue[bufId]; + bytesPerSec = + buf.frequency * + buf.bytesPerSample * + buf.channels; + break; + } + } + value /= bytesPerSec; + } + if (value < 0.0 || value > srcLen) { + AL.currentCtx.err = 40963; + return; + } + + AL.sourceSeek(src, value); + break; + case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: + if ( + value !== 0 && + value !== 1 && + value !== 2 /* AL_AUTO_SOFT */ + ) { + AL.currentCtx.err = 40963; + return; + } + + src.spatialize = value; + AL.initSourcePanner(src); + break; + case 0x2009 /* AL_BYTE_LENGTH_SOFT */: + case 0x200a /* AL_SAMPLE_LENGTH_SOFT */: + case 0x200b /* AL_SEC_LENGTH_SOFT */: + AL.currentCtx.err = 40964; + break; + case 53248: + switch (value) { + case 0: + case 0xd001 /* AL_INVERSE_DISTANCE */: + case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */: + case 0xd003 /* AL_LINEAR_DISTANCE */: + case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */: + case 0xd005 /* AL_EXPONENT_DISTANCE */: + case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */: + src.distanceModel = value; + if (AL.currentCtx.sourceDistanceModel) { + AL.updateContextGlobal(AL.currentCtx); + } + break; + default: + AL.currentCtx.err = 40963; + return; + } + break; + default: + AL.currentCtx.err = 40962; + return; + } + }, + captures: {}, + sharedCaptureAudioCtx: null, + requireValidCaptureDevice: (deviceId, funcname) => { + if (deviceId === 0) { + AL.alcErr = 40961; + return null; + } + var c = AL.captures[deviceId]; + if (!c) { + AL.alcErr = 40961; + return null; + } + var err = c.mediaStreamError; + if (err) { + AL.alcErr = 40961; + return null; + } + return c; + }, + }; + var _alBuffer3f = (bufferId, param, value0, value1, value2) => { + AL.setBufferParam('alBuffer3f', bufferId, param, null); + }; + _alBuffer3f.sig = 'viifff'; + + var _alBuffer3i = (bufferId, param, value0, value1, value2) => { + AL.setBufferParam('alBuffer3i', bufferId, param, null); + }; + _alBuffer3i.sig = 'viiiii'; + + var _alBufferData = (bufferId, format, pData, size, freq) => { + if (!AL.currentCtx) { + return; + } + var buf = AL.buffers[bufferId]; + if (!buf) { + AL.currentCtx.err = 40963; + return; + } + if (freq <= 0) { + AL.currentCtx.err = 40963; + return; + } + + var audioBuf = null; + try { + switch (format) { + case 0x1100 /* AL_FORMAT_MONO8 */: + if (size > 0) { + audioBuf = AL.currentCtx.audioCtx.createBuffer( + 1, + size, + freq + ); + var channel0 = audioBuf.getChannelData(0); + for (var i = 0; i < size; ++i) { + channel0[i] = + HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0; + } + } + buf.bytesPerSample = 1; + buf.channels = 1; + buf.length = size; + break; + case 0x1101 /* AL_FORMAT_MONO16 */: + if (size > 0) { + audioBuf = AL.currentCtx.audioCtx.createBuffer( + 1, + size >> 1, + freq + ); + var channel0 = audioBuf.getChannelData(0); + pData >>= 1; + for (var i = 0; i < size >> 1; ++i) { + channel0[i] = + HEAP16[pData++] * + 0.000030517578125 /* 1/32768 */; + } + } + buf.bytesPerSample = 2; + buf.channels = 1; + buf.length = size >> 1; + break; + case 0x1102 /* AL_FORMAT_STEREO8 */: + if (size > 0) { + audioBuf = AL.currentCtx.audioCtx.createBuffer( + 2, + size >> 1, + freq + ); + var channel0 = audioBuf.getChannelData(0); + var channel1 = audioBuf.getChannelData(1); + for (var i = 0; i < size >> 1; ++i) { + channel0[i] = + HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0; + channel1[i] = + HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0; + } + } + buf.bytesPerSample = 1; + buf.channels = 2; + buf.length = size >> 1; + break; + case 0x1103 /* AL_FORMAT_STEREO16 */: + if (size > 0) { + audioBuf = AL.currentCtx.audioCtx.createBuffer( + 2, + size >> 2, + freq + ); + var channel0 = audioBuf.getChannelData(0); + var channel1 = audioBuf.getChannelData(1); + pData >>= 1; + for (var i = 0; i < size >> 2; ++i) { + channel0[i] = + HEAP16[pData++] * + 0.000030517578125 /* 1/32768 */; + channel1[i] = + HEAP16[pData++] * + 0.000030517578125 /* 1/32768 */; + } + } + buf.bytesPerSample = 2; + buf.channels = 2; + buf.length = size >> 2; + break; + case 0x10010 /* AL_FORMAT_MONO_FLOAT32 */: + if (size > 0) { + audioBuf = AL.currentCtx.audioCtx.createBuffer( + 1, + size >> 2, + freq + ); + var channel0 = audioBuf.getChannelData(0); + pData >>= 2; + for (var i = 0; i < size >> 2; ++i) { + channel0[i] = HEAPF32[pData++]; + } + } + buf.bytesPerSample = 4; + buf.channels = 1; + buf.length = size >> 2; + break; + case 0x10011 /* AL_FORMAT_STEREO_FLOAT32 */: + if (size > 0) { + audioBuf = AL.currentCtx.audioCtx.createBuffer( + 2, + size >> 3, + freq + ); + var channel0 = audioBuf.getChannelData(0); + var channel1 = audioBuf.getChannelData(1); + pData >>= 2; + for (var i = 0; i < size >> 3; ++i) { + channel0[i] = HEAPF32[pData++]; + channel1[i] = HEAPF32[pData++]; + } + } + buf.bytesPerSample = 4; + buf.channels = 2; + buf.length = size >> 3; + break; + default: + AL.currentCtx.err = 40963; + return; + } + buf.frequency = freq; + buf.audioBuf = audioBuf; + } catch (e) { + AL.currentCtx.err = 40963; + return; + } + }; + _alBufferData.sig = 'viipii'; + + var _alBufferf = (bufferId, param, value) => { + AL.setBufferParam('alBufferf', bufferId, param, null); + }; + _alBufferf.sig = 'viif'; + + var _alBufferfv = (bufferId, param, pValues) => { + if (!AL.currentCtx) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + AL.setBufferParam('alBufferfv', bufferId, param, null); + }; + _alBufferfv.sig = 'viip'; + + var _alBufferi = (bufferId, param, value) => { + AL.setBufferParam('alBufferi', bufferId, param, null); + }; + _alBufferi.sig = 'viii'; + + var _alBufferiv = (bufferId, param, pValues) => { + if (!AL.currentCtx) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x2015 /* AL_LOOP_POINTS_SOFT */: + AL.paramArray[0] = HEAP32[pValues >> 2]; + AL.paramArray[1] = HEAP32[(pValues + 4) >> 2]; + AL.setBufferParam('alBufferiv', bufferId, param, AL.paramArray); + break; + default: + AL.setBufferParam('alBufferiv', bufferId, param, null); + break; + } + }; + _alBufferiv.sig = 'viip'; + + var _alDeleteBuffers = (count, pBufferIds) => { + if (!AL.currentCtx) { + return; + } + + for (var i = 0; i < count; ++i) { + var bufId = HEAP32[(pBufferIds + i * 4) >> 2]; + /// Deleting the zero buffer is a legal NOP, so ignore it + if (bufId === 0) { + continue; + } + + // Make sure the buffer index is valid. + if (!AL.buffers[bufId]) { + AL.currentCtx.err = 40961; + return; + } + + // Make sure the buffer is no longer in use. + if (AL.buffers[bufId].refCount) { + AL.currentCtx.err = 40964; + return; + } + } + + for (var i = 0; i < count; ++i) { + var bufId = HEAP32[(pBufferIds + i * 4) >> 2]; + if (bufId === 0) { + continue; + } + + AL.deviceRefCounts[AL.buffers[bufId].deviceId]--; + delete AL.buffers[bufId]; + AL.freeIds.push(bufId); + } + }; + _alDeleteBuffers.sig = 'vip'; + + var _alSourcei = (sourceId, param, value) => { + switch (param) { + case 0x202 /* AL_SOURCE_RELATIVE */: + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1007 /* AL_LOOPING */: + case 0x1009 /* AL_BUFFER */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: + case 0x2009 /* AL_BYTE_LENGTH_SOFT */: + case 0x200a /* AL_SAMPLE_LENGTH_SOFT */: + case 53248: + AL.setSourceParam('alSourcei', sourceId, param, value); + break; + default: + AL.setSourceParam('alSourcei', sourceId, param, null); + break; + } + }; + _alSourcei.sig = 'viii'; + + var _alDeleteSources = (count, pSourceIds) => { + if (!AL.currentCtx) { + return; + } + + for (var i = 0; i < count; ++i) { + var srcId = HEAP32[(pSourceIds + i * 4) >> 2]; + if (!AL.currentCtx.sources[srcId]) { + AL.currentCtx.err = 40961; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = HEAP32[(pSourceIds + i * 4) >> 2]; + AL.setSourceState(AL.currentCtx.sources[srcId], 4116); + _alSourcei(srcId, 0x1009 /* AL_BUFFER */, 0); + delete AL.currentCtx.sources[srcId]; + AL.freeIds.push(srcId); + } + }; + _alDeleteSources.sig = 'vip'; + + var _alDisable = (param) => { + if (!AL.currentCtx) { + return; + } + switch (param) { + case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: + AL.currentCtx.sourceDistanceModel = false; + AL.updateContextGlobal(AL.currentCtx); + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alDisable.sig = 'vi'; + + var _alDistanceModel = (model) => { + AL.setGlobalParam('alDistanceModel', 53248, model); + }; + _alDistanceModel.sig = 'vi'; + + var _alDopplerFactor = (value) => { + AL.setGlobalParam('alDopplerFactor', 49152, value); + }; + _alDopplerFactor.sig = 'vf'; + + var _alDopplerVelocity = (value) => { + warnOnce( + 'alDopplerVelocity() is deprecated, and only kept for compatibility with OpenAL 1.0. Use alSpeedOfSound() instead.' + ); + if (!AL.currentCtx) { + return; + } + if (value <= 0) { + // Negative or zero values are disallowed + AL.currentCtx.err = 40963; + return; + } + }; + _alDopplerVelocity.sig = 'vf'; + + var _alEnable = (param) => { + if (!AL.currentCtx) { + return; + } + switch (param) { + case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: + AL.currentCtx.sourceDistanceModel = true; + AL.updateContextGlobal(AL.currentCtx); + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alEnable.sig = 'vi'; + + var _alGenBuffers = (count, pBufferIds) => { + if (!AL.currentCtx) { + return; + } + + for (var i = 0; i < count; ++i) { + var buf = { + deviceId: AL.currentCtx.deviceId, + id: AL.newId(), + refCount: 0, + audioBuf: null, + frequency: 0, + bytesPerSample: 2, + channels: 1, + length: 0, + }; + AL.deviceRefCounts[buf.deviceId]++; + AL.buffers[buf.id] = buf; + HEAP32[(pBufferIds + i * 4) >> 2] = buf.id; + } + }; + _alGenBuffers.sig = 'vip'; + + var _alGenSources = (count, pSourceIds) => { + if (!AL.currentCtx) { + return; + } + for (var i = 0; i < count; ++i) { + var gain = AL.currentCtx.audioCtx.createGain(); + gain.connect(AL.currentCtx.gain); + var src = { + context: AL.currentCtx, + id: AL.newId(), + type: 0x1030 /* AL_UNDETERMINED */, + state: 4113, + bufQueue: [AL.buffers[0]], + audioQueue: [], + looping: false, + pitch: 1.0, + dopplerShift: 1.0, + gain, + minGain: 0.0, + maxGain: 1.0, + panner: null, + bufsProcessed: 0, + bufStartTime: Number.NEGATIVE_INFINITY, + bufOffset: 0.0, + relative: false, + refDistance: 1.0, + maxDistance: 3.40282e38 /* FLT_MAX */, + rolloffFactor: 1.0, + position: [0.0, 0.0, 0.0], + velocity: [0.0, 0.0, 0.0], + direction: [0.0, 0.0, 0.0], + coneOuterGain: 0.0, + coneInnerAngle: 360.0, + coneOuterAngle: 360.0, + distanceModel: 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */, + spatialize: 2 /* AL_AUTO_SOFT */, + + get playbackRate() { + return this.pitch * this.dopplerShift; + }, + }; + AL.currentCtx.sources[src.id] = src; + HEAP32[(pSourceIds + i * 4) >> 2] = src.id; + } + }; + _alGenSources.sig = 'vip'; + + var _alGetBoolean = (param) => { + var val = AL.getGlobalParam('alGetBoolean', param); + if (val === null) { + return 0; + } + + switch (param) { + case 49152: + case 49155: + case 53248: + return val !== 0 ? 1 : 0; + default: + AL.currentCtx.err = 40962; + return 0; + } + }; + _alGetBoolean.sig = 'ii'; + + var _alGetBooleanv = (param, pValues) => { + var val = AL.getGlobalParam('alGetBooleanv', param); + // Silently ignore null destinations, as per the spec for global state functions + if (val === null || !pValues) { + return; + } + + switch (param) { + case 49152: + case 49155: + case 53248: + HEAP8[pValues] = val; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetBooleanv.sig = 'vip'; + + var _alGetBuffer3f = (bufferId, param, pValue0, pValue1, pValue2) => { + var val = AL.getBufferParam('alGetBuffer3f', bufferId, param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { + AL.currentCtx.err = 40963; + return; + } + + AL.currentCtx.err = 40962; + }; + _alGetBuffer3f.sig = 'viippp'; + + var _alGetBuffer3i = (bufferId, param, pValue0, pValue1, pValue2) => { + var val = AL.getBufferParam('alGetBuffer3i', bufferId, param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { + AL.currentCtx.err = 40963; + return; + } + + AL.currentCtx.err = 40962; + }; + _alGetBuffer3i.sig = 'viippp'; + + var _alGetBufferf = (bufferId, param, pValue) => { + var val = AL.getBufferParam('alGetBufferf', bufferId, param); + if (val === null) { + return; + } + if (!pValue) { + AL.currentCtx.err = 40963; + return; + } + + AL.currentCtx.err = 40962; + }; + _alGetBufferf.sig = 'viip'; + + var _alGetBufferfv = (bufferId, param, pValues) => { + var val = AL.getBufferParam('alGetBufferfv', bufferId, param); + if (val === null) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + AL.currentCtx.err = 40962; + }; + _alGetBufferfv.sig = 'viip'; + + var _alGetBufferi = (bufferId, param, pValue) => { + var val = AL.getBufferParam('alGetBufferi', bufferId, param); + if (val === null) { + return; + } + if (!pValue) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x2001 /* AL_FREQUENCY */: + case 0x2002 /* AL_BITS */: + case 0x2003 /* AL_CHANNELS */: + case 0x2004 /* AL_SIZE */: + HEAP32[pValue >> 2] = val; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetBufferi.sig = 'viip'; + + var _alGetBufferiv = (bufferId, param, pValues) => { + var val = AL.getBufferParam('alGetBufferiv', bufferId, param); + if (val === null) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x2001 /* AL_FREQUENCY */: + case 0x2002 /* AL_BITS */: + case 0x2003 /* AL_CHANNELS */: + case 0x2004 /* AL_SIZE */: + HEAP32[pValues >> 2] = val; + break; + case 0x2015 /* AL_LOOP_POINTS_SOFT */: + HEAP32[pValues >> 2] = val[0]; + HEAP32[(pValues + 4) >> 2] = val[1]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetBufferiv.sig = 'viip'; + + var _alGetDouble = (param) => { + var val = AL.getGlobalParam('alGetDouble', param); + if (val === null) { + return 0.0; + } + + switch (param) { + case 49152: + case 49155: + case 53248: + return val; + default: + AL.currentCtx.err = 40962; + return 0.0; + } + }; + _alGetDouble.sig = 'di'; + + var _alGetDoublev = (param, pValues) => { + var val = AL.getGlobalParam('alGetDoublev', param); + // Silently ignore null destinations, as per the spec for global state functions + if (val === null || !pValues) { + return; + } + + switch (param) { + case 49152: + case 49155: + case 53248: + HEAPF64[pValues >> 3] = val; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetDoublev.sig = 'vip'; + + var _alGetEnumValue = (pEnumName) => { + if (!AL.currentCtx) { + return 0; + } + + if (!pEnumName) { + AL.currentCtx.err = 40963; + return 0; + } + var name = UTF8ToString(pEnumName); + + switch (name) { + // Spec doesn't clearly state that alGetEnumValue() is required to + // support _only_ extension tokens. + // We should probably follow OpenAL-Soft's example and support all + // of the names we know. + // See http://repo.or.cz/openal-soft.git/blob/HEAD:/Alc/ALc.c + case 'AL_BITS': + return 0x2002; + case 'AL_BUFFER': + return 0x1009; + case 'AL_BUFFERS_PROCESSED': + return 0x1016; + case 'AL_BUFFERS_QUEUED': + return 0x1015; + case 'AL_BYTE_OFFSET': + return 0x1026; + case 'AL_CHANNELS': + return 0x2003; + case 'AL_CONE_INNER_ANGLE': + return 0x1001; + case 'AL_CONE_OUTER_ANGLE': + return 0x1002; + case 'AL_CONE_OUTER_GAIN': + return 0x1022; + case 'AL_DIRECTION': + return 0x1005; + case 'AL_DISTANCE_MODEL': + return 0xd000; + case 'AL_DOPPLER_FACTOR': + return 0xc000; + case 'AL_DOPPLER_VELOCITY': + return 0xc001; + case 'AL_EXPONENT_DISTANCE': + return 0xd005; + case 'AL_EXPONENT_DISTANCE_CLAMPED': + return 0xd006; + case 'AL_EXTENSIONS': + return 0xb004; + case 'AL_FORMAT_MONO16': + return 0x1101; + case 'AL_FORMAT_MONO8': + return 0x1100; + case 'AL_FORMAT_STEREO16': + return 0x1103; + case 'AL_FORMAT_STEREO8': + return 0x1102; + case 'AL_FREQUENCY': + return 0x2001; + case 'AL_GAIN': + return 0x100a; + case 'AL_INITIAL': + return 0x1011; + case 'AL_INVALID': + return -1; + case 'AL_ILLEGAL_ENUM': // fallthrough + case 'AL_INVALID_ENUM': + return 0xa002; + case 'AL_INVALID_NAME': + return 0xa001; + case 'AL_ILLEGAL_COMMAND': // fallthrough + case 'AL_INVALID_OPERATION': + return 0xa004; + case 'AL_INVALID_VALUE': + return 0xa003; + case 'AL_INVERSE_DISTANCE': + return 0xd001; + case 'AL_INVERSE_DISTANCE_CLAMPED': + return 0xd002; + case 'AL_LINEAR_DISTANCE': + return 0xd003; + case 'AL_LINEAR_DISTANCE_CLAMPED': + return 0xd004; + case 'AL_LOOPING': + return 0x1007; + case 'AL_MAX_DISTANCE': + return 0x1023; + case 'AL_MAX_GAIN': + return 0x100e; + case 'AL_MIN_GAIN': + return 0x100d; + case 'AL_NONE': + return 0; + case 'AL_NO_ERROR': + return 0; + case 'AL_ORIENTATION': + return 0x100f; + case 'AL_OUT_OF_MEMORY': + return 0xa005; + case 'AL_PAUSED': + return 0x1013; + case 'AL_PENDING': + return 0x2011; + case 'AL_PITCH': + return 0x1003; + case 'AL_PLAYING': + return 0x1012; + case 'AL_POSITION': + return 0x1004; + case 'AL_PROCESSED': + return 0x2012; + case 'AL_REFERENCE_DISTANCE': + return 0x1020; + case 'AL_RENDERER': + return 0xb003; + case 'AL_ROLLOFF_FACTOR': + return 0x1021; + case 'AL_SAMPLE_OFFSET': + return 0x1025; + case 'AL_SEC_OFFSET': + return 0x1024; + case 'AL_SIZE': + return 0x2004; + case 'AL_SOURCE_RELATIVE': + return 0x202; + case 'AL_SOURCE_STATE': + return 0x1010; + case 'AL_SOURCE_TYPE': + return 0x1027; + case 'AL_SPEED_OF_SOUND': + return 0xc003; + case 'AL_STATIC': + return 0x1028; + case 'AL_STOPPED': + return 0x1014; + case 'AL_STREAMING': + return 0x1029; + case 'AL_UNDETERMINED': + return 0x1030; + case 'AL_UNUSED': + return 0x2010; + case 'AL_VELOCITY': + return 0x1006; + case 'AL_VENDOR': + return 0xb001; + case 'AL_VERSION': + return 0xb002; + + /* Extensions */ + case 'AL_AUTO_SOFT': + return 0x0002; + case 'AL_SOURCE_DISTANCE_MODEL': + return 0x200; + case 'AL_SOURCE_SPATIALIZE_SOFT': + return 0x1214; + case 'AL_LOOP_POINTS_SOFT': + return 0x2015; + case 'AL_BYTE_LENGTH_SOFT': + return 0x2009; + case 'AL_SAMPLE_LENGTH_SOFT': + return 0x200a; + case 'AL_SEC_LENGTH_SOFT': + return 0x200b; + case 'AL_FORMAT_MONO_FLOAT32': + return 0x10010; + case 'AL_FORMAT_STEREO_FLOAT32': + return 0x10011; + + default: + AL.currentCtx.err = 40963; + return 0; + } + }; + _alGetEnumValue.sig = 'ip'; + + var _alGetError = () => { + if (!AL.currentCtx) { + return 40964; + } + // Reset error on get. + var err = AL.currentCtx.err; + AL.currentCtx.err = 0; + return err; + }; + _alGetError.sig = 'i'; + + var _alGetFloat = (param) => { + var val = AL.getGlobalParam('alGetFloat', param); + if (val === null) { + return 0.0; + } + + switch (param) { + case 49152: + case 49155: + case 53248: + return val; + default: + return 0.0; + } + }; + _alGetFloat.sig = 'fi'; + + var _alGetFloatv = (param, pValues) => { + var val = AL.getGlobalParam('alGetFloatv', param); + // Silently ignore null destinations, as per the spec for global state functions + if (val === null || !pValues) { + return; + } + + switch (param) { + case 49152: + case 49155: + case 53248: + HEAPF32[pValues >> 2] = val; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetFloatv.sig = 'vip'; + + var _alGetInteger = (param) => { + var val = AL.getGlobalParam('alGetInteger', param); + if (val === null) { + return 0; + } + + switch (param) { + case 49152: + case 49155: + case 53248: + return val; + default: + AL.currentCtx.err = 40962; + return 0; + } + }; + _alGetInteger.sig = 'ii'; + + var _alGetIntegerv = (param, pValues) => { + var val = AL.getGlobalParam('alGetIntegerv', param); + // Silently ignore null destinations, as per the spec for global state functions + if (val === null || !pValues) { + return; + } + + switch (param) { + case 49152: + case 49155: + case 53248: + HEAP32[pValues >> 2] = val; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetIntegerv.sig = 'vip'; + + var _alGetListener3f = (param, pValue0, pValue1, pValue2) => { + var val = AL.getListenerParam('alGetListener3f', param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4100: + case 4102: + HEAPF32[pValue0 >> 2] = val[0]; + HEAPF32[pValue1 >> 2] = val[1]; + HEAPF32[pValue2 >> 2] = val[2]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetListener3f.sig = 'vippp'; + + var _alGetListener3i = (param, pValue0, pValue1, pValue2) => { + var val = AL.getListenerParam('alGetListener3i', param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4100: + case 4102: + HEAP32[pValue0 >> 2] = val[0]; + HEAP32[pValue1 >> 2] = val[1]; + HEAP32[pValue2 >> 2] = val[2]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetListener3i.sig = 'vippp'; + + var _alGetListenerf = (param, pValue) => { + var val = AL.getListenerParam('alGetListenerf', param); + if (val === null) { + return; + } + if (!pValue) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4106: + HEAPF32[pValue >> 2] = val; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetListenerf.sig = 'vip'; + + var _alGetListenerfv = (param, pValues) => { + var val = AL.getListenerParam('alGetListenerfv', param); + if (val === null) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4100: + case 4102: + HEAPF32[pValues >> 2] = val[0]; + HEAPF32[(pValues + 4) >> 2] = val[1]; + HEAPF32[(pValues + 8) >> 2] = val[2]; + break; + case 4111: + HEAPF32[pValues >> 2] = val[0]; + HEAPF32[(pValues + 4) >> 2] = val[1]; + HEAPF32[(pValues + 8) >> 2] = val[2]; + HEAPF32[(pValues + 12) >> 2] = val[3]; + HEAPF32[(pValues + 16) >> 2] = val[4]; + HEAPF32[(pValues + 20) >> 2] = val[5]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetListenerfv.sig = 'vip'; + + var _alGetListeneri = (param, pValue) => { + var val = AL.getListenerParam('alGetListeneri', param); + if (val === null) { + return; + } + if (!pValue) { + AL.currentCtx.err = 40963; + return; + } + + AL.currentCtx.err = 40962; + }; + _alGetListeneri.sig = 'vip'; + + var _alGetListeneriv = (param, pValues) => { + var val = AL.getListenerParam('alGetListeneriv', param); + if (val === null) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4100: + case 4102: + HEAP32[pValues >> 2] = val[0]; + HEAP32[(pValues + 4) >> 2] = val[1]; + HEAP32[(pValues + 8) >> 2] = val[2]; + break; + case 4111: + HEAP32[pValues >> 2] = val[0]; + HEAP32[(pValues + 4) >> 2] = val[1]; + HEAP32[(pValues + 8) >> 2] = val[2]; + HEAP32[(pValues + 12) >> 2] = val[3]; + HEAP32[(pValues + 16) >> 2] = val[4]; + HEAP32[(pValues + 20) >> 2] = val[5]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetListeneriv.sig = 'vip'; + + var _alGetSource3f = (sourceId, param, pValue0, pValue1, pValue2) => { + var val = AL.getSourceParam('alGetSource3f', sourceId, param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4100: + case 4101: + case 4102: + HEAPF32[pValue0 >> 2] = val[0]; + HEAPF32[pValue1 >> 2] = val[1]; + HEAPF32[pValue2 >> 2] = val[2]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetSource3f.sig = 'viippp'; + + var _alGetSource3i = (sourceId, param, pValue0, pValue1, pValue2) => { + var val = AL.getSourceParam('alGetSource3i', sourceId, param); + if (val === null) { + return; + } + if (!pValue0 || !pValue1 || !pValue2) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4100: + case 4101: + case 4102: + HEAP32[pValue0 >> 2] = val[0]; + HEAP32[pValue1 >> 2] = val[1]; + HEAP32[pValue2 >> 2] = val[2]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetSource3i.sig = 'viippp'; + + var _alGetSourcef = (sourceId, param, pValue) => { + var val = AL.getSourceParam('alGetSourcef', sourceId, param); + if (val === null) { + return; + } + if (!pValue) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1003 /* AL_PITCH */: + case 4106: + case 0x100d /* AL_MIN_GAIN */: + case 0x100e /* AL_MAX_GAIN */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1022 /* AL_CONE_OUTER_GAIN */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + case 0x200b /* AL_SEC_LENGTH_SOFT */: + HEAPF32[pValue >> 2] = val; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetSourcef.sig = 'viip'; + + var _alGetSourcefv = (sourceId, param, pValues) => { + var val = AL.getSourceParam('alGetSourcefv', sourceId, param); + if (val === null) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1003 /* AL_PITCH */: + case 4106: + case 0x100d /* AL_MIN_GAIN */: + case 0x100e /* AL_MAX_GAIN */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1022 /* AL_CONE_OUTER_GAIN */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + case 0x200b /* AL_SEC_LENGTH_SOFT */: + HEAPF32[pValues >> 2] = val[0]; + break; + case 4100: + case 4101: + case 4102: + HEAPF32[pValues >> 2] = val[0]; + HEAPF32[(pValues + 4) >> 2] = val[1]; + HEAPF32[(pValues + 8) >> 2] = val[2]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetSourcefv.sig = 'viip'; + + var _alGetSourcei = (sourceId, param, pValue) => { + var val = AL.getSourceParam('alGetSourcei', sourceId, param); + if (val === null) { + return; + } + if (!pValue) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x202 /* AL_SOURCE_RELATIVE */: + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1007 /* AL_LOOPING */: + case 0x1009 /* AL_BUFFER */: + case 0x1010 /* AL_SOURCE_STATE */: + case 0x1015 /* AL_BUFFERS_QUEUED */: + case 0x1016 /* AL_BUFFERS_PROCESSED */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + case 0x1027 /* AL_SOURCE_TYPE */: + case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: + case 0x2009 /* AL_BYTE_LENGTH_SOFT */: + case 0x200a /* AL_SAMPLE_LENGTH_SOFT */: + case 53248: + HEAP32[pValue >> 2] = val; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetSourcei.sig = 'viip'; + + var _alGetSourceiv = (sourceId, param, pValues) => { + var val = AL.getSourceParam('alGetSourceiv', sourceId, param); + if (val === null) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x202 /* AL_SOURCE_RELATIVE */: + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1007 /* AL_LOOPING */: + case 0x1009 /* AL_BUFFER */: + case 0x1010 /* AL_SOURCE_STATE */: + case 0x1015 /* AL_BUFFERS_QUEUED */: + case 0x1016 /* AL_BUFFERS_PROCESSED */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + case 0x1027 /* AL_SOURCE_TYPE */: + case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: + case 0x2009 /* AL_BYTE_LENGTH_SOFT */: + case 0x200a /* AL_SAMPLE_LENGTH_SOFT */: + case 53248: + HEAP32[pValues >> 2] = val; + break; + case 4100: + case 4101: + case 4102: + HEAP32[pValues >> 2] = val[0]; + HEAP32[(pValues + 4) >> 2] = val[1]; + HEAP32[(pValues + 8) >> 2] = val[2]; + break; + default: + AL.currentCtx.err = 40962; + return; + } + }; + _alGetSourceiv.sig = 'viip'; + + var stringToNewUTF8 = (str) => { + var size = lengthBytesUTF8(str) + 1; + var ret = _malloc(size); + if (ret) stringToUTF8(str, ret, size); + return ret; + }; + + var _alGetString = (param) => { + if (AL.stringCache[param]) { + return AL.stringCache[param]; + } + + var ret; + switch (param) { + case 0: + ret = 'No Error'; + break; + case 40961: + ret = 'Invalid Name'; + break; + case 40962: + ret = 'Invalid Enum'; + break; + case 40963: + ret = 'Invalid Value'; + break; + case 40964: + ret = 'Invalid Operation'; + break; + case 0xa005 /* AL_OUT_OF_MEMORY */: + ret = 'Out of Memory'; + break; + case 0xb001 /* AL_VENDOR */: + ret = 'Emscripten'; + break; + case 0xb002 /* AL_VERSION */: + ret = '1.1'; + break; + case 0xb003 /* AL_RENDERER */: + ret = 'WebAudio'; + break; + case 0xb004 /* AL_EXTENSIONS */: + ret = Object.keys(AL.AL_EXTENSIONS).join(' '); + break; + default: + if (AL.currentCtx) { + AL.currentCtx.err = 40962; + } else { + } + return 0; + } + + ret = stringToNewUTF8(ret); + AL.stringCache[param] = ret; + return ret; + }; + _alGetString.sig = 'pi'; + + var _alIsBuffer = (bufferId) => { + if (!AL.currentCtx) { + return false; + } + if (bufferId > AL.buffers.length) { + return false; + } + + if (!AL.buffers[bufferId]) { + return false; + } + return true; + }; + _alIsBuffer.sig = 'ii'; + + var _alIsEnabled = (param) => { + if (!AL.currentCtx) { + return 0; + } + switch (param) { + case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: + return AL.currentCtx.sourceDistanceModel ? 0 : 1; + default: + AL.currentCtx.err = 40962; + return 0; + } + }; + _alIsEnabled.sig = 'ii'; + + var _alIsExtensionPresent = (pExtName) => { + var name = UTF8ToString(pExtName); + + return AL.AL_EXTENSIONS[name] ? 1 : 0; + }; + _alIsExtensionPresent.sig = 'ip'; + + var _alIsSource = (sourceId) => { + if (!AL.currentCtx) { + return false; + } + + if (!AL.currentCtx.sources[sourceId]) { + return false; + } + return true; + }; + _alIsSource.sig = 'ii'; + + var _alListener3f = (param, value0, value1, value2) => { + switch (param) { + case 4100: + case 4102: + AL.paramArray[0] = value0; + AL.paramArray[1] = value1; + AL.paramArray[2] = value2; + AL.setListenerParam('alListener3f', param, AL.paramArray); + break; + default: + AL.setListenerParam('alListener3f', param, null); + break; + } + }; + _alListener3f.sig = 'vifff'; + + var _alListener3i = (param, value0, value1, value2) => { + switch (param) { + case 4100: + case 4102: + AL.paramArray[0] = value0; + AL.paramArray[1] = value1; + AL.paramArray[2] = value2; + AL.setListenerParam('alListener3i', param, AL.paramArray); + break; + default: + AL.setListenerParam('alListener3i', param, null); + break; + } + }; + _alListener3i.sig = 'viiii'; + + var _alListenerf = (param, value) => { + switch (param) { + case 4106: + AL.setListenerParam('alListenerf', param, value); + break; + default: + AL.setListenerParam('alListenerf', param, null); + break; + } + }; + _alListenerf.sig = 'vif'; + + var _alListenerfv = (param, pValues) => { + if (!AL.currentCtx) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4100: + case 4102: + AL.paramArray[0] = HEAPF32[pValues >> 2]; + AL.paramArray[1] = HEAPF32[(pValues + 4) >> 2]; + AL.paramArray[2] = HEAPF32[(pValues + 8) >> 2]; + AL.setListenerParam('alListenerfv', param, AL.paramArray); + break; + case 4111: + AL.paramArray[0] = HEAPF32[pValues >> 2]; + AL.paramArray[1] = HEAPF32[(pValues + 4) >> 2]; + AL.paramArray[2] = HEAPF32[(pValues + 8) >> 2]; + AL.paramArray[3] = HEAPF32[(pValues + 12) >> 2]; + AL.paramArray[4] = HEAPF32[(pValues + 16) >> 2]; + AL.paramArray[5] = HEAPF32[(pValues + 20) >> 2]; + AL.setListenerParam('alListenerfv', param, AL.paramArray); + break; + default: + AL.setListenerParam('alListenerfv', param, null); + break; + } + }; + _alListenerfv.sig = 'vip'; + + var _alListeneri = (param, value) => { + AL.setListenerParam('alListeneri', param, null); + }; + _alListeneri.sig = 'vii'; + + var _alListeneriv = (param, pValues) => { + if (!AL.currentCtx) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 4100: + case 4102: + AL.paramArray[0] = HEAP32[pValues >> 2]; + AL.paramArray[1] = HEAP32[(pValues + 4) >> 2]; + AL.paramArray[2] = HEAP32[(pValues + 8) >> 2]; + AL.setListenerParam('alListeneriv', param, AL.paramArray); + break; + case 4111: + AL.paramArray[0] = HEAP32[pValues >> 2]; + AL.paramArray[1] = HEAP32[(pValues + 4) >> 2]; + AL.paramArray[2] = HEAP32[(pValues + 8) >> 2]; + AL.paramArray[3] = HEAP32[(pValues + 12) >> 2]; + AL.paramArray[4] = HEAP32[(pValues + 16) >> 2]; + AL.paramArray[5] = HEAP32[(pValues + 20) >> 2]; + AL.setListenerParam('alListeneriv', param, AL.paramArray); + break; + default: + AL.setListenerParam('alListeneriv', param, null); + break; + } + }; + _alListeneriv.sig = 'vip'; + + var _alSource3f = (sourceId, param, value0, value1, value2) => { + switch (param) { + case 4100: + case 4101: + case 4102: + AL.paramArray[0] = value0; + AL.paramArray[1] = value1; + AL.paramArray[2] = value2; + AL.setSourceParam('alSource3f', sourceId, param, AL.paramArray); + break; + default: + AL.setSourceParam('alSource3f', sourceId, param, null); + break; + } + }; + _alSource3f.sig = 'viifff'; + + var _alSource3i = (sourceId, param, value0, value1, value2) => { + switch (param) { + case 4100: + case 4101: + case 4102: + AL.paramArray[0] = value0; + AL.paramArray[1] = value1; + AL.paramArray[2] = value2; + AL.setSourceParam('alSource3i', sourceId, param, AL.paramArray); + break; + default: + AL.setSourceParam('alSource3i', sourceId, param, null); + break; + } + }; + _alSource3i.sig = 'viiiii'; + + var _alSourcePause = (sourceId) => { + if (!AL.currentCtx) { + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { + AL.currentCtx.err = 40961; + return; + } + AL.setSourceState(src, 4115); + }; + _alSourcePause.sig = 'vi'; + + var _alSourcePausev = (count, pSourceIds) => { + if (!AL.currentCtx) { + return; + } + if (!pSourceIds) { + AL.currentCtx.err = 40963; + } + for (var i = 0; i < count; ++i) { + if (!AL.currentCtx.sources[HEAP32[(pSourceIds + i * 4) >> 2]]) { + AL.currentCtx.err = 40961; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = HEAP32[(pSourceIds + i * 4) >> 2]; + AL.setSourceState(AL.currentCtx.sources[srcId], 4115); + } + }; + _alSourcePausev.sig = 'vip'; + + var _alSourcePlay = (sourceId) => { + if (!AL.currentCtx) { + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { + AL.currentCtx.err = 40961; + return; + } + AL.setSourceState(src, 4114); + }; + _alSourcePlay.sig = 'vi'; + + var _alSourcePlayv = (count, pSourceIds) => { + if (!AL.currentCtx) { + return; + } + if (!pSourceIds) { + AL.currentCtx.err = 40963; + } + for (var i = 0; i < count; ++i) { + if (!AL.currentCtx.sources[HEAP32[(pSourceIds + i * 4) >> 2]]) { + AL.currentCtx.err = 40961; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = HEAP32[(pSourceIds + i * 4) >> 2]; + AL.setSourceState(AL.currentCtx.sources[srcId], 4114); + } + }; + _alSourcePlayv.sig = 'vip'; + + var _alSourceQueueBuffers = (sourceId, count, pBufferIds) => { + if (!AL.currentCtx) { + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { + AL.currentCtx.err = 40961; + return; + } + if (src.type === 4136) { + AL.currentCtx.err = 40964; + return; + } + + if (count === 0) { + return; + } + + // Find the first non-zero buffer in the queue to determine the proper format + var templateBuf = AL.buffers[0]; + for (var buf of src.bufQueue) { + if (buf.id !== 0) { + templateBuf = buf; + break; + } + } + + for (var i = 0; i < count; ++i) { + var bufId = HEAP32[(pBufferIds + i * 4) >> 2]; + var buf = AL.buffers[bufId]; + if (!buf) { + AL.currentCtx.err = 40961; + return; + } + + // Check that the added buffer has the correct format. If the template is the zero buffer, any format is valid. + if ( + templateBuf.id !== 0 && + (buf.frequency !== templateBuf.frequency || + buf.bytesPerSample !== templateBuf.bytesPerSample || + buf.channels !== templateBuf.channels) + ) { + AL.currentCtx.err = 40964; + } + } + + // If the only buffer in the queue is the zero buffer, clear the queue before we add anything. + if (src.bufQueue.length === 1 && src.bufQueue[0].id === 0) { + src.bufQueue.length = 0; + } + + src.type = 0x1029 /* AL_STREAMING */; + for (var i = 0; i < count; ++i) { + var bufId = HEAP32[(pBufferIds + i * 4) >> 2]; + var buf = AL.buffers[bufId]; + buf.refCount++; + src.bufQueue.push(buf); + } + + // if the source is looping, cancel the schedule so we can reschedule the loop order + if (src.looping) { + AL.cancelPendingSourceAudio(src); + } + + AL.initSourcePanner(src); + AL.scheduleSourceAudio(src); + }; + _alSourceQueueBuffers.sig = 'viip'; + + var _alSourceRewind = (sourceId) => { + if (!AL.currentCtx) { + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { + AL.currentCtx.err = 40961; + return; + } + // Stop the source first to clear the source queue + AL.setSourceState(src, 4116); + // Now set the state of AL_INITIAL according to the specification + AL.setSourceState(src, 4113); + }; + _alSourceRewind.sig = 'vi'; + + var _alSourceRewindv = (count, pSourceIds) => { + if (!AL.currentCtx) { + return; + } + if (!pSourceIds) { + AL.currentCtx.err = 40963; + } + for (var i = 0; i < count; ++i) { + if (!AL.currentCtx.sources[HEAP32[(pSourceIds + i * 4) >> 2]]) { + AL.currentCtx.err = 40961; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = HEAP32[(pSourceIds + i * 4) >> 2]; + AL.setSourceState(AL.currentCtx.sources[srcId], 4113); + } + }; + _alSourceRewindv.sig = 'vip'; + + var _alSourceStop = (sourceId) => { + if (!AL.currentCtx) { + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { + AL.currentCtx.err = 40961; + return; + } + AL.setSourceState(src, 4116); + }; + _alSourceStop.sig = 'vi'; + + var _alSourceStopv = (count, pSourceIds) => { + if (!AL.currentCtx) { + return; + } + if (!pSourceIds) { + AL.currentCtx.err = 40963; + } + for (var i = 0; i < count; ++i) { + if (!AL.currentCtx.sources[HEAP32[(pSourceIds + i * 4) >> 2]]) { + AL.currentCtx.err = 40961; + return; + } + } + + for (var i = 0; i < count; ++i) { + var srcId = HEAP32[(pSourceIds + i * 4) >> 2]; + AL.setSourceState(AL.currentCtx.sources[srcId], 4116); + } + }; + _alSourceStopv.sig = 'vip'; + + var _alSourceUnqueueBuffers = (sourceId, count, pBufferIds) => { + if (!AL.currentCtx) { + return; + } + var src = AL.currentCtx.sources[sourceId]; + if (!src) { + AL.currentCtx.err = 40961; + return; + } + if ( + count > + (src.bufQueue.length === 1 && src.bufQueue[0].id === 0 + ? 0 + : src.bufsProcessed) + ) { + AL.currentCtx.err = 40963; + return; + } + + if (count === 0) { + return; + } + + for (var i = 0; i < count; i++) { + var buf = src.bufQueue.shift(); + buf.refCount--; + // Write the buffers index out to the return list. + HEAP32[(pBufferIds + i * 4) >> 2] = buf.id; + src.bufsProcessed--; + } + + /// If the queue is empty, put the zero buffer back in + if (src.bufQueue.length === 0) { + src.bufQueue.push(AL.buffers[0]); + } + + AL.initSourcePanner(src); + AL.scheduleSourceAudio(src); + }; + _alSourceUnqueueBuffers.sig = 'viip'; + + var _alSourcef = (sourceId, param, value) => { + switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1003 /* AL_PITCH */: + case 4106: + case 0x100d /* AL_MIN_GAIN */: + case 0x100e /* AL_MAX_GAIN */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1022 /* AL_CONE_OUTER_GAIN */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + case 0x200b /* AL_SEC_LENGTH_SOFT */: + AL.setSourceParam('alSourcef', sourceId, param, value); + break; + default: + AL.setSourceParam('alSourcef', sourceId, param, null); + break; + } + }; + _alSourcef.sig = 'viif'; + + var _alSourcefv = (sourceId, param, pValues) => { + if (!AL.currentCtx) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1003 /* AL_PITCH */: + case 4106: + case 0x100d /* AL_MIN_GAIN */: + case 0x100e /* AL_MAX_GAIN */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1022 /* AL_CONE_OUTER_GAIN */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + case 0x200b /* AL_SEC_LENGTH_SOFT */: + var val = HEAPF32[pValues >> 2]; + AL.setSourceParam('alSourcefv', sourceId, param, val); + break; + case 4100: + case 4101: + case 4102: + AL.paramArray[0] = HEAPF32[pValues >> 2]; + AL.paramArray[1] = HEAPF32[(pValues + 4) >> 2]; + AL.paramArray[2] = HEAPF32[(pValues + 8) >> 2]; + AL.setSourceParam('alSourcefv', sourceId, param, AL.paramArray); + break; + default: + AL.setSourceParam('alSourcefv', sourceId, param, null); + break; + } + }; + _alSourcefv.sig = 'viip'; + + var _alSourceiv = (sourceId, param, pValues) => { + if (!AL.currentCtx) { + return; + } + if (!pValues) { + AL.currentCtx.err = 40963; + return; + } + + switch (param) { + case 0x202 /* AL_SOURCE_RELATIVE */: + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1007 /* AL_LOOPING */: + case 0x1009 /* AL_BUFFER */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: + case 0x2009 /* AL_BYTE_LENGTH_SOFT */: + case 0x200a /* AL_SAMPLE_LENGTH_SOFT */: + case 53248: + var val = HEAP32[pValues >> 2]; + AL.setSourceParam('alSourceiv', sourceId, param, val); + break; + case 4100: + case 4101: + case 4102: + AL.paramArray[0] = HEAP32[pValues >> 2]; + AL.paramArray[1] = HEAP32[(pValues + 4) >> 2]; + AL.paramArray[2] = HEAP32[(pValues + 8) >> 2]; + AL.setSourceParam('alSourceiv', sourceId, param, AL.paramArray); + break; + default: + AL.setSourceParam('alSourceiv', sourceId, param, null); + break; + } + }; + _alSourceiv.sig = 'viip'; + + var _alSpeedOfSound = (value) => { + AL.setGlobalParam('alSpeedOfSound', 49155, value); + }; + _alSpeedOfSound.sig = 'vf'; + + var _alcCaptureCloseDevice = (deviceId) => { + var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureCloseDevice'); + if (!c) return false; + + delete AL.captures[deviceId]; + AL.freeIds.push(deviceId); + + // This clean-up might be unnecessary (paranoid) ? + + // May happen if user hasn't decided to grant or deny input + c.mediaStreamSourceNode?.disconnect(); + c.mergerNode?.disconnect(); + c.splitterNode?.disconnect(); + // May happen if user hasn't decided to grant or deny input + c.scriptProcessorNode?.disconnect(); + if (c.mediaStream) { + // Disabling the microphone of the browser. + // Without this operation, the red dot on the browser tab page will remain. + c.mediaStream.getTracks().forEach((track) => track.stop()); + } + + delete c.buffers; + + c.capturedFrameCount = 0; + c.isCapturing = false; + + return true; + }; + _alcCaptureCloseDevice.sig = 'ip'; + + var autoResumeAudioContext = (ctx) => { + for (var event of ['keydown', 'mousedown', 'touchstart']) { + for (var element of [document, document.getElementById('canvas')]) { + element?.addEventListener( + event, + () => { + if (ctx.state === 'suspended') ctx.resume(); + }, + { once: true } + ); + } + } + }; + + var _alcCaptureOpenDevice = ( + pDeviceName, + requestedSampleRate, + format, + bufferFrameCapacity + ) => { + var resolvedDeviceName = AL.CAPTURE_DEVICE_NAME; + + // NULL is a valid device name here (resolves to default); + if (pDeviceName !== 0) { + resolvedDeviceName = UTF8ToString(pDeviceName); + if (resolvedDeviceName !== AL.CAPTURE_DEVICE_NAME) { + // ALC_OUT_OF_MEMORY + // From the programmer's guide, ALC_OUT_OF_MEMORY's meaning is + // overloaded here, to mean: + // 'The specified device is invalid, or can not capture audio.' + // This may be misleading to API users, but well... + AL.alcErr = 0xa005 /* ALC_OUT_OF_MEMORY */; + return 0; + } + } + + // Otherwise it's probably okay (though useless) for bufferFrameCapacity to be zero. + if (bufferFrameCapacity < 0) { + // ALCsizei is signed int + AL.alcErr = 40964; + return 0; + } + + navigator.getUserMedia = + navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia || + navigator.msGetUserMedia; + var has_getUserMedia = + navigator.getUserMedia || + (navigator.mediaDevices && navigator.mediaDevices.getUserMedia); + + if (!has_getUserMedia) { + // See previously mentioned rationale for ALC_OUT_OF_MEMORY + AL.alcErr = 0xa005 /* ALC_OUT_OF_MEMORY */; + return 0; + } + + var AudioContext = window.AudioContext || window.webkitAudioContext; + + if (!AL.sharedCaptureAudioCtx) { + try { + AL.sharedCaptureAudioCtx = new AudioContext(); + } catch (e) { + // See previously mentioned rationale for ALC_OUT_OF_MEMORY + AL.alcErr = 0xa005 /* ALC_OUT_OF_MEMORY */; + return 0; + } + } + + autoResumeAudioContext(AL.sharedCaptureAudioCtx); + + var outputChannelCount; + + switch (format) { + case 0x10010: /* AL_FORMAT_MONO_FLOAT32 */ + case 0x1101: /* AL_FORMAT_MONO16 */ + case 0x1100 /* AL_FORMAT_MONO8 */: + outputChannelCount = 1; + break; + case 0x10011: /* AL_FORMAT_STEREO_FLOAT32 */ + case 0x1103: /* AL_FORMAT_STEREO16 */ + case 0x1102 /* AL_FORMAT_STEREO8 */: + outputChannelCount = 2; + break; + default: + AL.alcErr = 40964; + return 0; + } + + function newF32Array(cap) { + return new Float32Array(cap); + } + function newI16Array(cap) { + return new Int16Array(cap); + } + function newU8Array(cap) { + return new Uint8Array(cap); + } + + var requestedSampleType; + var newSampleArray; + + switch (format) { + case 0x10010: /* AL_FORMAT_MONO_FLOAT32 */ + case 0x10011 /* AL_FORMAT_STEREO_FLOAT32 */: + requestedSampleType = 'f32'; + newSampleArray = newF32Array; + break; + case 0x1101: /* AL_FORMAT_MONO16 */ + case 0x1103 /* AL_FORMAT_STEREO16 */: + requestedSampleType = 'i16'; + newSampleArray = newI16Array; + break; + case 0x1100: /* AL_FORMAT_MONO8 */ + case 0x1102 /* AL_FORMAT_STEREO8 */: + requestedSampleType = 'u8'; + newSampleArray = newU8Array; + break; + } + + var buffers = []; + try { + for (var chan = 0; chan < outputChannelCount; ++chan) { + buffers[chan] = newSampleArray(bufferFrameCapacity); + } + } catch (e) { + AL.alcErr = 0xa005 /* ALC_OUT_OF_MEMORY */; + return 0; + } + + // What we'll place into the `AL.captures` array in the end, + // declared here for closures to access it + var newCapture = { + audioCtx: AL.sharedCaptureAudioCtx, + deviceName: resolvedDeviceName, + requestedSampleRate, + requestedSampleType, + outputChannelCount, + inputChannelCount: null, // Not known until the getUserMedia() promise resolves + mediaStreamError: null, // Used by other functions to return early and report an error. + mediaStreamSourceNode: null, + mediaStream: null, + // Either one, or none of the below two, is active. + mergerNode: null, + splitterNode: null, + scriptProcessorNode: null, + isCapturing: false, + buffers, + get bufferFrameCapacity() { + return buffers[0].length; + }, + capturePlayhead: 0, // current write position, in sample frames + captureReadhead: 0, + capturedFrameCount: 0, + }; + + // Preparing for getUserMedia() + + var onError = (mediaStreamError) => { + newCapture.mediaStreamError = mediaStreamError; + }; + var onSuccess = (mediaStream) => { + newCapture.mediaStreamSourceNode = + newCapture.audioCtx.createMediaStreamSource(mediaStream); + newCapture.mediaStream = mediaStream; + + var inputChannelCount = 1; + switch (newCapture.mediaStreamSourceNode.channelCountMode) { + case 'max': + inputChannelCount = outputChannelCount; + break; + case 'clamped-max': + inputChannelCount = Math.min( + outputChannelCount, + newCapture.mediaStreamSourceNode.channelCount + ); + break; + case 'explicit': + inputChannelCount = + newCapture.mediaStreamSourceNode.channelCount; + break; + } + + newCapture.inputChannelCount = inputChannelCount; + + // Have to pick a size from 256, 512, 1024, 2048, 4096, 8192, 16384. + // One can also set it to zero, which leaves the decision up to the impl. + // An extension could allow specifying this value. + var processorFrameCount = 512; + + newCapture.scriptProcessorNode = + newCapture.audioCtx.createScriptProcessor( + processorFrameCount, + inputChannelCount, + outputChannelCount + ); + + if (inputChannelCount > outputChannelCount) { + newCapture.mergerNode = + newCapture.audioCtx.createChannelMerger(inputChannelCount); + newCapture.mediaStreamSourceNode.connect(newCapture.mergerNode); + newCapture.mergerNode.connect(newCapture.scriptProcessorNode); + } else if (inputChannelCount < outputChannelCount) { + newCapture.splitterNode = + newCapture.audioCtx.createChannelSplitter( + outputChannelCount + ); + newCapture.mediaStreamSourceNode.connect( + newCapture.splitterNode + ); + newCapture.splitterNode.connect(newCapture.scriptProcessorNode); + } else { + newCapture.mediaStreamSourceNode.connect( + newCapture.scriptProcessorNode + ); + } + + newCapture.scriptProcessorNode.connect( + newCapture.audioCtx.destination + ); + + newCapture.scriptProcessorNode.onaudioprocess = ( + audioProcessingEvent + ) => { + if (!newCapture.isCapturing) { + return; + } + + var c = newCapture; + var srcBuf = audioProcessingEvent.inputBuffer; + + // Actually just copy srcBuf's channel data into + // c.buffers, optimizing for each case. + switch (format) { + case 0x10010 /* AL_FORMAT_MONO_FLOAT32 */: + var channel0 = srcBuf.getChannelData(0); + for (var i = 0; i < srcBuf.length; ++i) { + var wi = + (c.capturePlayhead + i) % c.bufferFrameCapacity; + c.buffers[0][wi] = channel0[i]; + } + break; + case 0x10011 /* AL_FORMAT_STEREO_FLOAT32 */: + var channel0 = srcBuf.getChannelData(0); + var channel1 = srcBuf.getChannelData(1); + for (var i = 0; i < srcBuf.length; ++i) { + var wi = + (c.capturePlayhead + i) % c.bufferFrameCapacity; + c.buffers[0][wi] = channel0[i]; + c.buffers[1][wi] = channel1[i]; + } + break; + case 0x1101 /* AL_FORMAT_MONO16 */: + var channel0 = srcBuf.getChannelData(0); + for (var i = 0; i < srcBuf.length; ++i) { + var wi = + (c.capturePlayhead + i) % c.bufferFrameCapacity; + c.buffers[0][wi] = channel0[i] * 32767; + } + break; + case 0x1103 /* AL_FORMAT_STEREO16 */: + var channel0 = srcBuf.getChannelData(0); + var channel1 = srcBuf.getChannelData(1); + for (var i = 0; i < srcBuf.length; ++i) { + var wi = + (c.capturePlayhead + i) % c.bufferFrameCapacity; + c.buffers[0][wi] = channel0[i] * 32767; + c.buffers[1][wi] = channel1[i] * 32767; + } + break; + case 0x1100 /* AL_FORMAT_MONO8 */: + var channel0 = srcBuf.getChannelData(0); + for (var i = 0; i < srcBuf.length; ++i) { + var wi = + (c.capturePlayhead + i) % c.bufferFrameCapacity; + c.buffers[0][wi] = (channel0[i] + 1.0) * 127; + } + break; + case 0x1102 /* AL_FORMAT_STEREO8 */: + var channel0 = srcBuf.getChannelData(0); + var channel1 = srcBuf.getChannelData(1); + for (var i = 0; i < srcBuf.length; ++i) { + var wi = + (c.capturePlayhead + i) % c.bufferFrameCapacity; + c.buffers[0][wi] = (channel0[i] + 1.0) * 127; + c.buffers[1][wi] = (channel1[i] + 1.0) * 127; + } + break; + } + + c.capturePlayhead += srcBuf.length; + c.capturePlayhead %= c.bufferFrameCapacity; + c.capturedFrameCount += srcBuf.length; + c.capturedFrameCount = Math.min( + c.capturedFrameCount, + c.bufferFrameCapacity + ); + }; + }; + + // The latest way to call getUserMedia() + if (navigator.mediaDevices?.getUserMedia) { + navigator.mediaDevices + .getUserMedia({ audio: true }) + .then(onSuccess) + .catch(onError); + } else { + // The usual (now deprecated) way + navigator.getUserMedia({ audio: true }, onSuccess, onError); + } + + var id = AL.newId(); + AL.captures[id] = newCapture; + return id; + }; + _alcCaptureOpenDevice.sig = 'ppiii'; + + var _alcCaptureSamples = (deviceId, pFrames, requestedFrameCount) => { + var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureSamples'); + if (!c) return; + + // ALCsizei is actually 32-bit signed int, so could be negative + // Also, spec says : + // Requesting more sample frames than are currently available is + // an error. + + var dstfreq = c.requestedSampleRate; + var srcfreq = c.audioCtx.sampleRate; + + var fratio = srcfreq / dstfreq; + + if ( + requestedFrameCount < 0 || + requestedFrameCount > c.capturedFrameCount / fratio + ) { + AL.alcErr = 40964; + return; + } + + function setF32Sample(i, sample) { + HEAPF32[(pFrames + 4 * i) >> 2] = sample; + } + function setI16Sample(i, sample) { + HEAP16[(pFrames + 2 * i) >> 1] = sample; + } + function setU8Sample(i, sample) { + HEAP8[pFrames + i] = sample; + } + + var setSample; + + switch (c.requestedSampleType) { + case 'f32': + setSample = setF32Sample; + break; + case 'i16': + setSample = setI16Sample; + break; + case 'u8': + setSample = setU8Sample; + break; + default: + return; + } + + // If fratio is an integer we don't need linear resampling, just skip samples + if (Math.floor(fratio) == fratio) { + for ( + var i = 0, frame_i = 0; + frame_i < requestedFrameCount; + ++frame_i + ) { + for (var chan = 0; chan < c.buffers.length; ++chan, ++i) { + setSample(i, c.buffers[chan][c.captureReadhead]); + } + c.captureReadhead = + (fratio + c.captureReadhead) % c.bufferFrameCapacity; + } + } else { + // Perform linear resampling. + + // There is room for improvement - right now we're fine with linear resampling. + // We don't use OfflineAudioContexts for this: See the discussion at + // https://github.com/jpernst/emscripten/issues/2#issuecomment-312729735 + // if you're curious about why. + for ( + var i = 0, frame_i = 0; + frame_i < requestedFrameCount; + ++frame_i + ) { + var lefti = Math.floor(c.captureReadhead); + var righti = Math.ceil(c.captureReadhead); + var d = c.captureReadhead - lefti; + for (var chan = 0; chan < c.buffers.length; ++chan, ++i) { + var lefts = c.buffers[chan][lefti]; + var rights = c.buffers[chan][righti]; + setSample(i, (1 - d) * lefts + d * rights); + } + c.captureReadhead = + (c.captureReadhead + fratio) % c.bufferFrameCapacity; + } + } + + // Spec doesn't say if alcCaptureSamples() must zero the number + // of available captured sample-frames, but not only would it + // be insane not to do, OpenAL-Soft happens to do that as well. + c.capturedFrameCount = 0; + }; + _alcCaptureSamples.sig = 'vppi'; + + var _alcCaptureStart = (deviceId) => { + var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureStart'); + if (!c) return; + + if (c.isCapturing) { + // NOTE: Spec says (emphasis mine): + // The amount of audio samples available after **restarting** a + // stopped capture device is reset to zero. + // So redundant calls to alcCaptureStart() must have no effect. + return; + } + c.isCapturing = true; + c.capturedFrameCount = 0; + c.capturePlayhead = 0; + }; + _alcCaptureStart.sig = 'vp'; + + var _alcCaptureStop = (deviceId) => { + var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureStop'); + if (!c) return; + + c.isCapturing = false; + }; + _alcCaptureStop.sig = 'vp'; + + var _alcCloseDevice = (deviceId) => { + if ( + !(deviceId in AL.deviceRefCounts) || + AL.deviceRefCounts[deviceId] > 0 + ) { + return 0; + } + + delete AL.deviceRefCounts[deviceId]; + AL.freeIds.push(deviceId); + return 1; + }; + _alcCloseDevice.sig = 'ip'; + + var _alcCreateContext = (deviceId, pAttrList) => { + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 0xa001; /* ALC_INVALID_DEVICE */ + return 0; + } + + var options = null; + var attrs = []; + var hrtf = null; + pAttrList >>= 2; + if (pAttrList) { + var attr = 0; + var val = 0; + while (true) { + attr = HEAP32[pAttrList++]; + attrs.push(attr); + if (attr === 0) { + break; + } + val = HEAP32[pAttrList++]; + attrs.push(val); + + switch (attr) { + case 0x1007 /* ALC_FREQUENCY */: + if (!options) { + options = {}; + } + + options.sampleRate = val; + break; + case 0x1010 /* ALC_MONO_SOURCES */: // fallthrough + case 0x1011 /* ALC_STEREO_SOURCES */: + // Do nothing; these hints are satisfied by default + break; + case 0x1992 /* ALC_HRTF_SOFT */: + switch (val) { + case 0: + hrtf = false; + break; + case 1: + hrtf = true; + break; + case 2 /* ALC_DONT_CARE_SOFT */: + break; + default: + AL.alcErr = 40964; + return 0; + } + break; + case 0x1996 /* ALC_HRTF_ID_SOFT */: + if (val !== 0) { + AL.alcErr = 40964; + return 0; + } + break; + default: + AL.alcErr = 0xa004; /* ALC_INVALID_VALUE */ + return 0; + } + } + } + + var AudioContext = window.AudioContext || window.webkitAudioContext; + var ac = null; + try { + // Only try to pass options if there are any, for compat with browsers that don't support this + if (options) { + ac = new AudioContext(options); + } else { + ac = new AudioContext(); + } + } catch (e) { + if (e.name === 'NotSupportedError') { + AL.alcErr = 0xa004; /* ALC_INVALID_VALUE */ + } else { + AL.alcErr = 0xa001; /* ALC_INVALID_DEVICE */ + } + + return 0; + } + + autoResumeAudioContext(ac); + + // Old Web Audio API (e.g. Safari 6.0.5) had an inconsistently named createGainNode function. + if (typeof ac.createGain == 'undefined') { + ac.createGain = ac.createGainNode; + } + + var gain = ac.createGain(); + gain.connect(ac.destination); + var ctx = { + deviceId, + id: AL.newId(), + attrs, + audioCtx: ac, + listener: { + position: [0.0, 0.0, 0.0], + velocity: [0.0, 0.0, 0.0], + direction: [0.0, 0.0, 0.0], + up: [0.0, 0.0, 0.0], + }, + sources: [], + interval: setInterval( + () => AL.scheduleContextAudio(ctx), + AL.QUEUE_INTERVAL + ), + gain, + distanceModel: 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */, + speedOfSound: 343.3, + dopplerFactor: 1.0, + sourceDistanceModel: false, + hrtf: hrtf || false, + + _err: 0, + get err() { + return this._err; + }, + set err(val) { + // Errors should not be overwritten by later errors until they are cleared by a query. + if (this._err === 0 || val === 0) { + this._err = val; + } + }, + }; + AL.deviceRefCounts[deviceId]++; + AL.contexts[ctx.id] = ctx; + + if (hrtf !== null) { + // Apply hrtf attrib to all contexts for this device + for (var ctxId in AL.contexts) { + var c = AL.contexts[ctxId]; + if (c.deviceId === deviceId) { + c.hrtf = hrtf; + AL.updateContextGlobal(c); + } + } + } + + return ctx.id; + }; + _alcCreateContext.sig = 'ppp'; + + var _alcDestroyContext = (contextId) => { + var ctx = AL.contexts[contextId]; + if (AL.currentCtx === ctx) { + AL.alcErr = 0xa002 /* ALC_INVALID_CONTEXT */; + return; + } + + // Stop playback, etc + if (AL.contexts[contextId].interval) { + clearInterval(AL.contexts[contextId].interval); + } + AL.deviceRefCounts[ctx.deviceId]--; + delete AL.contexts[contextId]; + AL.freeIds.push(contextId); + }; + _alcDestroyContext.sig = 'vp'; + + var _alcGetContextsDevice = (contextId) => { + if (contextId in AL.contexts) { + return AL.contexts[contextId].deviceId; + } + return 0; + }; + _alcGetContextsDevice.sig = 'pp'; + + var _alcGetCurrentContext = () => { + if (AL.currentCtx !== null) { + return AL.currentCtx.id; + } + return 0; + }; + _alcGetCurrentContext.sig = 'p'; + + var _alcGetEnumValue = (deviceId, pEnumName) => { + // Spec says : + // Using a NULL handle is legal, but only the + // tokens defined by the AL core are guaranteed. + if (deviceId !== 0 && !(deviceId in AL.deviceRefCounts)) { + // ALC_INVALID_DEVICE is not listed as a possible error state for + // this function, sadly. + return 0; + } else if (!pEnumName) { + AL.alcErr = 40964; + return 0; + } + var name = UTF8ToString(pEnumName); + // See alGetEnumValue(), but basically behave the same as OpenAL-Soft + switch (name) { + case 'ALC_NO_ERROR': + return 0; + case 'ALC_INVALID_DEVICE': + return 0xa001; + case 'ALC_INVALID_CONTEXT': + return 0xa002; + case 'ALC_INVALID_ENUM': + return 0xa003; + case 'ALC_INVALID_VALUE': + return 0xa004; + case 'ALC_OUT_OF_MEMORY': + return 0xa005; + case 'ALC_MAJOR_VERSION': + return 0x1000; + case 'ALC_MINOR_VERSION': + return 0x1001; + case 'ALC_ATTRIBUTES_SIZE': + return 0x1002; + case 'ALC_ALL_ATTRIBUTES': + return 0x1003; + case 'ALC_DEFAULT_DEVICE_SPECIFIER': + return 0x1004; + case 'ALC_DEVICE_SPECIFIER': + return 0x1005; + case 'ALC_EXTENSIONS': + return 0x1006; + case 'ALC_FREQUENCY': + return 0x1007; + case 'ALC_REFRESH': + return 0x1008; + case 'ALC_SYNC': + return 0x1009; + case 'ALC_MONO_SOURCES': + return 0x1010; + case 'ALC_STEREO_SOURCES': + return 0x1011; + case 'ALC_CAPTURE_DEVICE_SPECIFIER': + return 0x310; + case 'ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER': + return 0x311; + case 'ALC_CAPTURE_SAMPLES': + return 0x312; + + /* Extensions */ + case 'ALC_HRTF_SOFT': + return 0x1992; + case 'ALC_HRTF_ID_SOFT': + return 0x1996; + case 'ALC_DONT_CARE_SOFT': + return 0x0002; + case 'ALC_HRTF_STATUS_SOFT': + return 0x1993; + case 'ALC_NUM_HRTF_SPECIFIERS_SOFT': + return 0x1994; + case 'ALC_HRTF_SPECIFIER_SOFT': + return 0x1995; + case 'ALC_HRTF_DISABLED_SOFT': + return 0x0000; + case 'ALC_HRTF_ENABLED_SOFT': + return 0x0001; + case 'ALC_HRTF_DENIED_SOFT': + return 0x0002; + case 'ALC_HRTF_REQUIRED_SOFT': + return 0x0003; + case 'ALC_HRTF_HEADPHONES_DETECTED_SOFT': + return 0x0004; + case 'ALC_HRTF_UNSUPPORTED_FORMAT_SOFT': + return 0x0005; + + default: + AL.alcErr = 40964; + return 0; + } + }; + _alcGetEnumValue.sig = 'ipp'; + + var _alcGetError = (deviceId) => { + var err = AL.alcErr; + AL.alcErr = 0; + return err; + }; + _alcGetError.sig = 'ip'; + + var _alcGetIntegerv = (deviceId, param, size, pValues) => { + if (size === 0 || !pValues) { + // Ignore the query, per the spec + return; + } + + switch (param) { + case 0x1000 /* ALC_MAJOR_VERSION */: + HEAP32[pValues >> 2] = 1; + break; + case 0x1001 /* ALC_MINOR_VERSION */: + HEAP32[pValues >> 2] = 1; + break; + case 0x1002 /* ALC_ATTRIBUTES_SIZE */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xa002 /* ALC_INVALID_CONTEXT */; + return; + } + + HEAP32[pValues >> 2] = AL.currentCtx.attrs.length; + break; + case 0x1003 /* ALC_ALL_ATTRIBUTES */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xa002 /* ALC_INVALID_CONTEXT */; + return; + } + + for (var i = 0; i < AL.currentCtx.attrs.length; i++) { + HEAP32[(pValues + i * 4) >> 2] = AL.currentCtx.attrs[i]; + } + break; + case 0x1007 /* ALC_FREQUENCY */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xa002 /* ALC_INVALID_CONTEXT */; + return; + } + + HEAP32[pValues >> 2] = AL.currentCtx.audioCtx.sampleRate; + break; + case 0x1010 /* ALC_MONO_SOURCES */: + case 0x1011 /* ALC_STEREO_SOURCES */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xa002 /* ALC_INVALID_CONTEXT */; + return; + } + + HEAP32[pValues >> 2] = 0x7fffffff; + break; + case 0x1992 /* ALC_HRTF_SOFT */: + case 0x1993 /* ALC_HRTF_STATUS_SOFT */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + + var hrtfStatus = 0; /* ALC_HRTF_DISABLED_SOFT */ + for (var ctxId in AL.contexts) { + var ctx = AL.contexts[ctxId]; + if (ctx.deviceId === deviceId) { + hrtfStatus = ctx.hrtf + ? 1 /* ALC_HRTF_ENABLED_SOFT */ + : 0 /* ALC_HRTF_DISABLED_SOFT */; + } + } + HEAP32[pValues >> 2] = hrtfStatus; + break; + case 0x1994 /* ALC_NUM_HRTF_SPECIFIERS_SOFT */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + HEAP32[pValues >> 2] = 1; + break; + case 0x20003 /* ALC_MAX_AUXILIARY_SENDS */: + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + if (!AL.currentCtx) { + AL.alcErr = 0xa002 /* ALC_INVALID_CONTEXT */; + return; + } + + HEAP32[pValues >> 2] = 1; + case 0x312 /* ALC_CAPTURE_SAMPLES */: + var c = AL.requireValidCaptureDevice( + deviceId, + 'alcGetIntegerv' + ); + if (!c) { + return; + } + var n = c.capturedFrameCount; + var dstfreq = c.requestedSampleRate; + var srcfreq = c.audioCtx.sampleRate; + var nsamples = Math.floor(n * (dstfreq / srcfreq)); + HEAP32[pValues >> 2] = nsamples; + break; + default: + AL.alcErr = 40963; + return; + } + }; + _alcGetIntegerv.sig = 'vpiip'; + + var _alcGetString = (deviceId, param) => { + if (AL.alcStringCache[param]) { + return AL.alcStringCache[param]; + } + + var ret; + switch (param) { + case 0: + ret = 'No Error'; + break; + case 40961: + ret = 'Invalid Device'; + break; + case 0xa002 /* ALC_INVALID_CONTEXT */: + ret = 'Invalid Context'; + break; + case 40963: + ret = 'Invalid Enum'; + break; + case 40964: + ret = 'Invalid Value'; + break; + case 0xa005 /* ALC_OUT_OF_MEMORY */: + ret = 'Out of Memory'; + break; + case 0x1004 /* ALC_DEFAULT_DEVICE_SPECIFIER */: + if (globalThis.AudioContext || globalThis.webkitAudioContext) { + ret = AL.DEVICE_NAME; + } else { + return 0; + } + break; + case 0x1005 /* ALC_DEVICE_SPECIFIER */: + if (globalThis.AudioContext || globalThis.webkitAudioContext) { + ret = AL.DEVICE_NAME + '\0'; + } else { + ret = '\0'; + } + break; + case 0x311 /* ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER */: + ret = AL.CAPTURE_DEVICE_NAME; + break; + case 0x310 /* ALC_CAPTURE_DEVICE_SPECIFIER */: + if (deviceId === 0) { + ret = AL.CAPTURE_DEVICE_NAME + '\0'; + } else { + var c = AL.requireValidCaptureDevice( + deviceId, + 'alcGetString' + ); + if (!c) { + return 0; + } + ret = c.deviceName; + } + break; + case 0x1006 /* ALC_EXTENSIONS */: + if (!deviceId) { + AL.alcErr = 40961; + return 0; + } + + ret = Object.keys(AL.ALC_EXTENSIONS).join(' '); + break; + default: + AL.alcErr = 40963; + return 0; + } + + ret = stringToNewUTF8(ret); + AL.alcStringCache[param] = ret; + return ret; + }; + _alcGetString.sig = 'ppi'; + + var _alcIsExtensionPresent = (deviceId, pExtName) => { + var name = UTF8ToString(pExtName); + + return AL.ALC_EXTENSIONS[name] ? 1 : 0; + }; + _alcIsExtensionPresent.sig = 'ipp'; + + var _alcMakeContextCurrent = (contextId) => { + if (contextId === 0) { + AL.currentCtx = null; + } else { + AL.currentCtx = AL.contexts[contextId]; + } + return 1; + }; + _alcMakeContextCurrent.sig = 'ip'; + + var _alcOpenDevice = (pDeviceName) => { + if (pDeviceName) { + var name = UTF8ToString(pDeviceName); + if (name !== AL.DEVICE_NAME) { + return 0; + } + } + + if (globalThis.AudioContext || globalThis.webkitAudioContext) { + var deviceId = AL.newId(); + AL.deviceRefCounts[deviceId] = 0; + return deviceId; + } + return 0; + }; + _alcOpenDevice.sig = 'pp'; + + var _alcProcessContext = (contextId) => {}; + _alcProcessContext.sig = 'vp'; + + var _alcSuspendContext = (contextId) => {}; + _alcSuspendContext.sig = 'vp'; + + var _emscripten_get_now_res = () => { + // return resolution of get_now, in nanoseconds + if (ENVIRONMENT_IS_NODE) { + return 1; // nanoseconds + } + // Modern environment where performance.now() is supported: + return 1000; // microseconds (1/1000 of a millisecond) + }; + _emscripten_get_now_res.sig = 'd'; + + var nowIsMonotonic = 1; + + var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; + var _clock_res_get = (clk_id, pres) => { + if (!checkWasiClock(clk_id)) { + return 28; + } + var nsec; + // all wasi clocks but realtime are monotonic + if (clk_id === 0) { + nsec = 1000 * 1000; // educated guess that it's milliseconds + } else if (nowIsMonotonic) { + nsec = _emscripten_get_now_res(); + } else { + return 52; + } + HEAP64[pres >> 3] = BigInt(nsec); + return 0; + }; + _clock_res_get.sig = 'iip'; + + var _emscripten_date_now = () => Date.now(); + _emscripten_date_now.sig = 'd'; + + 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 * 1000 * 1000); + HEAP64[ptime >> 3] = BigInt(nsec); + return 0; + } + _clock_time_get.sig = 'iijp'; + + var _emscripten_alcDevicePauseSOFT = (deviceId) => { + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + + if (AL.paused) { + return; + } + AL.paused = true; + + for (var ctxId in AL.contexts) { + var ctx = AL.contexts[ctxId]; + if (ctx.deviceId !== deviceId) { + continue; + } + + ctx.audioCtx.suspend(); + clearInterval(ctx.interval); + ctx.interval = null; + } + }; + _emscripten_alcDevicePauseSOFT.sig = 'vi'; + + var _emscripten_alcDeviceResumeSOFT = (deviceId) => { + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return; + } + + if (!AL.paused) { + return; + } + AL.paused = false; + + for (var ctxId in AL.contexts) { + var ctx = AL.contexts[ctxId]; + if (ctx.deviceId !== deviceId) { + continue; + } + + ctx.interval = setInterval( + () => AL.scheduleContextAudio(ctx), + AL.QUEUE_INTERVAL + ); + ctx.audioCtx.resume(); + } + }; + _emscripten_alcDeviceResumeSOFT.sig = 'vi'; + + var _emscripten_alcGetStringiSOFT = (deviceId, param, index) => { + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return 0; + } + + if (AL.alcStringCache[param]) { + return AL.alcStringCache[param]; + } + + var ret; + switch (param) { + case 0x1995 /* ALC_HRTF_SPECIFIER_SOFT */: + if (index === 0) { + ret = 'Web Audio HRTF'; + } else { + AL.alcErr = 40964; + return 0; + } + break; + default: + if (index !== 0) { + AL.alcErr = 40963; + return 0; + } + return _alcGetString(deviceId, param); + } + + ret = stringToNewUTF8(ret); + AL.alcStringCache[param] = ret; + return ret; + }; + _emscripten_alcGetStringiSOFT.sig = 'iiii'; + + var _emscripten_alcResetDeviceSOFT = (deviceId, pAttrList) => { + if (!(deviceId in AL.deviceRefCounts)) { + AL.alcErr = 40961; + return 0; + } + + var hrtf = null; + pAttrList >>= 2; + if (pAttrList) { + var attr = 0; + var val = 0; + while (true) { + attr = HEAP32[pAttrList++]; + if (attr === 0) { + break; + } + val = HEAP32[pAttrList++]; + + switch (attr) { + case 0x1992 /* ALC_HRTF_SOFT */: + if (val === 1) { + hrtf = true; + } else if (val === 0) { + hrtf = false; + } + break; + } + } + } + + if (hrtf !== null) { + // Apply hrtf attrib to all contexts for this device + for (var ctxId in AL.contexts) { + var ctx = AL.contexts[ctxId]; + if (ctx.deviceId === deviceId) { + ctx.hrtf = hrtf; + AL.updateContextGlobal(ctx); + } + } + } + + return 1; + }; + _emscripten_alcResetDeviceSOFT.sig = 'iii'; + + var readEmAsmArgsArray = []; + var readEmAsmArgs = (sigPtr, buf) => { + readEmAsmArgsArray.length = 0; + var ch; + // Most arguments are i32s, so shift the buffer pointer so it is a plain + // index into HEAP32. + while ((ch = HEAPU8[sigPtr++])) { + // 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 + ? HEAPU32[buf >> 2] + : ch == 106 + ? HEAP64[buf >> 3] + : ch == 105 + ? HEAP32[buf >> 2] + : HEAPF64[buf >> 3] + ); + buf += wide ? 8 : 4; + } + return readEmAsmArgsArray; + }; + var runEmAsmFunction = (code, sigPtr, argbuf) => { + var args = readEmAsmArgs(sigPtr, argbuf); + return ASM_CONSTS[code](...args); + }; + var _emscripten_asm_const_int = (code, sigPtr, argbuf) => { + return runEmAsmFunction(code, sigPtr, argbuf); + }; + _emscripten_asm_const_int.sig = 'ippp'; + + var _emscripten_console_error = (str) => { + console.error(UTF8ToString(str)); + }; + _emscripten_console_error.sig = 'vp'; + + var _emscripten_console_log = (str) => { + console.log(UTF8ToString(str)); + }; + _emscripten_console_log.sig = 'vp'; + + var _emscripten_console_trace = (str) => { + console.trace(UTF8ToString(str)); + }; + _emscripten_console_trace.sig = 'vp'; + + var _emscripten_console_warn = (str) => { + console.warn(UTF8ToString(str)); + }; + _emscripten_console_warn.sig = 'vp'; + + var _emscripten_err = (str) => err(UTF8ToString(str)); + _emscripten_err.sig = 'vp'; + + 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(); + _emscripten_get_heap_max.sig = 'p'; + + 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_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 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: [], + stringCache: {}, + 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(0x502 /* GL_INVALID_OPERATION */); + } + HEAP32[(buffers + i * 4) >> 2] = id; + } + }, + getSource: (shader, count, string, length) => { + var source = ''; + for (var i = 0; i < count; ++i) { + var len = length ? HEAPU32[(length + i * 4) >> 2] : undefined; + source += UTF8ToString(HEAPU32[(string + i * 4) >> 2], len); + } + return source; + }, + createContext: ( + /** @type {HTMLCanvasElement} */ canvas, + webGLContextAttributes + ) => { + var ctx = canvas.getContext('webgl', webGLContextAttributes); + + if (!ctx) return 0; + + var handle = GL.registerContext(ctx, webGLContextAttributes); + + return handle; + }, + registerContext: (ctx, webGLContextAttributes) => { + // without pthreads a context is just an integer ID + var handle = GL.getNewId(GL.contexts); + + 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) => { + return 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; + } + 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); + { + GLctx.disjointTimerQueryExt = GLctx.getExtension( + 'EXT_disjoint_timer_query' + ); + } + + for (var ext of getEmscriptenSupportedExtensions(GLctx)) { + // 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 _emscripten_glActiveTexture = (x0) => GLctx.activeTexture(x0); + _emscripten_glActiveTexture.sig = 'vi'; + + var _emscripten_glAttachShader = (program, shader) => { + GLctx.attachShader(GL.programs[program], GL.shaders[shader]); + }; + _emscripten_glAttachShader.sig = 'vii'; + + var _emscripten_glBeginQueryEXT = (target, id) => { + GLctx.disjointTimerQueryExt['beginQueryEXT'](target, GL.queries[id]); + }; + _emscripten_glBeginQueryEXT.sig = 'vii'; + + var _emscripten_glBindAttribLocation = (program, index, name) => { + GLctx.bindAttribLocation( + GL.programs[program], + index, + UTF8ToString(name) + ); + }; + _emscripten_glBindAttribLocation.sig = 'viip'; + + var _emscripten_glBindBuffer = (target, buffer) => { + GLctx.bindBuffer(target, GL.buffers[buffer]); + }; + _emscripten_glBindBuffer.sig = 'vii'; + + var _emscripten_glBindFramebuffer = (target, framebuffer) => { + GLctx.bindFramebuffer(target, GL.framebuffers[framebuffer]); + }; + _emscripten_glBindFramebuffer.sig = 'vii'; + + var _emscripten_glBindRenderbuffer = (target, renderbuffer) => { + GLctx.bindRenderbuffer(target, GL.renderbuffers[renderbuffer]); + }; + _emscripten_glBindRenderbuffer.sig = 'vii'; + + var _emscripten_glBindTexture = (target, texture) => { + GLctx.bindTexture(target, GL.textures[texture]); + }; + _emscripten_glBindTexture.sig = 'vii'; + + var _emscripten_glBindVertexArray = (vao) => { + GLctx.bindVertexArray(GL.vaos[vao]); + }; + _emscripten_glBindVertexArray.sig = 'vi'; + var _emscripten_glBindVertexArrayOES = _emscripten_glBindVertexArray; + _emscripten_glBindVertexArrayOES.sig = 'vi'; + + var _emscripten_glBlendColor = (x0, x1, x2, x3) => + GLctx.blendColor(x0, x1, x2, x3); + _emscripten_glBlendColor.sig = 'vffff'; + + var _emscripten_glBlendEquation = (x0) => GLctx.blendEquation(x0); + _emscripten_glBlendEquation.sig = 'vi'; + + var _emscripten_glBlendEquationSeparate = (x0, x1) => + GLctx.blendEquationSeparate(x0, x1); + _emscripten_glBlendEquationSeparate.sig = 'vii'; + + var _emscripten_glBlendFunc = (x0, x1) => GLctx.blendFunc(x0, x1); + _emscripten_glBlendFunc.sig = 'vii'; + + var _emscripten_glBlendFuncSeparate = (x0, x1, x2, x3) => + GLctx.blendFuncSeparate(x0, x1, x2, x3); + _emscripten_glBlendFuncSeparate.sig = 'viiii'; + + var _emscripten_glBufferData = (target, size, data, usage) => { + // 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 ? HEAPU8.subarray(data, data + size) : size, + usage + ); + }; + _emscripten_glBufferData.sig = 'vippi'; + + var _emscripten_glBufferSubData = (target, offset, size, data) => { + GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data + size)); + }; + _emscripten_glBufferSubData.sig = 'vippp'; + + var _emscripten_glCheckFramebufferStatus = (x0) => + GLctx.checkFramebufferStatus(x0); + _emscripten_glCheckFramebufferStatus.sig = 'ii'; + + var _emscripten_glClear = (x0) => GLctx.clear(x0); + _emscripten_glClear.sig = 'vi'; + + var _emscripten_glClearColor = (x0, x1, x2, x3) => + GLctx.clearColor(x0, x1, x2, x3); + _emscripten_glClearColor.sig = 'vffff'; + + var _emscripten_glClearDepthf = (x0) => GLctx.clearDepth(x0); + _emscripten_glClearDepthf.sig = 'vf'; + + var _emscripten_glClearStencil = (x0) => GLctx.clearStencil(x0); + _emscripten_glClearStencil.sig = 'vi'; + + var _emscripten_glClipControlEXT = (origin, depth) => { + GLctx.extClipControl['clipControlEXT'](origin, depth); + }; + _emscripten_glClipControlEXT.sig = 'vii'; + + var _emscripten_glColorMask = (red, green, blue, alpha) => { + GLctx.colorMask(!!red, !!green, !!blue, !!alpha); + }; + _emscripten_glColorMask.sig = 'viiii'; + + var _emscripten_glCompileShader = (shader) => { + GLctx.compileShader(GL.shaders[shader]); + }; + _emscripten_glCompileShader.sig = 'vi'; + + var _emscripten_glCompressedTexImage2D = ( + target, + level, + internalFormat, + width, + height, + border, + imageSize, + data + ) => { + // `data` may be null here, which means "allocate uniniitalized space but + // don't upload" in GLES parlance, but `compressedTexImage2D` requires the + // final data parameter, so we simply pass a heap view starting at zero + // effectively uploading whatever happens to be near address zero. See + // https://github.com/emscripten-core/emscripten/issues/19300. + GLctx.compressedTexImage2D( + target, + level, + internalFormat, + width, + height, + border, + HEAPU8.subarray(data, data + imageSize) + ); + }; + _emscripten_glCompressedTexImage2D.sig = 'viiiiiiip'; + + var _emscripten_glCompressedTexSubImage2D = ( + target, + level, + xoffset, + yoffset, + width, + height, + format, + imageSize, + data + ) => { + GLctx.compressedTexSubImage2D( + target, + level, + xoffset, + yoffset, + width, + height, + format, + HEAPU8.subarray(data, data + imageSize) + ); + }; + _emscripten_glCompressedTexSubImage2D.sig = 'viiiiiiiip'; + + var _emscripten_glCopyTexImage2D = (x0, x1, x2, x3, x4, x5, x6, x7) => + GLctx.copyTexImage2D(x0, x1, x2, x3, x4, x5, x6, x7); + _emscripten_glCopyTexImage2D.sig = 'viiiiiiii'; + + var _emscripten_glCopyTexSubImage2D = (x0, x1, x2, x3, x4, x5, x6, x7) => + GLctx.copyTexSubImage2D(x0, x1, x2, x3, x4, x5, x6, x7); + _emscripten_glCopyTexSubImage2D.sig = 'viiiiiiii'; + + var _emscripten_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; + }; + _emscripten_glCreateProgram.sig = 'i'; + + var _emscripten_glCreateShader = (shaderType) => { + var id = GL.getNewId(GL.shaders); + GL.shaders[id] = GLctx.createShader(shaderType); + + return id; + }; + _emscripten_glCreateShader.sig = 'ii'; + + var _emscripten_glCullFace = (x0) => GLctx.cullFace(x0); + _emscripten_glCullFace.sig = 'vi'; + + var _emscripten_glDeleteBuffers = (n, buffers) => { + for (var i = 0; i < n; i++) { + var id = HEAP32[(buffers + i * 4) >> 2]; + var buffer = GL.buffers[id]; + + // From spec: "glDeleteBuffers silently ignores 0's and names that do not + // correspond to existing buffer objects." + if (!buffer) continue; + + GLctx.deleteBuffer(buffer); + buffer.name = 0; + GL.buffers[id] = null; + } + }; + _emscripten_glDeleteBuffers.sig = 'vip'; + + var _emscripten_glDeleteFramebuffers = (n, framebuffers) => { + for (var i = 0; i < n; ++i) { + var id = HEAP32[(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; + } + }; + _emscripten_glDeleteFramebuffers.sig = 'vip'; + + var _emscripten_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(0x501 /* GL_INVALID_VALUE */); + return; + } + GLctx.deleteProgram(program); + program.name = 0; + GL.programs[id] = null; + }; + _emscripten_glDeleteProgram.sig = 'vi'; + + var _emscripten_glDeleteQueriesEXT = (n, ids) => { + for (var i = 0; i < n; i++) { + var id = HEAP32[(ids + i * 4) >> 2]; + var query = GL.queries[id]; + if (!query) continue; // GL spec: "unused names in ids are ignored, as is the name zero." + GLctx.disjointTimerQueryExt['deleteQueryEXT'](query); + GL.queries[id] = null; + } + }; + _emscripten_glDeleteQueriesEXT.sig = 'vip'; + + var _emscripten_glDeleteRenderbuffers = (n, renderbuffers) => { + for (var i = 0; i < n; i++) { + var id = HEAP32[(renderbuffers + i * 4) >> 2]; + var renderbuffer = GL.renderbuffers[id]; + if (!renderbuffer) continue; // GL spec: "glDeleteRenderbuffers silently ignores 0s and names that do not correspond to existing renderbuffer objects". + GLctx.deleteRenderbuffer(renderbuffer); + renderbuffer.name = 0; + GL.renderbuffers[id] = null; + } + }; + _emscripten_glDeleteRenderbuffers.sig = 'vip'; + + var _emscripten_glDeleteShader = (id) => { + if (!id) return; + var shader = GL.shaders[id]; + if (!shader) { + // glDeleteShader actually signals an error when deleting a nonexisting + // object, unlike some other GL delete functions. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + GLctx.deleteShader(shader); + GL.shaders[id] = null; + }; + _emscripten_glDeleteShader.sig = 'vi'; + + var _emscripten_glDeleteTextures = (n, textures) => { + for (var i = 0; i < n; i++) { + var id = HEAP32[(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; + } + }; + _emscripten_glDeleteTextures.sig = 'vip'; + + var _emscripten_glDeleteVertexArrays = (n, vaos) => { + for (var i = 0; i < n; i++) { + var id = HEAP32[(vaos + i * 4) >> 2]; + GLctx.deleteVertexArray(GL.vaos[id]); + GL.vaos[id] = null; + } + }; + _emscripten_glDeleteVertexArrays.sig = 'vip'; + var _emscripten_glDeleteVertexArraysOES = _emscripten_glDeleteVertexArrays; + _emscripten_glDeleteVertexArraysOES.sig = 'vip'; + + var _emscripten_glDepthFunc = (x0) => GLctx.depthFunc(x0); + _emscripten_glDepthFunc.sig = 'vi'; + + var _emscripten_glDepthMask = (flag) => { + GLctx.depthMask(!!flag); + }; + _emscripten_glDepthMask.sig = 'vi'; + + var _emscripten_glDepthRangef = (x0, x1) => GLctx.depthRange(x0, x1); + _emscripten_glDepthRangef.sig = 'vff'; + + var _emscripten_glDetachShader = (program, shader) => { + GLctx.detachShader(GL.programs[program], GL.shaders[shader]); + }; + _emscripten_glDetachShader.sig = 'vii'; + + var _emscripten_glDisable = (x0) => GLctx.disable(x0); + _emscripten_glDisable.sig = 'vi'; + + var _emscripten_glDisableVertexAttribArray = (index) => { + GLctx.disableVertexAttribArray(index); + }; + _emscripten_glDisableVertexAttribArray.sig = 'vi'; + + var _emscripten_glDrawArrays = (mode, first, count) => { + GLctx.drawArrays(mode, first, count); + }; + _emscripten_glDrawArrays.sig = 'viii'; + + var _emscripten_glDrawArraysInstanced = (mode, first, count, primcount) => { + GLctx.drawArraysInstanced(mode, first, count, primcount); + }; + _emscripten_glDrawArraysInstanced.sig = 'viiii'; + var _emscripten_glDrawArraysInstancedANGLE = + _emscripten_glDrawArraysInstanced; + + var tempFixedLengthArray = []; + + var _emscripten_glDrawBuffers = (n, bufs) => { + var bufArray = tempFixedLengthArray[n]; + for (var i = 0; i < n; i++) { + bufArray[i] = HEAP32[(bufs + i * 4) >> 2]; + } + + GLctx.drawBuffers(bufArray); + }; + _emscripten_glDrawBuffers.sig = 'vip'; + var _emscripten_glDrawBuffersWEBGL = _emscripten_glDrawBuffers; + + var _emscripten_glDrawElements = (mode, count, type, indices) => { + GLctx.drawElements(mode, count, type, indices); + }; + _emscripten_glDrawElements.sig = 'viiip'; + + var _emscripten_glDrawElementsInstanced = ( + mode, + count, + type, + indices, + primcount + ) => { + GLctx.drawElementsInstanced(mode, count, type, indices, primcount); + }; + _emscripten_glDrawElementsInstanced.sig = 'viiipi'; + var _emscripten_glDrawElementsInstancedANGLE = + _emscripten_glDrawElementsInstanced; + + var _emscripten_glEnable = (x0) => GLctx.enable(x0); + _emscripten_glEnable.sig = 'vi'; + + var _emscripten_glEnableVertexAttribArray = (index) => { + GLctx.enableVertexAttribArray(index); + }; + _emscripten_glEnableVertexAttribArray.sig = 'vi'; + + var _emscripten_glEndQueryEXT = (target) => { + GLctx.disjointTimerQueryExt['endQueryEXT'](target); + }; + _emscripten_glEndQueryEXT.sig = 'vi'; + + var _emscripten_glFinish = () => GLctx.finish(); + _emscripten_glFinish.sig = 'v'; + + var _emscripten_glFlush = () => GLctx.flush(); + _emscripten_glFlush.sig = 'v'; + + var _emscripten_glFramebufferRenderbuffer = ( + target, + attachment, + renderbuffertarget, + renderbuffer + ) => { + GLctx.framebufferRenderbuffer( + target, + attachment, + renderbuffertarget, + GL.renderbuffers[renderbuffer] + ); + }; + _emscripten_glFramebufferRenderbuffer.sig = 'viiii'; + + var _emscripten_glFramebufferTexture2D = ( + target, + attachment, + textarget, + texture, + level + ) => { + GLctx.framebufferTexture2D( + target, + attachment, + textarget, + GL.textures[texture], + level + ); + }; + _emscripten_glFramebufferTexture2D.sig = 'viiiii'; + + var _emscripten_glFrontFace = (x0) => GLctx.frontFace(x0); + _emscripten_glFrontFace.sig = 'vi'; + + var _emscripten_glGenBuffers = (n, buffers) => { + GL.genObject(n, buffers, 'createBuffer', GL.buffers); + }; + _emscripten_glGenBuffers.sig = 'vip'; + + var _emscripten_glGenFramebuffers = (n, ids) => { + GL.genObject(n, ids, 'createFramebuffer', GL.framebuffers); + }; + _emscripten_glGenFramebuffers.sig = 'vip'; + + var _emscripten_glGenQueriesEXT = (n, ids) => { + for (var i = 0; i < n; i++) { + var query = GLctx.disjointTimerQueryExt['createQueryEXT'](); + if (!query) { + GL.recordError(0x502 /* GL_INVALID_OPERATION */); + while (i < n) HEAP32[(ids + i++ * 4) >> 2] = 0; + return; + } + var id = GL.getNewId(GL.queries); + query.name = id; + GL.queries[id] = query; + HEAP32[(ids + i * 4) >> 2] = id; + } + }; + _emscripten_glGenQueriesEXT.sig = 'vip'; + + var _emscripten_glGenRenderbuffers = (n, renderbuffers) => { + GL.genObject(n, renderbuffers, 'createRenderbuffer', GL.renderbuffers); + }; + _emscripten_glGenRenderbuffers.sig = 'vip'; + + var _emscripten_glGenTextures = (n, textures) => { + GL.genObject(n, textures, 'createTexture', GL.textures); + }; + _emscripten_glGenTextures.sig = 'vip'; + + var _emscripten_glGenVertexArrays = (n, arrays) => { + GL.genObject(n, arrays, 'createVertexArray', GL.vaos); + }; + _emscripten_glGenVertexArrays.sig = 'vip'; + var _emscripten_glGenVertexArraysOES = _emscripten_glGenVertexArrays; + _emscripten_glGenVertexArraysOES.sig = 'vip'; + + var _emscripten_glGenerateMipmap = (x0) => GLctx.generateMipmap(x0); + _emscripten_glGenerateMipmap.sig = 'vi'; + + var __glGetActiveAttribOrUniform = ( + funcName, + program, + index, + bufSize, + length, + size, + type, + name + ) => { + program = GL.programs[program]; + var info = GLctx[funcName](program, index); + if (info) { + // If an error occurs, nothing will be written to length, size and type and name. + var numBytesWrittenExclNull = + name && stringToUTF8(info.name, name, bufSize); + if (length) HEAP32[length >> 2] = numBytesWrittenExclNull; + if (size) HEAP32[size >> 2] = info.size; + if (type) HEAP32[type >> 2] = info.type; + } + }; + + var _emscripten_glGetActiveAttrib = ( + program, + index, + bufSize, + length, + size, + type, + name + ) => + __glGetActiveAttribOrUniform( + 'getActiveAttrib', + program, + index, + bufSize, + length, + size, + type, + name + ); + _emscripten_glGetActiveAttrib.sig = 'viiipppp'; + + var _emscripten_glGetActiveUniform = ( + program, + index, + bufSize, + length, + size, + type, + name + ) => + __glGetActiveAttribOrUniform( + 'getActiveUniform', + program, + index, + bufSize, + length, + size, + type, + name + ); + _emscripten_glGetActiveUniform.sig = 'viiipppp'; + + var _emscripten_glGetAttachedShaders = ( + program, + maxCount, + count, + shaders + ) => { + var result = GLctx.getAttachedShaders(GL.programs[program]); + var len = result.length; + if (len > maxCount) { + len = maxCount; + } + HEAP32[count >> 2] = len; + for (var i = 0; i < len; ++i) { + var id = GL.shaders.indexOf(result[i]); + HEAP32[(shaders + i * 4) >> 2] = id; + } + }; + _emscripten_glGetAttachedShaders.sig = 'viipp'; + + var _emscripten_glGetAttribLocation = (program, name) => + GLctx.getAttribLocation(GL.programs[program], UTF8ToString(name)); + _emscripten_glGetAttribLocation.sig = 'iip'; + + var writeI53ToI64 = (ptr, num) => { + HEAPU32[ptr >> 2] = num; + var lower = HEAPU32[ptr >> 2]; + HEAPU32[(ptr + 4) >> 2] = (num - lower) / 4294967296; + }; + + var emscriptenWebGLGet = (name_, p, type) => { + // Guard against user passing a null pointer. + // Note that GLES2 spec does not say anything about how passing a null + // pointer should be treated. Testing on desktop core GL 3, the application + // crashes on glGetIntegerv to a null pointer, but better to report an error + // instead of doing anything random. + if (!p) { + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + var ret = undefined; + switch ( + name_ // Handle a few trivial GLES values + ) { + case 0x8dfa: // GL_SHADER_COMPILER + ret = 1; + break; + case 0x8df8: // GL_SHADER_BINARY_FORMATS + if (type != 0 && type != 1) { + GL.recordError(0x500); // GL_INVALID_ENUM + } + // Do not write anything to the out pointer, since no binary formats are + // supported. + return; + case 0x8df9: // GL_NUM_SHADER_BINARY_FORMATS + ret = 0; + break; + case 0x86a2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS + // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete + // since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be + // queried for length), so implement it ourselves to allow C++ GLES2 + // code get the length. + var formats = GLctx.getParameter( + 0x86a3 /*GL_COMPRESSED_TEXTURE_FORMATS*/ + ); + ret = formats ? formats.length : 0; + break; + } + + if (ret === undefined) { + var result = GLctx.getParameter(name_); + switch (typeof result) { + case 'number': + ret = result; + break; + case 'boolean': + ret = result ? 1 : 0; + break; + case 'string': + GL.recordError(0x500); // GL_INVALID_ENUM + return; + case 'object': + if (result === null) { + // null is a valid result for some (e.g., which buffer is bound - + // perhaps nothing is bound), but otherwise can mean an invalid + // name_, which we need to report as an error + switch (name_) { + case 0x8894: // ARRAY_BUFFER_BINDING + case 0x8b8d: // CURRENT_PROGRAM + case 0x8895: // ELEMENT_ARRAY_BUFFER_BINDING + case 0x8ca6: // FRAMEBUFFER_BINDING or DRAW_FRAMEBUFFER_BINDING + case 0x8ca7: // RENDERBUFFER_BINDING + case 0x8069: // TEXTURE_BINDING_2D + case 0x85b5: // WebGL 2 GL_VERTEX_ARRAY_BINDING, or WebGL 1 extension OES_vertex_array_object GL_VERTEX_ARRAY_BINDING_OES + case 0x8514: { + // TEXTURE_BINDING_CUBE_MAP + ret = 0; + break; + } + default: { + GL.recordError(0x500); // GL_INVALID_ENUM + return; + } + } + } else if ( + result instanceof Float32Array || + result instanceof Uint32Array || + result instanceof Int32Array || + result instanceof Array + ) { + for (var i = 0; i < result.length; ++i) { + switch (type) { + case 0: + HEAP32[(p + i * 4) >> 2] = result[i]; + break; + case 2: + HEAPF32[(p + i * 4) >> 2] = result[i]; + break; + case 4: + HEAP8[p + i] = result[i] ? 1 : 0; + break; + } + } + return; + } else { + try { + ret = result.name | 0; + } catch (e) { + GL.recordError(0x500); // GL_INVALID_ENUM + err( + `GL_INVALID_ENUM in glGet${type}v: Unknown object returned from WebGL getParameter(${name_})! (error: ${e})` + ); + return; + } + } + break; + default: + GL.recordError(0x500); // GL_INVALID_ENUM + err( + `GL_INVALID_ENUM in glGet${type}v: Native code calling glGet${type}v(${name_}) and it returns ${result} of type ${typeof result}!` + ); + return; + } + } + + switch (type) { + case 1: + writeI53ToI64(p, ret); + break; + case 0: + HEAP32[p >> 2] = ret; + break; + case 2: + HEAPF32[p >> 2] = ret; + break; + case 4: + HEAP8[p] = ret ? 1 : 0; + break; + } + }; + + var _emscripten_glGetBooleanv = (name_, p) => + emscriptenWebGLGet(name_, p, 4); + _emscripten_glGetBooleanv.sig = 'vip'; + + var _emscripten_glGetBufferParameteriv = (target, value, data) => { + if (!data) { + // GLES2 specification does not specify how to behave if data is a null + // pointer. Since calling this function does not make sense if data == + // null, issue a GL error to notify user about it. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + HEAP32[data >> 2] = GLctx.getBufferParameter(target, value); + }; + _emscripten_glGetBufferParameteriv.sig = 'viip'; + + var _emscripten_glGetError = () => { + var error = GLctx.getError() || GL.lastError; + GL.lastError = 0 /*GL_NO_ERROR*/; + return error; + }; + _emscripten_glGetError.sig = 'i'; + + var _emscripten_glGetFloatv = (name_, p) => emscriptenWebGLGet(name_, p, 2); + _emscripten_glGetFloatv.sig = 'vip'; + + var _emscripten_glGetFramebufferAttachmentParameteriv = ( + target, + attachment, + pname, + params + ) => { + var result = GLctx.getFramebufferAttachmentParameter( + target, + attachment, + pname + ); + if ( + result instanceof WebGLRenderbuffer || + result instanceof WebGLTexture + ) { + result = result.name | 0; + } + HEAP32[params >> 2] = result; + }; + _emscripten_glGetFramebufferAttachmentParameteriv.sig = 'viiip'; + + var _emscripten_glGetIntegerv = (name_, p) => + emscriptenWebGLGet(name_, p, 0); + _emscripten_glGetIntegerv.sig = 'vip'; + + var _emscripten_glGetProgramInfoLog = ( + program, + maxLength, + length, + infoLog + ) => { + var log = GLctx.getProgramInfoLog(GL.programs[program]); + if (log === null) log = '(unknown error)'; + var numBytesWrittenExclNull = + maxLength > 0 && infoLog + ? stringToUTF8(log, infoLog, maxLength) + : 0; + if (length) HEAP32[length >> 2] = numBytesWrittenExclNull; + }; + _emscripten_glGetProgramInfoLog.sig = 'viipp'; + + var _emscripten_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(0x501 /* GL_INVALID_VALUE */); + return; + } + + if (program >= GL.counter) { + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + + program = GL.programs[program]; + + if (pname == 0x8b84) { + // GL_INFO_LOG_LENGTH + var log = GLctx.getProgramInfoLog(program); + if (log === null) log = '(unknown error)'; + HEAP32[p >> 2] = log.length + 1; + } else if (pname == 0x8b87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) { + if (!program.maxUniformLength) { + var numActiveUniforms = GLctx.getProgramParameter( + program, + 0x8b86 /*GL_ACTIVE_UNIFORMS*/ + ); + for (var i = 0; i < numActiveUniforms; ++i) { + program.maxUniformLength = Math.max( + program.maxUniformLength, + GLctx.getActiveUniform(program, i).name.length + 1 + ); + } + } + HEAP32[p >> 2] = program.maxUniformLength; + } else if (pname == 0x8b8a /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { + if (!program.maxAttributeLength) { + var numActiveAttributes = GLctx.getProgramParameter( + program, + 0x8b89 /*GL_ACTIVE_ATTRIBUTES*/ + ); + for (var i = 0; i < numActiveAttributes; ++i) { + program.maxAttributeLength = Math.max( + program.maxAttributeLength, + GLctx.getActiveAttrib(program, i).name.length + 1 + ); + } + } + HEAP32[p >> 2] = program.maxAttributeLength; + } else if ( + pname == 0x8a35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */ + ) { + if (!program.maxUniformBlockNameLength) { + var numActiveUniformBlocks = GLctx.getProgramParameter( + program, + 0x8a36 /*GL_ACTIVE_UNIFORM_BLOCKS*/ + ); + for (var i = 0; i < numActiveUniformBlocks; ++i) { + program.maxUniformBlockNameLength = Math.max( + program.maxUniformBlockNameLength, + GLctx.getActiveUniformBlockName(program, i).length + 1 + ); + } + } + HEAP32[p >> 2] = program.maxUniformBlockNameLength; + } else { + HEAP32[p >> 2] = GLctx.getProgramParameter(program, pname); + } + }; + _emscripten_glGetProgramiv.sig = 'viip'; + + var _emscripten_glGetQueryObjecti64vEXT = (id, pname, params) => { + if (!params) { + // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); + return; + } + var query = GL.queries[id]; + var param; + { + param = GLctx.disjointTimerQueryExt['getQueryObjectEXT']( + query, + pname + ); + } + var ret; + if (typeof param == 'boolean') { + ret = param ? 1 : 0; + } else { + ret = param; + } + writeI53ToI64(params, ret); + }; + _emscripten_glGetQueryObjecti64vEXT.sig = 'viip'; + + var _emscripten_glGetQueryObjectivEXT = (id, pname, params) => { + if (!params) { + // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); + return; + } + var query = GL.queries[id]; + var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT']( + query, + pname + ); + var ret; + if (typeof param == 'boolean') { + ret = param ? 1 : 0; + } else { + ret = param; + } + HEAP32[params >> 2] = ret; + }; + _emscripten_glGetQueryObjectivEXT.sig = 'viip'; + + var _emscripten_glGetQueryObjectui64vEXT = + _emscripten_glGetQueryObjecti64vEXT; + + var _emscripten_glGetQueryObjectuivEXT = _emscripten_glGetQueryObjectivEXT; + + var _emscripten_glGetQueryivEXT = (target, pname, params) => { + if (!params) { + // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); + return; + } + HEAP32[params >> 2] = GLctx.disjointTimerQueryExt['getQueryEXT']( + target, + pname + ); + }; + _emscripten_glGetQueryivEXT.sig = 'viip'; + + var _emscripten_glGetRenderbufferParameteriv = (target, pname, params) => { + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if params == null, issue a GL error to notify user about it. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + HEAP32[params >> 2] = GLctx.getRenderbufferParameter(target, pname); + }; + _emscripten_glGetRenderbufferParameteriv.sig = 'viip'; + + var _emscripten_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) HEAP32[length >> 2] = numBytesWrittenExclNull; + }; + _emscripten_glGetShaderInfoLog.sig = 'viipp'; + + var _emscripten_glGetShaderPrecisionFormat = ( + shaderType, + precisionType, + range, + precision + ) => { + var result = GLctx.getShaderPrecisionFormat(shaderType, precisionType); + HEAP32[range >> 2] = result.rangeMin; + HEAP32[(range + 4) >> 2] = result.rangeMax; + HEAP32[precision >> 2] = result.precision; + }; + _emscripten_glGetShaderPrecisionFormat.sig = 'viipp'; + + var _emscripten_glGetShaderSource = (shader, bufSize, length, source) => { + var result = GLctx.getShaderSource(GL.shaders[shader]); + if (!result) return; // If an error occurs, nothing will be written to length or source. + var numBytesWrittenExclNull = + bufSize > 0 && source ? stringToUTF8(result, source, bufSize) : 0; + if (length) HEAP32[length >> 2] = numBytesWrittenExclNull; + }; + _emscripten_glGetShaderSource.sig = 'viipp'; + + var _emscripten_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(0x501 /* GL_INVALID_VALUE */); + return; + } + if (pname == 0x8b84) { + // 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; + HEAP32[p >> 2] = logLength; + } else if (pname == 0x8b88) { + // 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; + HEAP32[p >> 2] = sourceLength; + } else { + HEAP32[p >> 2] = GLctx.getShaderParameter( + GL.shaders[shader], + pname + ); + } + }; + _emscripten_glGetShaderiv.sig = 'viip'; + + var webglGetExtensions = () => { + var exts = getEmscriptenSupportedExtensions(GLctx); + exts = exts.concat(exts.map((e) => 'GL_' + e)); + return exts; + }; + + var _emscripten_glGetString = (name_) => { + var ret = GL.stringCache[name_]; + if (!ret) { + switch (name_) { + case 0x1f03 /* GL_EXTENSIONS */: + ret = stringToNewUTF8(webglGetExtensions().join(' ')); + break; + case 0x1f00 /* GL_VENDOR */: + case 0x1f01 /* GL_RENDERER */: + case 0x9245 /* UNMASKED_VENDOR_WEBGL */: + case 0x9246 /* UNMASKED_RENDERER_WEBGL */: + var s = GLctx.getParameter(name_); + if (!s) { + GL.recordError(0x500 /*GL_INVALID_ENUM*/); + } + ret = s ? stringToNewUTF8(s) : 0; + break; + + case 0x1f02 /* GL_VERSION */: + var webGLVersion = GLctx.getParameter( + 0x1f02 /*GL_VERSION*/ + ); + // return GLES version string corresponding to the version of the WebGL context + var glVersion = `OpenGL ES 2.0 (${webGLVersion})`; + ret = stringToNewUTF8(glVersion); + break; + case 0x8b8c /* GL_SHADING_LANGUAGE_VERSION */: + var glslVersion = GLctx.getParameter( + 0x8b8c /*GL_SHADING_LANGUAGE_VERSION*/ + ); + // extract the version number 'N.M' from the string 'WebGL GLSL ES N.M ...' + var ver_re = /^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/; + var ver_num = glslVersion.match(ver_re); + if (ver_num !== null) { + if (ver_num[1].length == 3) + ver_num[1] = ver_num[1] + '0'; // ensure minor version has 2 digits + glslVersion = `OpenGL ES GLSL ES ${ver_num[1]} (${glslVersion})`; + } + ret = stringToNewUTF8(glslVersion); + break; + default: + GL.recordError(0x500 /*GL_INVALID_ENUM*/); + // fall through + } + GL.stringCache[name_] = ret; + } + return ret; + }; + _emscripten_glGetString.sig = 'pi'; + + var _emscripten_glGetTexParameterfv = (target, pname, params) => { + if (!params) { + // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); + return; + } + HEAPF32[params >> 2] = GLctx.getTexParameter(target, pname); + }; + _emscripten_glGetTexParameterfv.sig = 'viip'; + + var _emscripten_glGetTexParameteriv = (target, pname, params) => { + if (!params) { + // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); + return; + } + HEAP32[params >> 2] = GLctx.getTexParameter(target, pname); + }; + _emscripten_glGetTexParameteriv.sig = 'viip'; + + /** @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, + 0x8b86 /*GL_ACTIVE_UNIFORMS*/ + ); + 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 _emscripten_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(0x501 /* GL_INVALID_VALUE */); + } + return -1; + }; + _emscripten_glGetUniformLocation.sig = 'iip'; + + 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(0x502 /*GL_INVALID_OPERATION*/); + } + }; + + /** @suppress{checkTypes} */ + var emscriptenWebGLGetUniform = (program, location, params, type) => { + if (!params) { + // GLES2 specification does not specify how to behave if params is a null + // pointer. Since calling this function does not make sense if params == + // null, issue a GL error to notify user about it. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + program = GL.programs[program]; + webglPrepareUniformLocationsBeforeFirstUse(program); + var data = GLctx.getUniform(program, webglGetUniformLocation(location)); + if (typeof data == 'number' || typeof data == 'boolean') { + switch (type) { + case 0: + HEAP32[params >> 2] = data; + break; + case 2: + HEAPF32[params >> 2] = data; + break; + } + } else { + for (var i = 0; i < data.length; i++) { + switch (type) { + case 0: + HEAP32[(params + i * 4) >> 2] = data[i]; + break; + case 2: + HEAPF32[(params + i * 4) >> 2] = data[i]; + break; + } + } + } + }; + + var _emscripten_glGetUniformfv = (program, location, params) => { + emscriptenWebGLGetUniform(program, location, params, 2); + }; + _emscripten_glGetUniformfv.sig = 'viip'; + + var _emscripten_glGetUniformiv = (program, location, params) => { + emscriptenWebGLGetUniform(program, location, params, 0); + }; + _emscripten_glGetUniformiv.sig = 'viip'; + + var _emscripten_glGetVertexAttribPointerv = (index, pname, pointer) => { + if (!pointer) { + // GLES2 specification does not specify how to behave if pointer is a null + // pointer. Since calling this function does not make sense if pointer == + // null, issue a GL error to notify user about it. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + HEAP32[pointer >> 2] = GLctx.getVertexAttribOffset(index, pname); + }; + _emscripten_glGetVertexAttribPointerv.sig = 'viip'; + + /** @suppress{checkTypes} */ + var emscriptenWebGLGetVertexAttrib = (index, pname, params, type) => { + if (!params) { + // GLES2 specification does not specify how to behave if params is a null + // pointer. Since calling this function does not make sense if params == + // null, issue a GL error to notify user about it. + GL.recordError(0x501 /* GL_INVALID_VALUE */); + return; + } + var data = GLctx.getVertexAttrib(index, pname); + if (pname == 0x889f /*VERTEX_ATTRIB_ARRAY_BUFFER_BINDING*/) { + HEAP32[params >> 2] = data && data['name']; + } else if (typeof data == 'number' || typeof data == 'boolean') { + switch (type) { + case 0: + HEAP32[params >> 2] = data; + break; + case 2: + HEAPF32[params >> 2] = data; + break; + case 5: + HEAP32[params >> 2] = Math.fround(data); + break; + } + } else { + for (var i = 0; i < data.length; i++) { + switch (type) { + case 0: + HEAP32[(params + i * 4) >> 2] = data[i]; + break; + case 2: + HEAPF32[(params + i * 4) >> 2] = data[i]; + break; + case 5: + HEAP32[(params + i * 4) >> 2] = Math.fround(data[i]); + break; + } + } + } + }; + + var _emscripten_glGetVertexAttribfv = (index, pname, params) => { + // N.B. This function may only be called if the vertex attribute was + // specified using the function glVertexAttrib*f(), otherwise the results + // are undefined. (GLES3 spec 6.1.12) + emscriptenWebGLGetVertexAttrib(index, pname, params, 2); + }; + _emscripten_glGetVertexAttribfv.sig = 'viip'; + + var _emscripten_glGetVertexAttribiv = (index, pname, params) => { + // N.B. This function may only be called if the vertex attribute was + // specified using the function glVertexAttrib*f(), otherwise the results + // are undefined. (GLES3 spec 6.1.12) + emscriptenWebGLGetVertexAttrib(index, pname, params, 5); + }; + _emscripten_glGetVertexAttribiv.sig = 'viip'; + + var _emscripten_glHint = (x0, x1) => GLctx.hint(x0, x1); + _emscripten_glHint.sig = 'vii'; + + var _emscripten_glIsBuffer = (buffer) => { + var b = GL.buffers[buffer]; + if (!b) return 0; + return GLctx.isBuffer(b); + }; + _emscripten_glIsBuffer.sig = 'ii'; + + var _emscripten_glIsEnabled = (x0) => GLctx.isEnabled(x0); + _emscripten_glIsEnabled.sig = 'ii'; + + var _emscripten_glIsFramebuffer = (framebuffer) => { + var fb = GL.framebuffers[framebuffer]; + if (!fb) return 0; + return GLctx.isFramebuffer(fb); + }; + _emscripten_glIsFramebuffer.sig = 'ii'; + + var _emscripten_glIsProgram = (program) => { + program = GL.programs[program]; + if (!program) return 0; + return GLctx.isProgram(program); + }; + _emscripten_glIsProgram.sig = 'ii'; + + var _emscripten_glIsQueryEXT = (id) => { + var query = GL.queries[id]; + if (!query) return 0; + return GLctx.disjointTimerQueryExt['isQueryEXT'](query); + }; + _emscripten_glIsQueryEXT.sig = 'ii'; + + var _emscripten_glIsRenderbuffer = (renderbuffer) => { + var rb = GL.renderbuffers[renderbuffer]; + if (!rb) return 0; + return GLctx.isRenderbuffer(rb); + }; + _emscripten_glIsRenderbuffer.sig = 'ii'; + + var _emscripten_glIsShader = (shader) => { + var s = GL.shaders[shader]; + if (!s) return 0; + return GLctx.isShader(s); + }; + _emscripten_glIsShader.sig = 'ii'; + + var _emscripten_glIsTexture = (id) => { + var texture = GL.textures[id]; + if (!texture) return 0; + return GLctx.isTexture(texture); + }; + _emscripten_glIsTexture.sig = 'ii'; + + var _emscripten_glIsVertexArray = (array) => { + var vao = GL.vaos[array]; + if (!vao) return 0; + return GLctx.isVertexArray(vao); + }; + _emscripten_glIsVertexArray.sig = 'ii'; + var _emscripten_glIsVertexArrayOES = _emscripten_glIsVertexArray; + _emscripten_glIsVertexArrayOES.sig = 'ii'; + + var _emscripten_glLineWidth = (x0) => GLctx.lineWidth(x0); + _emscripten_glLineWidth.sig = 'vf'; + + var _emscripten_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 = {}; + }; + _emscripten_glLinkProgram.sig = 'vi'; + + var _emscripten_glPixelStorei = (pname, param) => { + if (pname == 3317) { + GL.unpackAlignment = param; + } else if (pname == 3314) { + GL.unpackRowLength = param; + } + GLctx.pixelStorei(pname, param); + }; + _emscripten_glPixelStorei.sig = 'vii'; + + var _emscripten_glPolygonModeWEBGL = (face, mode) => { + GLctx.webglPolygonMode['polygonModeWEBGL'](face, mode); + }; + _emscripten_glPolygonModeWEBGL.sig = 'vii'; + + var _emscripten_glPolygonOffset = (x0, x1) => GLctx.polygonOffset(x0, x1); + _emscripten_glPolygonOffset.sig = 'vff'; + + var _emscripten_glPolygonOffsetClampEXT = (factor, units, clamp) => { + GLctx.extPolygonOffsetClamp['polygonOffsetClampEXT']( + factor, + units, + clamp + ); + }; + _emscripten_glPolygonOffsetClampEXT.sig = 'vfff'; + + var _emscripten_glQueryCounterEXT = (id, target) => { + GLctx.disjointTimerQueryExt['queryCounterEXT'](GL.queries[id], target); + }; + _emscripten_glQueryCounterEXT.sig = 'vii'; + + 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, + }; + return colorChannels[format - 0x1902] || 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 -= 0x1400; + + if (type == 1) return HEAPU8; + + if (type == 4) return HEAP32; + + if (type == 6) return HEAPF32; + + if (type == 5 || type == 28922) return HEAPU32; + + return HEAPU16; + }; + + 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 _emscripten_glReadPixels = ( + x, + y, + width, + height, + format, + type, + pixels + ) => { + var pixelData = emscriptenWebGLGetTexPixelData( + type, + format, + width, + height, + pixels, + format + ); + if (!pixelData) { + GL.recordError(0x500 /*GL_INVALID_ENUM*/); + return; + } + GLctx.readPixels(x, y, width, height, format, type, pixelData); + }; + _emscripten_glReadPixels.sig = 'viiiiiip'; + + var _emscripten_glReleaseShaderCompiler = () => { + // NOP (as allowed by GLES 2.0 spec) + }; + _emscripten_glReleaseShaderCompiler.sig = 'v'; + + var _emscripten_glRenderbufferStorage = (x0, x1, x2, x3) => + GLctx.renderbufferStorage(x0, x1, x2, x3); + _emscripten_glRenderbufferStorage.sig = 'viiii'; + + var _emscripten_glSampleCoverage = (value, invert) => { + GLctx.sampleCoverage(value, !!invert); + }; + _emscripten_glSampleCoverage.sig = 'vfi'; + + var _emscripten_glScissor = (x0, x1, x2, x3) => + GLctx.scissor(x0, x1, x2, x3); + _emscripten_glScissor.sig = 'viiii'; + + var _emscripten_glShaderBinary = ( + count, + shaders, + binaryformat, + binary, + length + ) => { + GL.recordError(0x500 /*GL_INVALID_ENUM*/); + }; + _emscripten_glShaderBinary.sig = 'vipipi'; + + var _emscripten_glShaderSource = (shader, count, string, length) => { + var source = GL.getSource(shader, count, string, length); + + GLctx.shaderSource(GL.shaders[shader], source); + }; + _emscripten_glShaderSource.sig = 'viipp'; + + var _emscripten_glStencilFunc = (x0, x1, x2) => + GLctx.stencilFunc(x0, x1, x2); + _emscripten_glStencilFunc.sig = 'viii'; + + var _emscripten_glStencilFuncSeparate = (x0, x1, x2, x3) => + GLctx.stencilFuncSeparate(x0, x1, x2, x3); + _emscripten_glStencilFuncSeparate.sig = 'viiii'; + + var _emscripten_glStencilMask = (x0) => GLctx.stencilMask(x0); + _emscripten_glStencilMask.sig = 'vi'; + + var _emscripten_glStencilMaskSeparate = (x0, x1) => + GLctx.stencilMaskSeparate(x0, x1); + _emscripten_glStencilMaskSeparate.sig = 'vii'; + + var _emscripten_glStencilOp = (x0, x1, x2) => GLctx.stencilOp(x0, x1, x2); + _emscripten_glStencilOp.sig = 'viii'; + + var _emscripten_glStencilOpSeparate = (x0, x1, x2, x3) => + GLctx.stencilOpSeparate(x0, x1, x2, x3); + _emscripten_glStencilOpSeparate.sig = 'viiii'; + + var _emscripten_glTexImage2D = ( + target, + level, + internalFormat, + width, + height, + border, + format, + type, + pixels + ) => { + var pixelData = pixels + ? emscriptenWebGLGetTexPixelData( + type, + format, + width, + height, + pixels, + internalFormat + ) + : null; + GLctx.texImage2D( + target, + level, + internalFormat, + width, + height, + border, + format, + type, + pixelData + ); + }; + _emscripten_glTexImage2D.sig = 'viiiiiiiip'; + + var _emscripten_glTexParameterf = (x0, x1, x2) => + GLctx.texParameterf(x0, x1, x2); + _emscripten_glTexParameterf.sig = 'viif'; + + var _emscripten_glTexParameterfv = (target, pname, params) => { + var param = HEAPF32[params >> 2]; + GLctx.texParameterf(target, pname, param); + }; + _emscripten_glTexParameterfv.sig = 'viip'; + + var _emscripten_glTexParameteri = (x0, x1, x2) => + GLctx.texParameteri(x0, x1, x2); + _emscripten_glTexParameteri.sig = 'viii'; + + var _emscripten_glTexParameteriv = (target, pname, params) => { + var param = HEAP32[params >> 2]; + GLctx.texParameteri(target, pname, param); + }; + _emscripten_glTexParameteriv.sig = 'viip'; + + var _emscripten_glTexSubImage2D = ( + target, + level, + xoffset, + yoffset, + width, + height, + format, + type, + pixels + ) => { + var pixelData = pixels + ? emscriptenWebGLGetTexPixelData( + type, + format, + width, + height, + pixels, + 0 + ) + : null; + GLctx.texSubImage2D( + target, + level, + xoffset, + yoffset, + width, + height, + format, + type, + pixelData + ); + }; + _emscripten_glTexSubImage2D.sig = 'viiiiiiiip'; + + var _emscripten_glUniform1f = (location, v0) => { + GLctx.uniform1f(webglGetUniformLocation(location), v0); + }; + _emscripten_glUniform1f.sig = 'vif'; + + var miniTempWebGLFloatBuffers = []; + + var _emscripten_glUniform1fv = (location, count, value) => { + if (count <= 288) { + // avoid allocation when uploading few enough uniforms + var view = miniTempWebGLFloatBuffers[count]; + for (var i = 0; i < count; ++i) { + view[i] = HEAPF32[(value + 4 * i) >> 2]; + } + } else { + var view = HEAPF32.subarray(value >> 2, (value + count * 4) >> 2); + } + GLctx.uniform1fv(webglGetUniformLocation(location), view); + }; + _emscripten_glUniform1fv.sig = 'viip'; + + var _emscripten_glUniform1i = (location, v0) => { + GLctx.uniform1i(webglGetUniformLocation(location), v0); + }; + _emscripten_glUniform1i.sig = 'vii'; + + var miniTempWebGLIntBuffers = []; + + var _emscripten_glUniform1iv = (location, count, value) => { + if (count <= 288) { + // avoid allocation when uploading few enough uniforms + var view = miniTempWebGLIntBuffers[count]; + for (var i = 0; i < count; ++i) { + view[i] = HEAP32[(value + 4 * i) >> 2]; + } + } else { + var view = HEAP32.subarray(value >> 2, (value + count * 4) >> 2); + } + GLctx.uniform1iv(webglGetUniformLocation(location), view); + }; + _emscripten_glUniform1iv.sig = 'viip'; + + var _emscripten_glUniform2f = (location, v0, v1) => { + GLctx.uniform2f(webglGetUniformLocation(location), v0, v1); + }; + _emscripten_glUniform2f.sig = 'viff'; + + var _emscripten_glUniform2fv = (location, count, value) => { + if (count <= 144) { + // avoid allocation when uploading few enough uniforms + count *= 2; + var view = miniTempWebGLFloatBuffers[count]; + for (var i = 0; i < count; i += 2) { + view[i] = HEAPF32[(value + 4 * i) >> 2]; + view[i + 1] = HEAPF32[(value + (4 * i + 4)) >> 2]; + } + } else { + var view = HEAPF32.subarray(value >> 2, (value + count * 8) >> 2); + } + GLctx.uniform2fv(webglGetUniformLocation(location), view); + }; + _emscripten_glUniform2fv.sig = 'viip'; + + var _emscripten_glUniform2i = (location, v0, v1) => { + GLctx.uniform2i(webglGetUniformLocation(location), v0, v1); + }; + _emscripten_glUniform2i.sig = 'viii'; + + var _emscripten_glUniform2iv = (location, count, value) => { + if (count <= 144) { + // avoid allocation when uploading few enough uniforms + count *= 2; + var view = miniTempWebGLIntBuffers[count]; + for (var i = 0; i < count; i += 2) { + view[i] = HEAP32[(value + 4 * i) >> 2]; + view[i + 1] = HEAP32[(value + (4 * i + 4)) >> 2]; + } + } else { + var view = HEAP32.subarray(value >> 2, (value + count * 8) >> 2); + } + GLctx.uniform2iv(webglGetUniformLocation(location), view); + }; + _emscripten_glUniform2iv.sig = 'viip'; + + var _emscripten_glUniform3f = (location, v0, v1, v2) => { + GLctx.uniform3f(webglGetUniformLocation(location), v0, v1, v2); + }; + _emscripten_glUniform3f.sig = 'vifff'; + + var _emscripten_glUniform3fv = (location, count, value) => { + if (count <= 96) { + // avoid allocation when uploading few enough uniforms + count *= 3; + var view = miniTempWebGLFloatBuffers[count]; + for (var i = 0; i < count; i += 3) { + view[i] = HEAPF32[(value + 4 * i) >> 2]; + view[i + 1] = HEAPF32[(value + (4 * i + 4)) >> 2]; + view[i + 2] = HEAPF32[(value + (4 * i + 8)) >> 2]; + } + } else { + var view = HEAPF32.subarray(value >> 2, (value + count * 12) >> 2); + } + GLctx.uniform3fv(webglGetUniformLocation(location), view); + }; + _emscripten_glUniform3fv.sig = 'viip'; + + var _emscripten_glUniform3i = (location, v0, v1, v2) => { + GLctx.uniform3i(webglGetUniformLocation(location), v0, v1, v2); + }; + _emscripten_glUniform3i.sig = 'viiii'; + + var _emscripten_glUniform3iv = (location, count, value) => { + if (count <= 96) { + // avoid allocation when uploading few enough uniforms + count *= 3; + var view = miniTempWebGLIntBuffers[count]; + for (var i = 0; i < count; i += 3) { + view[i] = HEAP32[(value + 4 * i) >> 2]; + view[i + 1] = HEAP32[(value + (4 * i + 4)) >> 2]; + view[i + 2] = HEAP32[(value + (4 * i + 8)) >> 2]; + } + } else { + var view = HEAP32.subarray(value >> 2, (value + count * 12) >> 2); + } + GLctx.uniform3iv(webglGetUniformLocation(location), view); + }; + _emscripten_glUniform3iv.sig = 'viip'; + + var _emscripten_glUniform4f = (location, v0, v1, v2, v3) => { + GLctx.uniform4f(webglGetUniformLocation(location), v0, v1, v2, v3); + }; + _emscripten_glUniform4f.sig = 'viffff'; + + var _emscripten_glUniform4fv = (location, count, value) => { + if (count <= 72) { + // avoid allocation when uploading few enough uniforms + var view = miniTempWebGLFloatBuffers[4 * count]; + // hoist the heap out of the loop for size and for pthreads+growth. + var heap = HEAPF32; + value = value >> 2; + count *= 4; + for (var i = 0; i < count; i += 4) { + 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]; + } + } else { + var view = HEAPF32.subarray(value >> 2, (value + count * 16) >> 2); + } + GLctx.uniform4fv(webglGetUniformLocation(location), view); + }; + _emscripten_glUniform4fv.sig = 'viip'; + + var _emscripten_glUniform4i = (location, v0, v1, v2, v3) => { + GLctx.uniform4i(webglGetUniformLocation(location), v0, v1, v2, v3); + }; + _emscripten_glUniform4i.sig = 'viiiii'; + + var _emscripten_glUniform4iv = (location, count, value) => { + if (count <= 72) { + // avoid allocation when uploading few enough uniforms + count *= 4; + var view = miniTempWebGLIntBuffers[count]; + for (var i = 0; i < count; i += 4) { + view[i] = HEAP32[(value + 4 * i) >> 2]; + view[i + 1] = HEAP32[(value + (4 * i + 4)) >> 2]; + view[i + 2] = HEAP32[(value + (4 * i + 8)) >> 2]; + view[i + 3] = HEAP32[(value + (4 * i + 12)) >> 2]; + } + } else { + var view = HEAP32.subarray(value >> 2, (value + count * 16) >> 2); + } + GLctx.uniform4iv(webglGetUniformLocation(location), view); + }; + _emscripten_glUniform4iv.sig = 'viip'; + + var _emscripten_glUniformMatrix2fv = ( + location, + count, + transpose, + value + ) => { + if (count <= 72) { + // avoid allocation when uploading few enough uniforms + count *= 4; + var view = miniTempWebGLFloatBuffers[count]; + for (var i = 0; i < count; i += 4) { + view[i] = HEAPF32[(value + 4 * i) >> 2]; + view[i + 1] = HEAPF32[(value + (4 * i + 4)) >> 2]; + view[i + 2] = HEAPF32[(value + (4 * i + 8)) >> 2]; + view[i + 3] = HEAPF32[(value + (4 * i + 12)) >> 2]; + } + } else { + var view = HEAPF32.subarray(value >> 2, (value + count * 16) >> 2); + } + GLctx.uniformMatrix2fv( + webglGetUniformLocation(location), + !!transpose, + view + ); + }; + _emscripten_glUniformMatrix2fv.sig = 'viiip'; + + var _emscripten_glUniformMatrix3fv = ( + location, + count, + transpose, + value + ) => { + if (count <= 32) { + // avoid allocation when uploading few enough uniforms + count *= 9; + var view = miniTempWebGLFloatBuffers[count]; + for (var i = 0; i < count; i += 9) { + view[i] = HEAPF32[(value + 4 * i) >> 2]; + view[i + 1] = HEAPF32[(value + (4 * i + 4)) >> 2]; + view[i + 2] = HEAPF32[(value + (4 * i + 8)) >> 2]; + view[i + 3] = HEAPF32[(value + (4 * i + 12)) >> 2]; + view[i + 4] = HEAPF32[(value + (4 * i + 16)) >> 2]; + view[i + 5] = HEAPF32[(value + (4 * i + 20)) >> 2]; + view[i + 6] = HEAPF32[(value + (4 * i + 24)) >> 2]; + view[i + 7] = HEAPF32[(value + (4 * i + 28)) >> 2]; + view[i + 8] = HEAPF32[(value + (4 * i + 32)) >> 2]; + } + } else { + var view = HEAPF32.subarray(value >> 2, (value + count * 36) >> 2); + } + GLctx.uniformMatrix3fv( + webglGetUniformLocation(location), + !!transpose, + view + ); + }; + _emscripten_glUniformMatrix3fv.sig = 'viiip'; + + var _emscripten_glUniformMatrix4fv = ( + location, + count, + transpose, + value + ) => { + 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 = HEAPF32; + 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 = HEAPF32.subarray(value >> 2, (value + count * 64) >> 2); + } + GLctx.uniformMatrix4fv( + webglGetUniformLocation(location), + !!transpose, + view + ); + }; + _emscripten_glUniformMatrix4fv.sig = 'viiip'; + + var _emscripten_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; + }; + _emscripten_glUseProgram.sig = 'vi'; + + var _emscripten_glValidateProgram = (program) => { + GLctx.validateProgram(GL.programs[program]); + }; + _emscripten_glValidateProgram.sig = 'vi'; + + var _emscripten_glVertexAttrib1f = (x0, x1) => GLctx.vertexAttrib1f(x0, x1); + _emscripten_glVertexAttrib1f.sig = 'vif'; + + var _emscripten_glVertexAttrib1fv = (index, v) => { + GLctx.vertexAttrib1f(index, HEAPF32[v >> 2]); + }; + _emscripten_glVertexAttrib1fv.sig = 'vip'; + + var _emscripten_glVertexAttrib2f = (x0, x1, x2) => + GLctx.vertexAttrib2f(x0, x1, x2); + _emscripten_glVertexAttrib2f.sig = 'viff'; + + var _emscripten_glVertexAttrib2fv = (index, v) => { + GLctx.vertexAttrib2f(index, HEAPF32[v >> 2], HEAPF32[(v + 4) >> 2]); + }; + _emscripten_glVertexAttrib2fv.sig = 'vip'; + + var _emscripten_glVertexAttrib3f = (x0, x1, x2, x3) => + GLctx.vertexAttrib3f(x0, x1, x2, x3); + _emscripten_glVertexAttrib3f.sig = 'vifff'; + + var _emscripten_glVertexAttrib3fv = (index, v) => { + GLctx.vertexAttrib3f( + index, + HEAPF32[v >> 2], + HEAPF32[(v + 4) >> 2], + HEAPF32[(v + 8) >> 2] + ); + }; + _emscripten_glVertexAttrib3fv.sig = 'vip'; + + var _emscripten_glVertexAttrib4f = (x0, x1, x2, x3, x4) => + GLctx.vertexAttrib4f(x0, x1, x2, x3, x4); + _emscripten_glVertexAttrib4f.sig = 'viffff'; + + var _emscripten_glVertexAttrib4fv = (index, v) => { + GLctx.vertexAttrib4f( + index, + HEAPF32[v >> 2], + HEAPF32[(v + 4) >> 2], + HEAPF32[(v + 8) >> 2], + HEAPF32[(v + 12) >> 2] + ); + }; + _emscripten_glVertexAttrib4fv.sig = 'vip'; + + var _emscripten_glVertexAttribDivisor = (index, divisor) => { + GLctx.vertexAttribDivisor(index, divisor); + }; + _emscripten_glVertexAttribDivisor.sig = 'vii'; + var _emscripten_glVertexAttribDivisorANGLE = + _emscripten_glVertexAttribDivisor; + + var _emscripten_glVertexAttribPointer = ( + index, + size, + type, + normalized, + stride, + ptr + ) => { + GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); + }; + _emscripten_glVertexAttribPointer.sig = 'viiiiip'; + + var _emscripten_glViewport = (x0, x1, x2, x3) => + GLctx.viewport(x0, x1, x2, x3); + _emscripten_glViewport.sig = 'viiii'; + + var _emscripten_out = (str) => out(UTF8ToString(str)); + _emscripten_out.sig = 'vp'; + + class HandleAllocator { + allocated = [undefined]; + freelist = []; + get(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) { + // 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 promiseMap = new HandleAllocator(); + var makePromise = () => { + var promiseInfo = {}; + promiseInfo.promise = new Promise((resolve, reject) => { + promiseInfo.reject = reject; + promiseInfo.resolve = resolve; + }); + promiseInfo.id = promiseMap.allocate(promiseInfo); + return promiseInfo; + }; + var _emscripten_promise_create = () => makePromise().id; + _emscripten_promise_create.sig = 'p'; + + var _emscripten_promise_destroy = (id) => { + promiseMap.free(id); + }; + _emscripten_promise_destroy.sig = 'vp'; + + var getPromise = (id) => promiseMap.get(id).promise; + + var _emscripten_promise_resolve = (id, result, value) => { + var info = promiseMap.get(id); + switch (result) { + case 0: + info.resolve(value); + return; + case 1: + info.resolve(getPromise(value)); + return; + case 2: + info.resolve(getPromise(value)); + _emscripten_promise_destroy(value); + return; + case 3: + info.reject(value); + return; + } + }; + _emscripten_promise_resolve.sig = 'vpip'; + + var growMemory = (size) => { + var oldHeapSize = wasmMemory.buffer.byteLength; + var pages = ((size - oldHeapSize + 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 /*success*/; + } catch (e) {} + // implicit 0 return to save code size (caller will cast "undefined" into 0 + // anyhow) + }; + var _emscripten_resize_heap = (requestedSize) => { + var oldSize = HEAPU8.length; + // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. + 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. + + // 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) { + 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 + 0.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; + } + } + return false; + }; + _emscripten_resize_heap.sig = 'ip'; + + var maybeCStringToJsString = (cString) => { + // "cString > 2" checks if the input is a number, and isn't of the special + // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). + // In other words, if cString > 2 then it's a pointer to a valid place in + // memory, and points to a C string. + return cString > 2 ? UTF8ToString(cString) : cString; + }; + + /** @type {Object} */ + var specialHTMLTargets = [ + 0, + typeof document != 'undefined' ? document : 0, + typeof window != 'undefined' ? window : 0, + ]; + var findEventTarget = (target) => { + target = maybeCStringToJsString(target); + var domElement = + specialHTMLTargets[target] || + (typeof document != 'undefined' + ? document.querySelector(target) + : null); + return domElement; + }; + var findCanvasEventTarget = findEventTarget; + var _emscripten_set_canvas_element_size = (target, width, height) => { + var canvas = findCanvasEventTarget(target); + if (!canvas) return -4; + canvas.width = width; + canvas.height = height; + return 0; + }; + _emscripten_set_canvas_element_size.sig = 'ipii'; + + /** @param {number=} timeout */ + var safeSetTimeout = (func, timeout) => { + runtimeKeepalivePush(); + return setTimeout(() => { + runtimeKeepalivePop(); + callUserCallback(func); + }, timeout); + }; + var _emscripten_sleep = (ms) => + Asyncify.handleSleep((wakeUp) => safeSetTimeout(wakeUp, ms)); + _emscripten_sleep.sig = 'vi'; + _emscripten_sleep.isAsync = true; + + var _emscripten_wget_data = (url, pbuffer, pnum, perror) => + Asyncify.handleAsync(async () => { + /* no need for run dependency, this is async but will not do any prepare etc. step */ + try { + const byteArray = await asyncLoad(UTF8ToString(url)); + // can only allocate the buffer after the wakeUp, not during an asyncing + var buffer = _malloc(byteArray.length); // must be freed by caller! + HEAPU8.set(byteArray, buffer); + HEAPU32[pbuffer >> 2] = buffer; + HEAP32[pnum >> 2] = byteArray.length; + HEAP32[perror >> 2] = 0; + } catch (err) { + HEAP32[perror >> 2] = 1; + } + }); + _emscripten_wget_data.sig = 'vpppp'; + _emscripten_wget_data.isAsync = true; + + var ENV = PHPLoader.ENV || {}; + + var getEnvStrings = () => { + if (!getEnvStrings.strings) { + // Default values. + // Browser language detection #8751 + var lang = + ( + (typeof navigator == 'object' && navigator.language) || + '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 _environ_get = (__environ, environ_buf) => { + var bufSize = 0; + var envp = 0; + for (var string of getEnvStrings()) { + var ptr = environ_buf + bufSize; + HEAPU32[(__environ + envp) >> 2] = ptr; + bufSize += stringToUTF8(string, ptr, Infinity) + 1; + envp += 4; + } + return 0; + }; + _environ_get.sig = 'ipp'; + + var _environ_sizes_get = (penviron_count, penviron_buf_size) => { + var strings = getEnvStrings(); + HEAPU32[penviron_count >> 2] = strings.length; + var bufSize = 0; + for (var string of strings) { + bufSize += lengthBytesUTF8(string) + 1; + } + HEAPU32[penviron_buf_size >> 2] = bufSize; + return 0; + }; + _environ_sizes_get.sig = 'ipp'; + + function _fd_fdstat_get(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; + } + HEAP8[pbuf] = type; + HEAP16[(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; + } + } + _fd_fdstat_get.sig = 'iip'; + + /** @param {number=} offset */ + var doReadv = (stream, iov, iovcnt, offset) => { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[iov >> 2]; + var len = HEAPU32[(iov + 4) >> 2]; + iov += 8; + var curr = FS.read(stream, HEAP8, 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_pread(fd, iov, iovcnt, offset, pnum) { + offset = bigintToI53Checked(offset); + + try { + if (isNaN(offset)) return 61; + var stream = SYSCALLS.getStreamFromFD(fd); + var num = doReadv(stream, iov, iovcnt, offset); + HEAPU32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return e.errno; + } + } + _fd_pread.sig = 'iippjp'; + + /** @param {number=} offset */ + var doWritev = (stream, iov, iovcnt, offset) => { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[iov >> 2]; + var len = HEAPU32[(iov + 4) >> 2]; + iov += 8; + var curr = FS.write(stream, HEAP8, ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + if (curr < len) { + // No more space to write. + break; + } + if (typeof offset != 'undefined') { + offset += curr; + } + } + return ret; + }; + + function _fd_pwrite(fd, iov, iovcnt, offset, pnum) { + offset = bigintToI53Checked(offset); + + try { + if (isNaN(offset)) return 61; + var stream = SYSCALLS.getStreamFromFD(fd); + var num = doWritev(stream, iov, iovcnt, offset); + HEAPU32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return e.errno; + } + } + _fd_pwrite.sig = 'iippjp'; + + function _fd_read(fd, iov, iovcnt, pnum) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var num = doReadv(stream, iov, iovcnt); + HEAPU32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return e.errno; + } + } + _fd_read.sig = 'iippp'; + + function _fd_seek(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; + } + } + _fd_seek.sig = 'iijip'; + + var _fd_sync = function (fd) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + return Asyncify.handleSleep((wakeUp) => { + var mount = stream.node.mount; + if (!mount.type.syncfs) { + // We write directly to the file system, so there's nothing to do here. + wakeUp(0); + return; + } + mount.type.syncfs(mount, false, (err) => { + wakeUp(err ? 29 : 0); + }); + }); + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return e.errno; + } + }; + _fd_sync.sig = 'ii'; + _fd_sync.isAsync = true; + + function _fd_write(fd, iov, iovcnt, pnum) { + try { + var stream = SYSCALLS.getStreamFromFD(fd); + var num = doWritev(stream, iov, iovcnt); + HEAPU32[pnum >> 2] = num; + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return e.errno; + } + } + _fd_write.sig = 'iippp'; + + var _getaddrinfo = (node, service, hint, out) => { + // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL + // hints are specified or ai_family set to AF_UNSPEC or ai_socktype or ai_protocol set to 0 then we + // really should provide a linked list of suitable addrinfo values. + var addrs = []; + var canon = null; + var addr = 0; + var port = 0; + var flags = 0; + var family = 0; + var type = 0; + var proto = 0; + var ai, last; + + function allocaddrinfo(family, type, proto, canon, addr, port) { + var sa, salen, ai; + var errno; + + salen = family === 10 ? 28 : 16; + addr = family === 10 ? inetNtop6(addr) : inetNtop4(addr); + sa = _malloc(salen); + errno = writeSockaddr(sa, family, addr, port); + + ai = _malloc(32); + HEAP32[(ai + 4) >> 2] = family; + HEAP32[(ai + 8) >> 2] = type; + HEAP32[(ai + 12) >> 2] = proto; + HEAPU32[(ai + 24) >> 2] = canon; + HEAPU32[(ai + 20) >> 2] = sa; + if (family === 10) { + HEAP32[(ai + 16) >> 2] = 28; + } else { + HEAP32[(ai + 16) >> 2] = 16; + } + HEAP32[(ai + 28) >> 2] = 0; + + return ai; + } + + if (hint) { + flags = HEAP32[hint >> 2]; + family = HEAP32[(hint + 4) >> 2]; + type = HEAP32[(hint + 8) >> 2]; + proto = HEAP32[(hint + 12) >> 2]; + } + if (type && !proto) { + proto = type === 2 ? 17 : 6; + } + if (!type && proto) { + type = proto === 17 ? 2 : 1; + } + + // If type or proto are set to zero in hints we should really be returning multiple addrinfo values, but for + // now default to a TCP STREAM socket so we can at least return a sensible addrinfo given NULL hints. + if (proto === 0) { + proto = 6; + } + if (type === 0) { + type = 1; + } + + if (!node && !service) { + return -2; + } + if (flags & ~(1 | 2 | 4 | 1024 | 8 | 16 | 32)) { + return -1; + } + if (hint !== 0 && HEAP32[hint >> 2] & 2 && !node) { + return -1; + } + if (flags & 32) { + // TODO + return -2; + } + if (type !== 0 && type !== 1 && type !== 2) { + return -7; + } + if (family !== 0 && family !== 2 && family !== 10) { + return -6; + } + + if (service) { + service = UTF8ToString(service); + port = parseInt(service, 10); + + if (isNaN(port)) { + if (flags & 1024) { + return -2; + } + // TODO support resolving well-known service names from: + // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt + return -8; + } + } + + if (!node) { + if (family === 0) { + family = 2; + } + if ((flags & 1) === 0) { + if (family === 2) { + addr = _htonl(2130706433); + } else { + addr = [0, 0, 0, _htonl(1)]; + } + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + HEAPU32[out >> 2] = ai; + return 0; + } + + // + // try as a numeric address + // + node = UTF8ToString(node); + addr = inetPton4(node); + if (addr !== null) { + // incoming node is a valid ipv4 address + if (family === 0 || family === 2) { + family = 2; + } else if (family === 10 && flags & 8) { + addr = [0, 0, _htonl(0xffff), addr]; + family = 10; + } else { + return -2; + } + } else { + addr = inetPton6(node); + if (addr !== null) { + // incoming node is a valid ipv6 address + if (family === 0 || family === 10) { + family = 10; + } else { + return -2; + } + } + } + if (addr != null) { + ai = allocaddrinfo(family, type, proto, node, addr, port); + HEAPU32[out >> 2] = ai; + return 0; + } + if (flags & 4) { + return -2; + } + + // + // try as a hostname + // + // resolve the hostname to a temporary fake address + node = DNS.lookup_name(node); + addr = inetPton4(node); + if (family === 0) { + family = 2; + } else if (family === 10) { + addr = [0, 0, _htonl(0xffff), addr]; + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + HEAPU32[out >> 2] = ai; + return 0; + }; + _getaddrinfo.sig = 'ipppp'; + + var _getnameinfo = (sa, salen, node, nodelen, serv, servlen, flags) => { + var info = readSockaddr(sa, salen); + if (info.errno) { + return -6; + } + var port = info.port; + var addr = info.addr; + + var overflowed = false; + + if (node && nodelen) { + var lookup; + if (flags & 1 || !(lookup = DNS.lookup_addr(addr))) { + if (flags & 8) { + return -2; + } + } else { + addr = lookup; + } + var numBytesWrittenExclNull = stringToUTF8(addr, node, nodelen); + + if (numBytesWrittenExclNull + 1 >= nodelen) { + overflowed = true; + } + } + + if (serv && servlen) { + port = '' + port; + var numBytesWrittenExclNull = stringToUTF8(port, serv, servlen); + + if (numBytesWrittenExclNull + 1 >= servlen) { + overflowed = true; + } + } + + if (overflowed) { + // Note: even when we overflow, getnameinfo() is specced to write out the truncated results. + return -12; + } + + return 0; + }; + _getnameinfo.sig = 'ipipipii'; + + var Protocols = { + list: [], + map: {}, + }; + + var stringToAscii = (str, buffer) => { + for (var i = 0; i < str.length; ++i) { + HEAP8[buffer++] = str.charCodeAt(i); + } + // Null-terminate the string + HEAP8[buffer] = 0; + }; + + var _setprotoent = (stayopen) => { + // void setprotoent(int stayopen); + + // Allocate and populate a protoent structure given a name, protocol number and array of aliases + function allocprotoent(name, proto, aliases) { + // write name into buffer + var nameBuf = _malloc(name.length + 1); + stringToAscii(name, nameBuf); + + // write aliases into buffer + var j = 0; + var length = aliases.length; + var aliasListBuf = _malloc((length + 1) * 4); // Use length + 1 so we have space for the terminating NULL ptr. + + for (var i = 0; i < length; i++, j += 4) { + var alias = aliases[i]; + var aliasBuf = _malloc(alias.length + 1); + stringToAscii(alias, aliasBuf); + HEAPU32[(aliasListBuf + j) >> 2] = aliasBuf; + } + HEAPU32[(aliasListBuf + j) >> 2] = 0; // Terminating NULL pointer. + + // generate protoent + var pe = _malloc(12); + HEAPU32[pe >> 2] = nameBuf; + HEAPU32[(pe + 4) >> 2] = aliasListBuf; + HEAP32[(pe + 8) >> 2] = proto; + return pe; + } + + // Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial + // to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful. + var list = Protocols.list; + var map = Protocols.map; + if (list.length === 0) { + var entry = allocprotoent('tcp', 6, ['TCP']); + list.push(entry); + map['tcp'] = map['6'] = entry; + entry = allocprotoent('udp', 17, ['UDP']); + list.push(entry); + map['udp'] = map['17'] = entry; + } + + _setprotoent.index = 0; + }; + _setprotoent.sig = 'vi'; + + var _getprotobyname = (name) => { + // struct protoent *getprotobyname(const char *); + name = UTF8ToString(name); + _setprotoent(true); + var result = Protocols.map[name]; + return result; + }; + _getprotobyname.sig = 'pp'; + + var _getprotobynumber = (number) => { + // struct protoent *getprotobynumber(int proto); + _setprotoent(true); + var result = Protocols.map[number]; + return result; + }; + _getprotobynumber.sig = 'pi'; + + function _js_flock(fd, op) { + _js_wasm_trace('js_flock(%d, %d)', fd, op); + // Emscripten does not expose these constants to JS, so we hardcode them here. + // Based on + // https://github.com/emscripten-core/emscripten/blob/76860cc47cef67f5712a7a03a247bc1baabf7ba4/system/lib/libc/musl/include/sys/file.h#L7-L10 + const emscripten_LOCK_SH = 1; + const emscripten_LOCK_EX = 2; + const emscripten_LOCK_NB = 4; + const emscripten_LOCK_UN = 8; + + const flockToLockOpType = { + [emscripten_LOCK_SH]: 'shared', + [emscripten_LOCK_EX]: 'exclusive', + [emscripten_LOCK_UN]: 'unlocked', + }; + + let vfsPath; + let errno; + + [vfsPath, errno] = locking.get_vfs_path_from_fd(fd); + if (errno !== 0) { + _js_wasm_trace( + 'js_flock(%d, %d) get_vfs_path_from_fd errno %d', + fd, + op, + vfsPath, + errno + ); + return -errno; + } + + if (!locking.is_path_to_shared_fs(vfsPath)) { + _js_wasm_trace( + 'flock(%d, %d) locking is not implemented for non-NodeFS path %s', + fd, + op, + vfsPath + ); + // If not a NodeFS path, we can't lock it. + // Default to succeeding as Emscripten does. + return 0; + } + + errno = locking.check_lock_params(fd, op); + if (errno !== 0) { + _js_wasm_trace( + 'js_flock(%d, %d) check_lock_params errno %d', + fd, + op, + errno + ); + return -errno; + } + + // @TODO: Consider supporting blocking mode of flock() + if (op & (emscripten_LOCK_NB === 0)) { + _js_wasm_trace( + 'js_flock(%d, %d) blocking mode of flock() is not implemented', + fd, + op + ); + // We do not yet support the blocking form of flock(). + // We respond with EINVAL to indicate failure + // because it is a known errno for a failed blocking flock(). + return -ERRNO_CODES.EINVAL; + } + + const maskedOp = + op & (emscripten_LOCK_SH | emscripten_LOCK_EX | emscripten_LOCK_UN); + + const lockOpType = flockToLockOpType[maskedOp]; + if (lockOpType === undefined) { + _js_wasm_trace( + 'js_flock(%d, %d) invalid flock() operation', + fd, + op + ); + return -ERRNO_CODES.EINVAL; + } + + try { + const nativeFilePath = + locking.get_native_path_from_vfs_path(vfsPath); + const obtainedLock = PHPLoader.fileLockManager.lockWholeFile( + nativeFilePath, + { + type: lockOpType, + pid: PHPLoader.processId, + fd, + } + ); + _js_wasm_trace( + 'js_flock(%d, %d) lockWholeFile %s returned %d', + fd, + op, + vfsPath, + obtainedLock + ); + if (obtainedLock) { + locking.maybeLockedFds.add(fd); + return 0; + } else { + return -ERRNO_CODES.EWOULDBLOCK; + } + } catch (e) { + _js_wasm_trace( + 'js_flock(%d, %d) lockWholeFile error %s', + fd, + op, + e + ); + return -ERRNO_CODES.EINVAL; + } + } -// Begin JS library code + function _js_open_process( + command, + argsPtr, + argsLength, + descriptorsPtr, + descriptorsLength, + cwdPtr, + cwdLength, + envPtr, + envLength + ) { + if (!command) { + ___errno_location(ERRNO_CODES.EINVAL); + return -1; + } + + const cmdstr = UTF8ToString(command); + if (!cmdstr.length) { + ___errno_location(ERRNO_CODES.EINVAL); + return -1; + } + + let argsArray = []; + if (argsLength) { + for (var i = 0; i < argsLength; i++) { + const charPointer = argsPtr + i * 4; + argsArray.push(UTF8ToString(HEAPU32[charPointer >> 2])); + } + } + + const cwdstr = cwdPtr ? UTF8ToString(cwdPtr) : FS.cwd(); + let envObject = null; + + if (envLength) { + envObject = {}; + for (var i = 0; i < envLength; i++) { + const envPointer = envPtr + i * 4; + const envEntry = UTF8ToString(HEAPU32[envPointer >> 2]); + const splitAt = envEntry.indexOf('='); + if (splitAt === -1) { + continue; + } + const key = envEntry.substring(0, splitAt); + const value = envEntry.substring(splitAt + 1); + envObject[key] = value; + } + } + + var std = {}; + // Extracts an array of available descriptors that should be dispatched to streams. + // On the C side, the descriptors are expressed as `**int` so we must go read + // each of the `descriptorsLength` `*int` pointers and convert the associated data into + // a JavaScript object { descriptor : { child : fd, parent : fd } }. + for (var i = 0; i < descriptorsLength; i++) { + const descriptorPtr = HEAPU32[(descriptorsPtr + i * 4) >> 2]; + std[HEAPU32[descriptorPtr >> 2]] = { + child: HEAPU32[(descriptorPtr + 4) >> 2], + parent: HEAPU32[(descriptorPtr + 8) >> 2], + }; + // swap parent and child descs until we rebuild PHP 7.4 + if (i === 0) { + HEAPU32[(descriptorPtr + 8) >> 2] = + std[HEAPU32[descriptorPtr >> 2]].parent; + HEAPU32[(descriptorPtr + 4) >> 2] = + std[HEAPU32[descriptorPtr >> 2]].child; + } + } + + return Asyncify.handleAsync(async () => { + let cp; + try { + const options = {}; + if (cwdstr !== null) { + options.cwd = cwdstr; + } + if (envObject !== null) { + options.env = envObject; + } + cp = PHPWASM.spawnProcess(cmdstr, argsArray, options); + if (cp instanceof Promise) { + cp = await cp; + } + } catch (e) { + if (e.code === 'SPAWN_UNSUPPORTED') { + ___errno_location(ERRNO_CODES.ENOSYS); + return -1; + } + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) + throw e; + ___errno_location(e.code); + return -1; + } + + const ProcInfo = { + pid: cp.pid, + exited: false, + }; + PHPWASM.processTable[ProcInfo.pid] = ProcInfo; + + const stdinParentFd = std[0]?.parent, + stdinChildFd = std[0]?.child, + stdoutChildFd = std[1]?.child, + stdoutParentFd = std[1]?.parent, + stderrChildFd = std[2]?.child, + stderrParentFd = std[2]?.parent; + + cp.on('exit', function (code) { + for (const fd of [ + // The child process exited. Let's clean up its output streams: + stdoutChildFd, + stderrChildFd, + stdinChildFd, + + // We won't close these because the PHP already handles that in the parent process: + // stdoutParentFd, + // stderrParentFd, + // stdinParentFd, + ]) { + if (FS.streams[fd] && !FS.isClosed(FS.streams[fd])) { + FS.close(FS.streams[fd]); + } + } + + ProcInfo.exitCode = code; + ProcInfo.exited = true; + }); + + // Pass data from child process's stdout to PHP's end of the stdout pipe. + if (stdoutChildFd) { + const stdoutStream = SYSCALLS.getStreamFromFD(stdoutChildFd); + let stdoutAt = 0; + cp.stdout.on('data', function (data) { + stdoutStream.stream_ops.write( + stdoutStream, + data, + 0, + data.length, + stdoutAt + ); + stdoutAt += data.length; + }); + } + + // Pass data from child process's stderr to PHP's end of the stdout pipe. + if (stderrChildFd) { + const stderrStream = SYSCALLS.getStreamFromFD(stderrChildFd); + let stderrAt = 0; + cp.stderr.on('data', function (data) { + stderrStream.stream_ops.write( + stderrStream, + data, + 0, + data.length, + stderrAt + ); + stderrAt += data.length; + }); + } + + /** + * Wait until the child process has been spawned. + * Unfortunately there is no Node.js API to check whether + * the process has already been spawned. We can only listen + * to the 'spawn' event and if it has already been spawned, + * listen to the 'exit' event. + */ + try { + await new Promise((resolve, reject) => { + /** + * There was no `await` between the `spawnProcess` call + * and the `await` below so the process haven't had a chance + * to run any of the exit-related callbacks yet. + * + * Good. + * + * Let's listen to all the lifecycle events and resolve + * the promise when the process starts or immediately crashes. + */ + let resolved = false; + cp.on('spawn', () => { + if (resolved) return; + resolved = true; + resolve(); + }); + cp.on('error', (e) => { + if (resolved) return; + resolved = true; + reject(e); + }); + cp.on('exit', function (code) { + if (resolved) return; + resolved = true; + if (code === 0) { + resolve(); + } else { + reject( + new Error(`Process exited with code ${code}`) + ); + } + }); + /** + * If the process haven't even started after 5 seconds, something + * is wrong. Perhaps we're missing an event listener, or perhaps + * the `spawnProcess` implementation failed to dispatch the relevant + * event. Either way, let's crash to avoid blocking the proc_open() + * call indefinitely. + */ + setTimeout(() => { + if (resolved) return; + resolved = true; + reject(new Error('Process timed out')); + }, 5000); + }); + } catch (e) { + // Process already started. Even if it exited early, PHP still + // needs to know about the pid and clean up the resources. + console.error(e); + return ProcInfo.pid; + } + + // Now we want to pass data from the STDIN source supplied by PHP + // to the child process. + if (stdinChildFd) { + // We're in a kernel function used instead of fork(). + // + // We are the ones responsible for pumping the data from the stdinChildFd + // into the child process. There is no concurrent task operating on the + // piped data or polling the file descriptors, etc. Nothing will ever + // read from the stdinChildFd if we don't do it here. + // + // Well, let's do it! We'll periodically read from the child end of the + // data pipe and push what we get into the child process. + let stdinStream; + try { + stdinStream = SYSCALLS.getStreamFromFD(stdinChildFd); + } catch (e) { + ___errno_location(ERRNO_CODES.EBADF); + return ProcInfo.pid; + } + if (!stdinStream?.node) { + return ProcInfo.pid; + } + + // Pipe the entire stdinStream to cp.stdin + const CHUNK_SIZE = 1024; + + const iov = _malloc(16); // Space for iovec structure + const pnum = _malloc(4); // Space for number of bytes read + const buffer = _malloc(CHUNK_SIZE); + + // Set up iovec structure pointing to our buffer + HEAPU32[iov >> 2] = buffer; // iov_base + HEAPU32[(iov + 4) >> 2] = CHUNK_SIZE; // iov_len + + function pump() { + try { + while (true) { + if (cp.killed) { + stopPumpingAndCloseStdin(); + return; + } + + const result = js_fd_read( + stdinChildFd, + iov, + 1, + pnum, + false + ); + const bytesRead = HEAPU32[pnum >> 2]; + if (result === 0 && bytesRead > 0) { + const wrote = HEAPU8.subarray( + buffer, + buffer + bytesRead + ); + cp.stdin.write(wrote); + // We've read some data. Let the next iteration decide + // how to break out of the loop. + } else if (result === 0 && bytesRead === 0) { + // result === 0 and bytesRead === 0 means the file descriptor + // is at EOF. Let's close the stdin stream and clean up. + stopPumpingAndCloseStdin(); + break; + } else if (result === ERRNO_CODES.EAGAIN) { + // The file descriptor is not ready for reading. + // Let's break out of the loop. setInterval will invoke + // this function again soon. + break; + } else { + throw new FS.ErrnoError(result); + } + } + } catch (e) { + if ( + typeof FS == 'undefined' || + !(e.name === 'ErrnoError') + ) { + throw e; + } + ___errno_location(e.errno); + stopPumpingAndCloseStdin(); + } + } + function stopPumpingAndCloseStdin() { + clearInterval(interval); + if (!cp.stdin.closed) { + cp.stdin.end(); + } + _free(buffer); + _free(iov); + _free(pnum); + } + + // pump() can never alter the result of this function. + // Even when it fails, we still return the pid. + // Why? + // Because the process already started. We wouldn't backtrack + // with fork(), we won't backtrack here. Let's give PHP the pid, + // and let it think it's the parent process. It will clean up the + // resources as needed. + + // stdin may be non-blocking – let's check for updates periodically. + // If we exhaust it at any point, pump() will self-terminate. + // + // Note handling any failures, closing the descriptor, etc. will not + // happen synchronously when PHP calls fclose($pipes[0]) or proc_close(). + // It will all happen asynchronously on the next tick. It seems off, + // but there doesn't seem to be a better way: cp.stdin.write() and + // cp.stdin.end() are both async APIs and they both accept onCompleted + // callbacks. + const interval = setInterval(pump, 20); + pump(); + } + + return ProcInfo.pid; + }); + } + function _js_process_status(pid, exitCodePtr) { + if (!PHPWASM.processTable[pid]) { + return -1; + } + if (PHPWASM.processTable[pid].exited) { + HEAPU32[exitCodePtr >> 2] = PHPWASM.processTable[pid].exitCode; + return 1; + } + return 0; + } - class ExitStatus { - name = 'ExitStatus'; - constructor(status) { - this.message = `Program terminated with exit(${status})`; - this.status = status; - } - } -ExitStatus = class PHPExitStatus extends Error { - constructor(status) { - super(status); - this.name = 'ExitStatus'; - this.message = 'Program terminated with exit(' + status + ')'; - this.status = status; + function _js_release_file_locks() { + _js_wasm_trace('js_release_file_locks()'); + const pid = PHPLoader.processId; + if (!pid || !PHPLoader.fileLockManager) { + _js_wasm_trace('js_release_file_locks no pid or file lock manager'); + return 0; + } + + try { + PHPLoader.fileLockManager.releaseLocksForProcess(pid); + _js_wasm_trace('js_release_file_locks succeeded'); + } catch (e) { + _js_wasm_trace('js_release_file_locks error %s', e); + } } -}; - - var GOT = { - }; - - var currentModuleWeakSymbols = new Set(["__start___llvm_prf_data","__stop___llvm_prf_data","__start___llvm_prf_names","__stop___llvm_prf_names","__start___llvm_prf_vns","__stop___llvm_prf_vns","__start___llvm_prf_vtab","__stop___llvm_prf_vtab","__start___llvm_prf_cnts","__stop___llvm_prf_cnts","__start___llvm_prf_bits","__stop___llvm_prf_bits","__start___llvm_prf_vnds","__stop___llvm_prf_vnds"]); - var GOTHandler = { - get(obj, symName) { - var rtn = GOT[symName]; - if (!rtn) { - rtn = GOT[symName] = new WebAssembly.Global({'value': 'i32', 'mutable': true}, -1); - } - if (!currentModuleWeakSymbols.has(symName)) { - // Any non-weak reference to a symbol marks it as `required`, which - // enabled `reportUndefinedSymbols` to report undefined symbol errors - // correctly. - rtn.required = true; - } - return rtn; - }, - }; - - var callRuntimeCallbacks = (callbacks) => { - while (callbacks.length > 0) { - // Pass the module as the first argument. - callbacks.shift()(Module); - } - }; - var onPostRuns = []; - var addOnPostRun = (cb) => onPostRuns.push(cb); - - var onPreRuns = []; - var addOnPreRun = (cb) => onPreRuns.push(cb); - - var runDependencies = 0; - - - var dependenciesFulfilled = null; - var removeRunDependency = (id) => { - runDependencies--; - - Module['monitorRunDependencies']?.(runDependencies); - - if (runDependencies == 0) { - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); // can add another dependenciesFulfilled - } - } - }; - var addRunDependency = (id) => { - runDependencies++; - - Module['monitorRunDependencies']?.(runDependencies); - - }; - - - var dynCalls = { - }; - var dynCallLegacy = (sig, ptr, args) => { - sig = sig.replace(/p/g, 'i') - var f = dynCalls[sig]; - return f(ptr, ...args); - }; - var dynCall = (sig, ptr, args = [], promising = false) => { - var rtn = dynCallLegacy(sig, ptr, args); - - function convert(rtn) { - return rtn; - } - - return convert(rtn); - }; - - var UTF8Decoder = globalThis.TextDecoder && new TextDecoder(); - - var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { - var maxIdx = idx + maxBytesToRead; - if (ignoreNul) return maxIdx; - // TextDecoder needs to know the byte length in advance, it doesn't stop on - // null terminator by itself. - // As a tiny code save trick, compare idx against maxIdx using a negation, - // so that maxBytesToRead=undefined/NaN means Infinity. - while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; - return idx; - }; - - /** - * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given - * array that contains uint8 values, returns a copy of that string as a - * Javascript String object. - * heapOrArray is either a regular array, or a JavaScript typed array view. - * @param {number=} idx - * @param {number=} maxBytesToRead - * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. - * @return {string} - */ - var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { - - var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); - - // When using conditional TextDecoder, skip it for short strings as the overhead of the native call is not worth it. - if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { - return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); - } - var str = ''; - 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 & 0x80)) { str += String.fromCharCode(u0); continue; } - var u1 = heapOrArray[idx++] & 63; - if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } - var u2 = heapOrArray[idx++] & 63; - if ((u0 & 0xF0) == 0xE0) { - u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; - } else { - u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); - } - - if (u0 < 0x10000) { - str += String.fromCharCode(u0); - } else { - var ch = u0 - 0x10000; - str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); - } - } - return str; - }; - var getDylinkMetadata = (binary) => { - var offset = 0; - var end = 0; - - function getU8() { - return binary[offset++]; - } - - function getLEB() { - var ret = 0; - var mul = 1; - while (1) { - var byte = binary[offset++]; - ret += ((byte & 0x7f) * mul); - mul *= 0x80; - if (!(byte & 0x80)) break; - } - return ret; - } - - function getString() { - var len = getLEB(); - offset += len; - return UTF8ArrayToString(binary, offset - len, len); - } - - function getStringList() { - var count = getLEB(); - var rtn = [] - while (count--) rtn.push(getString()); - return rtn; - } - - /** @param {string=} message */ - function failIf(condition, message) { - if (condition) throw new Error(message); - } - - if (binary instanceof WebAssembly.Module) { - var dylinkSection = WebAssembly.Module.customSections(binary, 'dylink.0'); - failIf(dylinkSection.length === 0, 'need dylink section'); - binary = new Uint8Array(dylinkSection[0]); - end = binary.length - } else { - var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer); - var magicNumberFound = int32View[0] == 0x6d736100; - failIf(!magicNumberFound, 'need to see wasm magic number'); // \0asm - // we should see the dylink custom section right after the magic number and wasm version - failIf(binary[8] !== 0, 'need the dylink section to be first') - offset = 9; - var section_size = getLEB(); //section size - end = offset + section_size; - var name = getString(); - failIf(name !== 'dylink.0'); - } - - var customSection = { neededDynlibs: [], tlsExports: new Set(), weakImports: new Set(), runtimePaths: [] }; - var WASM_DYLINK_MEM_INFO = 0x1; - var WASM_DYLINK_NEEDED = 0x2; - var WASM_DYLINK_EXPORT_INFO = 0x3; - var WASM_DYLINK_IMPORT_INFO = 0x4; - var WASM_DYLINK_RUNTIME_PATH = 0x5; - var WASM_SYMBOL_TLS = 0x100; - var WASM_SYMBOL_BINDING_MASK = 0x3; - var WASM_SYMBOL_BINDING_WEAK = 0x1; - while (offset < end) { - var subsectionType = getU8(); - var subsectionSize = getLEB(); - if (subsectionType === WASM_DYLINK_MEM_INFO) { - customSection.memorySize = getLEB(); - customSection.memoryAlign = getLEB(); - customSection.tableSize = getLEB(); - customSection.tableAlign = getLEB(); - } else if (subsectionType === WASM_DYLINK_NEEDED) { - customSection.neededDynlibs = getStringList(); - } else if (subsectionType === WASM_DYLINK_EXPORT_INFO) { - var count = getLEB(); - while (count--) { - var symname = getString(); - var flags = getLEB(); - if (flags & WASM_SYMBOL_TLS) { - customSection.tlsExports.add(symname); - } - } - } else if (subsectionType === WASM_DYLINK_IMPORT_INFO) { - var count = getLEB(); - while (count--) { - var modname = getString(); - var symname = getString(); - var flags = getLEB(); - if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) { - customSection.weakImports.add(symname); - } - } - } else if (subsectionType === WASM_DYLINK_RUNTIME_PATH) { - customSection.runtimePaths = getStringList(); - } else { - // unknown subsection - offset += subsectionSize; - } - } - - return customSection; - }; - - - /** - * @param {number} ptr - * @param {string} type - */ - function getValue(ptr, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': return HEAP8[ptr]; - case 'i8': return HEAP8[ptr]; - case 'i16': return HEAP16[((ptr)>>1)]; - case 'i32': return HEAP32[((ptr)>>2)]; - case 'i64': return HEAP64[((ptr)>>3)]; - case 'float': return HEAPF32[((ptr)>>2)]; - case 'double': return HEAPF64[((ptr)>>3)]; - case '*': return HEAPU32[((ptr)>>2)]; - default: abort(`invalid type for getValue: ${type}`); - } - } - - var newDSO = (name, handle, syms) => { - var dso = { - refcount: Infinity, - name, - exports: syms, - global: true, - }; - LDSO.loadedLibsByName[name] = dso; - if (handle != undefined) { - LDSO.loadedLibsByHandle[handle] = dso; - } - return dso; - }; - var LDSO = { - loadedLibsByName:{ - }, - loadedLibsByHandle:{ - }, - init() { - newDSO('__main__', 0, wasmImports); - }, - }; - - - - - - var alignMemory = (size, alignment) => { - return Math.ceil(size / alignment) * alignment; - }; - - var getMemory = (size) => { - // After the runtime is initialized, we must only use sbrk() normally. - if (runtimeInitialized) { - // Currently we don't support freeing of static data when modules are - // unloaded via dlclose. This function is tagged as `noleakcheck` to - // avoid having this reported as leak. - return _calloc(size, 1); - } - var ret = ___heap_base; - // Keep __heap_base stack aligned. - var end = ret + alignMemory(size, 16); - ___heap_base = end; - - // After allocating the memory from the start of the heap we need to ensure - // that once the program starts it doesn't use this region. In relocatable - // mode we can just update the __heap_base symbol that we are exporting to - // the main module. - // When not relocatable `__heap_base` is fixed and exported by the main - // module, but we can update the `sbrk_ptr` value instead. We call - // `_emscripten_get_sbrk_ptr` knowing that it is safe to call prior to - // runtime initialization (unlike, the higher level sbrk function) - var sbrk_ptr = _emscripten_get_sbrk_ptr(); - HEAPU32[((sbrk_ptr)>>2)] = end - return ret; - }; - - - var isInternalSym = (symName) => { - // TODO: find a way to mark these in the binary or avoid exporting them. - return [ - 'memory', - '__memory_base', - '__table_base', - '__stack_pointer', - '__indirect_function_table', - '__cpp_exception', - '__c_longjmp', - '__wasm_apply_data_relocs', - '__dso_handle', - '__tls_size', - '__tls_align', - '__set_stack_limits', - '_emscripten_tls_init', - '__wasm_init_tls', - '__wasm_call_ctors', - '__start_em_asm', - '__stop_em_asm', - '__start_em_js', - '__stop_em_js', - ].includes(symName) || symName.startsWith('__em_js__') - ; - }; - - var wasmTableMirror = []; - - - var getWasmTableEntry = (funcPtr) => { - var func = wasmTableMirror[funcPtr]; - if (!func) { - /** @suppress {checkTypes} */ - wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); - } - return func; - }; - - 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 - return wasmTable['grow'](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); - }; - - var uleb128EncodeWithLen = (arr) => { - const n = arr.length; - // Note: this LEB128 length encoding produces extra byte for n < 128, - // but we don't care as it's only used in a temporary representation. - return [(n % 128) | 128, n >> 7, ...arr]; - }; - - - var wasmTypeCodes = { - 'i': 0x7f, // i32 - 'p': 0x7f, // i32 - 'j': 0x7e, // i64 - 'f': 0x7d, // f32 - 'd': 0x7c, // f64 - 'e': 0x6f, // externref - }; - var generateTypePack = (types) => uleb128EncodeWithLen(Array.from(types, (type) => { - var code = wasmTypeCodes[type]; - return code; - })); - var convertJsFunctionToWasm = (func, sig) => { - - // Rest of the module is static - var bytes = Uint8Array.of( - 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") - 0x01, 0x00, 0x00, 0x00, // version: 1 - 0x01, // Type section code - // The module is static, with the exception of the type section, which is - // generated based on the signature passed in. - ...uleb128EncodeWithLen([ - 0x01, // count: 1 - 0x60 /* form: func */, - // param types - ...generateTypePack(sig.slice(1)), - // return types (for now only supporting [] if `void` and single [T] otherwise) - ...generateTypePack(sig[0] === 'v' ? '' : sig[0]) - ]), - // The rest of the module is static - 0x02, 0x07, // import section - // (import "e" "f" (func 0 (type 0))) - 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00, - 0x07, 0x05, // export section - // (export "f" (func 0 (type 0))) - 0x01, 0x01, 0x66, 0x00, 0x00, - ); - - // 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(bytes); - var instance = new WebAssembly.Instance(module, { 'e': { 'f': func } }); - var wrappedFunc = instance.exports['f']; - return wrappedFunc; - }; - /** @param {string=} sig */ - var addFunction = (func, sig) => { - // 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. - - 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; - } - var wrapped = convertJsFunctionToWasm(func, sig); - setWasmTableEntry(ret, wrapped); - } - - functionsInTableMap.set(func, ret); - - return ret; - }; - /** @param {boolean=} replace */ - var updateGOT = (exports, replace) => { - for (var symName in exports) { - if (isInternalSym(symName)) { - continue; - } - - var value = exports[symName]; - - var existingEntry = GOT[symName] && GOT[symName].value != -1; - if (replace || !existingEntry) { - var newValue; - if (typeof value == 'function') { - newValue = addFunction(value); - } else if (typeof value == 'number') { - newValue = value; - } else { - // The GOT can only contain addresses (i.e data addresses or function - // addresses so we currently ignore other types export here. - continue; - } - GOT[symName] ??= new WebAssembly.Global({'value': 'i32', 'mutable': true}); - GOT[symName].value = newValue; - } - } - }; - - var isImmutableGlobal = (val) => { - if (val instanceof WebAssembly.Global) { - try { - val.value = val.value; - } catch { - return true; - } - } - return false; - }; - var relocateExports = (exports, memoryBase = 0) => { - - function relocateExport(name, value) { - // Detect immuable wasm global exports. These represent data addresses - // which are relative to `memoryBase` - if (isImmutableGlobal(value)) { - return value.value + memoryBase; - } - - // Return unmodified value (no relocation required). - return value; - } - - var relocated = {}; - for (var e in exports) { - relocated[e] = relocateExport(e, exports[e]) - } - return relocated; - }; - - var isSymbolDefined = (symName) => { - // Ignore 'stub' symbols that are auto-generated as part of the original - // `wasmImports` used to instantiate the main module. - var existing = wasmImports[symName]; - if (!existing || existing.stub) { - return false; - } - // Even if a symbol exists in wasmImports, and is not itself a stub, it - // could be an ASYNCIFY wrapper function that wraps a stub function. - if (symName in asyncifyStubs && !asyncifyStubs[symName]) { - return false; - } - return true; - }; - - var createNamedFunction = (name, func) => Object.defineProperty(func, 'name', { value: name }); - - - - var stackSave = () => _emscripten_stack_get_current(); - - var stackRestore = (val) => __emscripten_stack_restore(val); - var createInvokeFunction = (sig) => (ptr, ...args) => { - var sp = stackSave(); - try { - return dynCall(sig, ptr, args); - } catch(e) { - stackRestore(sp); - // Create a try-catch guard that rethrows the Emscripten EH exception. - // Exceptions thrown from C++ will be a pointer (number) and longjmp - // will throw the number Infinity. Use the compact and fast "e !== e+0" - // test to check if e was not a Number. - if (e !== e+0) throw e; - _setThrew(1, 0); - // In theory this if statement could be done on - // creating the function, but I just added this to - // save wasting code space as it only happens on exception. - if (sig[0] == "j") return 0n; - } - }; - var resolveGlobalSymbol = (symName, direct = false) => { - var sym; - if (isSymbolDefined(symName)) { - sym = wasmImports[symName]; - } - // Asm.js-style exception handling: invoke wrapper generation - else if (symName.startsWith('invoke_')) { - // Create (and cache) new invoke_ functions on demand. - sym = wasmImports[symName] = createNamedFunction(symName, createInvokeFunction(symName.split('_')[1])); - } - return {sym, name: symName}; - }; - - - - - - - - var onPostCtors = []; - var addOnPostCtor = (cb) => onPostCtors.push(cb); - - - /** - * 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. - * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. - * @return {string} - */ - var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => { - return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead, ignoreNul) : ''; - }; - - /** - * @param {string=} libName - * @param {Object=} localScope - * @param {number=} handle - */ - var loadWebAssemblyModule = (binary, flags, libName, localScope, handle) => { - var metadata = getDylinkMetadata(binary); - - // loadModule loads the wasm module after all its dependencies have been loaded. - // can be called both sync/async. - function loadModule() { - // alignments are powers of 2 - var memAlign = Math.pow(2, metadata.memoryAlign); - // prepare memory - var memoryBase = metadata.memorySize ? alignMemory(getMemory(metadata.memorySize + memAlign), memAlign) : 0; // TODO: add to cleanups - var tableBase = metadata.tableSize ? wasmTable.length : 0; - if (handle) { - HEAP8[(handle)+(8)] = 1; - HEAPU32[(((handle)+(12))>>2)] = memoryBase; - HEAP32[(((handle)+(16))>>2)] = metadata.memorySize; - HEAPU32[(((handle)+(20))>>2)] = tableBase; - HEAP32[(((handle)+(24))>>2)] = metadata.tableSize; - } - - if (metadata.tableSize) { - wasmTable.grow(metadata.tableSize); - } - - // This is the export map that we ultimately return. We declare it here - // so it can be used within resolveSymbol. We resolve symbols against - // this local symbol map in the case there they are not present on the - // global Module object. We need this fallback because Modules sometime - // need to import their own symbols - var moduleExports; - - function resolveSymbol(sym) { - var resolved = resolveGlobalSymbol(sym).sym; - if (!resolved && localScope) { - resolved = localScope[sym]; - } - if (!resolved) { - resolved = moduleExports[sym]; - } - return resolved; - } - - // TODO kill ↓↓↓ (except "symbols local to this module", it will likely be - // not needed if we require that if A wants symbols from B it has to link - // to B explicitly: similarly to -Wl,--no-undefined) - // - // wasm dynamic libraries are pure wasm, so they cannot assist in - // their own loading. When side module A wants to import something - // provided by a side module B that is loaded later, we need to - // add a layer of indirection, but worse, we can't even tell what - // to add the indirection for, without inspecting what A's imports - // are. To do that here, we use a JS proxy (another option would - // be to inspect the binary directly). - var proxyHandler = { - get(stubs, prop) { - // symbols that should be local to this module - switch (prop) { - case '__memory_base': - return memoryBase; - case '__table_base': - return tableBase; - } - if (prop in wasmImports && !wasmImports[prop].stub) { - // No stub needed, symbol already exists in symbol table - var res = wasmImports[prop]; - // Asyncify wraps exports, and we need to look through those wrappers. - if (res.orig) { - res = res.orig; - } - return res; - } - // Return a stub function that will resolve the symbol - // when first called. - if (!(prop in stubs)) { - var resolved; - stubs[prop] = (...args) => { - resolved ||= resolveSymbol(prop); - return resolved(...args); - }; - } - return stubs[prop]; - } - }; - var proxy = new Proxy({}, proxyHandler); - currentModuleWeakSymbols = metadata.weakImports; - var info = { - 'GOT.mem': new Proxy({}, GOTHandler), - 'GOT.func': new Proxy({}, GOTHandler), - 'env': proxy, - 'wasi_snapshot_preview1': proxy, - }; - - function postInstantiation(module, instance) { - // add new entries to functionsInTableMap - updateTableMap(tableBase, metadata.tableSize); - moduleExports = relocateExports(instance.exports, memoryBase); - updateGOT(moduleExports); - moduleExports = Asyncify.instrumentWasmExports(moduleExports); - if (!flags.allowUndefined) { - reportUndefinedSymbols(); - } - - function addEmAsm(addr, body) { - var args = []; - for (var arity = 0; ; arity++) { - var argName = '$' + arity; - if (!body.includes(argName)) break; - args.push(argName); - } - args = args.join(','); - var func = `(${args}) => { ${body} };`; - ASM_CONSTS[start] = eval(func); - } - - // Add any EM_ASM function that exist in the side module - if ('__start_em_asm' in moduleExports) { - var start = moduleExports['__start_em_asm']; - var stop = moduleExports['__stop_em_asm']; - - - while (start < stop) { - var jsString = UTF8ToString(start); - addEmAsm(start, jsString); - start = HEAPU8.indexOf(0, start) + 1; - } - } - - function addEmJs(name, cSig, body) { - // The signature here is a C signature (e.g. "(int foo, char* bar)"). - // See `create_em_js` in emcc.py` for the build-time version of this - // code. - var jsArgs = []; - cSig = cSig.slice(1, -1) - if (cSig != 'void') { - cSig = cSig.split(','); - for (var arg of cSig) { - var jsArg = arg.split(' ').pop(); - jsArgs.push(jsArg.replace('*', '')); - } - } - var func = `(${jsArgs}) => ${body};`; - moduleExports[name] = eval(func); - } - - for (var name in moduleExports) { - if (name.startsWith('__em_js__')) { - var start = moduleExports[name] - var jsString = UTF8ToString(start); - // EM_JS strings are stored in the data section in the form - // SIG<::>BODY. - var [sig, body] = jsString.split('<::>'); - addEmJs(name.replace('__em_js__', ''), sig, body); - delete moduleExports[name]; - } - } - - // initialize the module - var applyRelocs = moduleExports['__wasm_apply_data_relocs']; - if (applyRelocs) { - if (runtimeInitialized) { - applyRelocs(); - } else { - __RELOC_FUNCS__.push(applyRelocs); - } - } - var init = moduleExports['__wasm_call_ctors']; - if (init) { - if (runtimeInitialized) { - init(); - } else { - // we aren't ready to run compiled code yet - addOnPostCtor(init); - } - } - return moduleExports; - } - - if (flags.loadAsync) { - return (async () => { - var instance; - if (binary instanceof WebAssembly.Module) { - instance = new WebAssembly.Instance(binary, info); - } else { - // Destructuring assignment without declaration has to be wrapped - // with parens or parser will treat the l-value as an object - // literal instead. - ({ module: binary, instance } = await WebAssembly.instantiate(binary, info)); - } - return postInstantiation(binary, instance); - })(); - } - - var module = binary instanceof WebAssembly.Module ? binary : new WebAssembly.Module(binary); - var instance = new WebAssembly.Instance(module, info); - return postInstantiation(module, instance); - } - - // We need to set rpath in flags based on the current library's rpath. - // We can't mutate flags or else if a depends on b and c and b depends on d, - // then c will be loaded with b's rpath instead of a's. - flags = {...flags, rpath: { parentLibPath: libName, paths: metadata.runtimePaths }} - // now load needed libraries and the module itself. - if (flags.loadAsync) { - return metadata.neededDynlibs - .reduce((chain, dynNeeded) => chain.then(() => - loadDynamicLibrary(dynNeeded, flags, localScope) - ), Promise.resolve()) - .then(loadModule); - } - - for (var needed of metadata.neededDynlibs) { - loadDynamicLibrary(needed, flags, localScope) - } - return loadModule(); - }; - - var mergeLibSymbols = (exports, libName) => { - registerDynCallSymbols(exports); - // add symbols into global namespace TODO: weak linking etc. - for (var [sym, exp] of Object.entries(exports)) { - - // When RTLD_GLOBAL is enabled, the symbols defined by this shared object - // will be made available for symbol resolution of subsequently loaded - // shared objects. - // - // We should copy the symbols (which include methods and variables) from - // SIDE_MODULE to MAIN_MODULE. - const setImport = (target) => { - if (target in asyncifyStubs) { - asyncifyStubs[target] = exp; - } - if (!isSymbolDefined(target)) { - wasmImports[target] = exp; - } - } - setImport(sym); - - // Special case for handling of main symbol: If a side module exports - // `main` that also acts a definition for `__main_argc_argv` and vice - // versa. - const main_alias = '__main_argc_argv'; - if (sym == 'main') { - setImport(main_alias) - } - if (sym == main_alias) { - setImport('main') - } - } - }; - - - var asyncLoad = async (url) => { - var arrayBuffer = await readAsync(url); - return new Uint8Array(arrayBuffer); - }; - - var preloadPlugins = []; - var registerWasmPlugin = () => { - // Use string keys here for public methods to avoid minification since the - // plugin consumer also uses string keys. - var wasmPlugin = { - promiseChainEnd: Promise.resolve(), - 'canHandle': (name) => { - return !Module['noWasmDecoding'] && name.endsWith('.so') - }, - 'handle': async (byteArray, name) => - // loadWebAssemblyModule can not load modules out-of-order, so rather - // than just running the promises in parallel, this makes a chain of - // promises to run in series. - wasmPlugin.promiseChainEnd = wasmPlugin.promiseChainEnd.then(async () => { - try { - var exports = await loadWebAssemblyModule(byteArray, {loadAsync: true, nodelete: true}, name, {}); - } catch (error) { - throw new Error(`failed to instantiate wasm: ${name}: ${error}`); - } - preloadedWasm[name] = exports; - return byteArray; - }) - }; - preloadPlugins.push(wasmPlugin); - }; - var preloadedWasm = { - }; - - 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 replaceORIGIN = (parentLibName, rpath) => { - if (rpath.startsWith('$ORIGIN')) { - // TODO: what to do if we only know the relative path of the file? It will return "." here. - var origin = PATH.dirname(parentLibName); - return rpath.replace('$ORIGIN', origin); - } - - return rpath; - }; - - - - var withStackSave = (f) => { - var stack = stackSave(); - var ret = f(); - stackRestore(stack); - return ret; - }; - - var stackAlloc = (sz) => __emscripten_stack_alloc(sz); - - 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 <= 0x7F) { - len++; - } else if (c <= 0x7FF) { - len += 2; - } else if (c >= 0xD800 && c <= 0xDFFF) { - len += 4; ++i; - } else { - len += 3; - } - } - return len; - }; - - - var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { - // 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) { - // 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.codePointAt(i); - if (u <= 0x7F) { - if (outIdx >= endIdx) break; - heap[outIdx++] = u; - } else if (u <= 0x7FF) { - if (outIdx + 1 >= endIdx) break; - heap[outIdx++] = 0xC0 | (u >> 6); - heap[outIdx++] = 0x80 | (u & 63); - } else if (u <= 0xFFFF) { - if (outIdx + 2 >= endIdx) break; - heap[outIdx++] = 0xE0 | (u >> 12); - heap[outIdx++] = 0x80 | ((u >> 6) & 63); - heap[outIdx++] = 0x80 | (u & 63); - } else { - if (outIdx + 3 >= endIdx) break; - heap[outIdx++] = 0xF0 | (u >> 18); - heap[outIdx++] = 0x80 | ((u >> 12) & 63); - heap[outIdx++] = 0x80 | ((u >> 6) & 63); - heap[outIdx++] = 0x80 | (u & 63); - // Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16. - // We need to manually skip over the second code unit for correct iteration. - i++; - } - } - // Null-terminate the pointer to the buffer. - heap[outIdx] = 0; - return outIdx - startIdx; - }; - var stringToUTF8 = (str, outPtr, maxBytesToWrite) => { - return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); - }; - - var stringToUTF8OnStack = (str) => { - var size = lengthBytesUTF8(str) + 1; - var ret = stackAlloc(size); - stringToUTF8(str, ret, size); - return ret; - }; - - - var initRandomFill = () => { - - return (view) => crypto.getRandomValues(view); - }; - 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 ''; // an invalid portion invalidates the whole thing - } - 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 = []; - - - /** @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 (ENVIRONMENT_IS_NODE) { - // we will read data by chunks of BUFSIZE - var BUFSIZE = 256; - var buf = Buffer.alloc(BUFSIZE); - var bytesRead = 0; - - // For some reason we must suppress a closure warning here, even though - // fd definitely exists on process.stdin, and is even the proper way to - // get the fd of stdin, - // https://github.com/nodejs/help/issues/2136#issuecomment-523649904 - // This started to happen after moving this logic out of library_tty.js, - // so it is related to the surrounding code in some unclear manner. - /** @suppress {missingProperties} */ - var fd = process.stdin.fd; - - try { - bytesRead = fs.readSync(fd, buf, 0, BUFSIZE); - } catch(e) { - // Cross-platform differences: on Windows, reading EOF throws an - // exception, but on other OSes, reading EOF returns 0. Uniformize - // behavior by treating the EOF exception to return 0. - if (e.toString().includes('EOF')) bytesRead = 0; - else throw e; - } - - if (bytesRead > 0) { - result = buf.slice(0, bytesRead).toString('utf-8'); - } - } else - {} - if (!result) { - return null; - } - FS_stdin_getChar_buffer = intArrayFromString(result, true); - } - return FS_stdin_getChar_buffer.shift(); - }; - var TTY = { - ttys:[], - init() { - // https://github.com/emscripten-core/emscripten/pull/1555 - // if (ENVIRONMENT_IS_NODE) { - // // currently, FS.init does not distinguish if process.stdin is a file or TTY - // // device, it always assumes it's a TTY device. because of this, we're forcing - // // process.stdin to UTF8 encoding to at least make stdin reading compatible - // // with text files until FS.init can be refactored. - // process.stdin.setEncoding('utf8'); - // } - }, - shutdown() { - // https://github.com/emscripten-core/emscripten/pull/1555 - // if (ENVIRONMENT_IS_NODE) { - // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)? - // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation - // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists? - // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle - // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call - // process.stdin.pause(); - // } - }, - register(dev, ops) { - TTY.ttys[dev] = { input: [], output: [], ops: 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 /* ignored */) { - 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); // val == 0 would cut text output off in the middle. - } - }, - 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: [ - 0x03, 0x1c, 0x7f, 0x15, 0x04, 0x00, 0x01, 0x00, 0x11, 0x13, 0x1a, 0x00, - 0x12, 0x0f, 0x17, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ] - }; - }, - 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) => HEAPU8.fill(0, ptr, ptr + size); - - 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.0 : 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); // Copy old data over to the new storage. - }, - 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))); // Copy old data over to the new storage. - } - 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) { - // This error may happen quite a bit. To avoid overhead we reuse it (and - // suffer a lack of stack info). - if (!MEMFS.doesNotExistError) { - MEMFS.doesNotExistError = new FS.ErrnoError(44); - /** @suppress {checkTypes} */ - MEMFS.doesNotExistError.stack = ''; - } - throw MEMFS.doesNotExistError; - }, - mknod(parent, name, mode, dev) { - return MEMFS.createNode(parent, name, mode, dev); - }, - rename(old_node, new_dir, new_name) { - var new_node; - try { - new_node = FS.lookupNode(new_dir, new_name); - } catch (e) {} - if (new_node) { - if (FS.isDir(old_node.mode)) { - // 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, 0o777 | 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); - 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) { - // 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 === HEAP8.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) { - 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]; // Or fall back to manual write if not. - } - } - 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 === HEAP8.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); - } - } - HEAP8.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 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 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 NODEFS = { - isWindows:false, - staticInit() { - NODEFS.isWindows = !!process.platform.match(/^win/); - var flags = process.binding("constants")["fs"]; - NODEFS.flagsForNodeMap = { - "1024": flags["O_APPEND"], - "64": flags["O_CREAT"], - "128": flags["O_EXCL"], - "256": flags["O_NOCTTY"], - "0": flags["O_RDONLY"], - "2": flags["O_RDWR"], - "4096": flags["O_SYNC"], - "512": flags["O_TRUNC"], - "1": flags["O_WRONLY"], - "131072": flags["O_NOFOLLOW"], - }; - }, - convertNodeCode(e) { - var code = e.code; - return ERRNO_CODES[code]; - }, - tryFSOperation(f) { - try { - return f(); - } catch (e) { - if (!e.code) throw e; - // node under windows can return code 'UNKNOWN' here: - // https://github.com/emscripten-core/emscripten/issues/15468 - if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28); - throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); - } - }, - mount(mount) { - return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0); - }, - createNode(parent, name, mode, dev) { - if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { - throw new FS.ErrnoError(28); - } - var node = FS.createNode(parent, name, mode); - node.node_ops = NODEFS.node_ops; - node.stream_ops = NODEFS.stream_ops; - return node; - }, - getMode(path) { - return NODEFS.tryFSOperation(() => { - var mode = fs.lstatSync(path).mode; - if (NODEFS.isWindows) { - // Windows does not report the 'x' permission bit, so propagate read - // bits to execute bits. - mode |= (mode & 292) >> 2; - } - return mode; - }); - }, - realPath(node) { - var parts = []; - while (node.parent !== node) { - parts.push(node.name); - node = node.parent; - } - parts.push(node.mount.opts.root); - parts.reverse(); - return PATH.join(...parts); - }, - flagsForNode(flags) { - flags &= ~2097152; // Ignore this flag from musl, otherwise node.js fails to open the file. - flags &= ~2048; // Ignore this flag from musl, otherwise node.js fails to open the file. - flags &= ~32768; // Ignore this flag from musl, otherwise node.js fails to open the file. - flags &= ~524288; // Some applications may pass it; it makes no sense for a single process. - flags &= ~65536; // Node.js doesn't need this passed in, it errors. - var newFlags = 0; - for (var k in NODEFS.flagsForNodeMap) { - if (flags & k) { - newFlags |= NODEFS.flagsForNodeMap[k]; - flags ^= k; - } - } - if (flags) { - throw new FS.ErrnoError(28); - } - return newFlags; - }, - getattr(func, node) { - var stat = NODEFS.tryFSOperation(func); - if (NODEFS.isWindows) { - // node.js v0.10.20 doesn't report blksize and blocks on Windows. Fake - // them with default blksize of 4096. - // See http://support.microsoft.com/kb/140365 - if (!stat.blksize) { - stat.blksize = 4096; - } - if (!stat.blocks) { - stat.blocks = (stat.size+stat.blksize-1)/stat.blksize|0; - } - // Windows does not report the 'x' permission bit, so propagate read - // bits to execute bits. - stat.mode |= (stat.mode & 292) >> 2; - } - return { - dev: stat.dev, - ino: node.id, - mode: stat.mode, - nlink: stat.nlink, - uid: stat.uid, - gid: stat.gid, - rdev: stat.rdev, - size: stat.size, - atime: stat.atime, - mtime: stat.mtime, - ctime: stat.ctime, - blksize: stat.blksize, - blocks: stat.blocks - }; - }, - setattr(arg, node, attr, chmod, utimes, truncate, stat) { - NODEFS.tryFSOperation(() => { - if (attr.mode !== undefined) { - var mode = attr.mode; - if (NODEFS.isWindows) { - // Windows only supports S_IREAD / S_IWRITE (S_IRUSR / S_IWUSR) - // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod - mode &= 384; - } - chmod(arg, mode); - // update the common node structure mode as well - node.mode = attr.mode; - } - if (typeof (attr.atime ?? attr.mtime) === "number") { - // Unfortunately, we have to stat the current value if we don't want - // to change it. On top of that, since the times don't round trip - // this will only keep the value nearly unchanged not exactly - // unchanged. See: - // https://github.com/nodejs/node/issues/56492 - var atime = new Date(attr.atime ?? stat(arg).atime); - var mtime = new Date(attr.mtime ?? stat(arg).mtime); - utimes(arg, atime, mtime); - } - if (attr.size !== undefined) { - truncate(arg, attr.size); - } - }); - }, - node_ops:{ - getattr(node) { - var path = NODEFS.realPath(node); - return NODEFS.getattr(() => fs.lstatSync(path), node); - }, - setattr(node, attr) { - var path = NODEFS.realPath(node); - if (attr.mode != null && attr.dontFollow) { - throw new FS.ErrnoError(52); - } - NODEFS.setattr(path, node, attr, fs.chmodSync, fs.utimesSync, fs.truncateSync, fs.lstatSync); - }, - lookup(parent, name) { - var path = PATH.join2(NODEFS.realPath(parent), name); - var mode = NODEFS.getMode(path); - return NODEFS.createNode(parent, name, mode); - }, - mknod(parent, name, mode, dev) { - var node = NODEFS.createNode(parent, name, mode, dev); - // create the backing node for this in the fs root as well - var path = NODEFS.realPath(node); - NODEFS.tryFSOperation(() => { - if (FS.isDir(node.mode)) { - fs.mkdirSync(path, node.mode); - } else { - fs.writeFileSync(path, '', { mode: node.mode }); - } - }); - return node; - }, - rename(oldNode, newDir, newName) { - var oldPath = NODEFS.realPath(oldNode); - var newPath = PATH.join2(NODEFS.realPath(newDir), newName); - try { - FS.unlink(newPath); - } catch(e) {} - NODEFS.tryFSOperation(() => fs.renameSync(oldPath, newPath)); - oldNode.name = newName; - }, - unlink(parent, name) { - var path = PATH.join2(NODEFS.realPath(parent), name); - NODEFS.tryFSOperation(() => fs.unlinkSync(path)); - }, - rmdir(parent, name) { - var path = PATH.join2(NODEFS.realPath(parent), name); - NODEFS.tryFSOperation(() => fs.rmdirSync(path)); - }, - readdir(node) { - var path = NODEFS.realPath(node); - return NODEFS.tryFSOperation(() => fs.readdirSync(path)); - }, - symlink(parent, newName, oldPath) { - var newPath = PATH.join2(NODEFS.realPath(parent), newName); - NODEFS.tryFSOperation(() => fs.symlinkSync(oldPath, newPath)); - }, - readlink(node) { - var path = NODEFS.realPath(node); - return NODEFS.tryFSOperation(() => fs.readlinkSync(path)); - }, - statfs(path) { - var stats = NODEFS.tryFSOperation(() => fs.statfsSync(path)); - // Node.js doesn't provide frsize (fragment size). Set it to bsize (block size) - // as they're often the same in many file systems. May not be accurate for all. - stats.frsize = stats.bsize; - return stats; - }, - }, - stream_ops:{ - getattr(stream) { - return NODEFS.getattr(() => fs.fstatSync(stream.nfd), stream.node); - }, - setattr(stream, attr) { - NODEFS.setattr(stream.nfd, stream.node, attr, fs.fchmodSync, fs.futimesSync, fs.ftruncateSync, fs.fstatSync); - }, - open(stream) { - var path = NODEFS.realPath(stream.node); - NODEFS.tryFSOperation(() => { - stream.shared.refcount = 1; - stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)); - }); - }, - close(stream) { - NODEFS.tryFSOperation(() => { - if (stream.nfd && --stream.shared.refcount === 0) { - fs.closeSync(stream.nfd); - } - }); - }, - dup(stream) { - stream.shared.refcount++; - }, - read(stream, buffer, offset, length, position) { - return NODEFS.tryFSOperation(() => - fs.readSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), 0, length, position) - ); - }, - write(stream, buffer, offset, length, position) { - return NODEFS.tryFSOperation(() => - fs.writeSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), 0, length, position) - ); - }, - llseek(stream, offset, whence) { - var position = offset; - if (whence === 1) { - position += stream.position; - } else if (whence === 2) { - if (FS.isFile(stream.node.mode)) { - NODEFS.tryFSOperation(() => { - var stat = fs.fstatSync(stream.nfd); - position += stat.size; - }); - } - } - - 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 = mmapAlloc(length); - - NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position); - return { ptr, allocated: true }; - }, - msync(stream, buffer, offset, length, mmapFlags) { - NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false); - // should we check if bytesWritten and length are the same? - return 0; - }, - }, - }; - - - - var PROXYFS = { - mount(mount) { - return PROXYFS.createNode(null, '/', mount.opts.fs.lstat(mount.opts.root).mode, 0); - }, - createNode(parent, name, mode, dev) { - if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - var node = FS.createNode(parent, name, mode); - node.node_ops = PROXYFS.node_ops; - node.stream_ops = PROXYFS.stream_ops; - return node; - }, - realPath(node) { - var parts = []; - while (node.parent !== node) { - parts.push(node.name); - node = node.parent; - } - parts.push(node.mount.opts.root); - parts.reverse(); - return PATH.join(...parts); - }, - node_ops:{ - getattr(node) { - var path = PROXYFS.realPath(node); - var stat; - try { - stat = node.mount.opts.fs.lstat(path); - } catch (e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - return { - dev: stat.dev, - ino: stat.ino, - mode: stat.mode, - nlink: stat.nlink, - uid: stat.uid, - gid: stat.gid, - rdev: stat.rdev, - size: stat.size, - atime: stat.atime, - mtime: stat.mtime, - ctime: stat.ctime, - blksize: stat.blksize, - blocks: stat.blocks - }; - }, - setattr(node, attr) { - var path = PROXYFS.realPath(node); - try { - if (attr.mode !== undefined) { - node.mount.opts.fs.chmod(path, attr.mode); - // update the common node structure mode as well - node.mode = attr.mode; - } - if (attr.atime || attr.mtime) { - var atime = new Date(attr.atime || attr.mtime); - var mtime = new Date(attr.mtime || attr.atime); - node.mount.opts.fs.utime(path, atime, mtime); - } - if (attr.size !== undefined) { - node.mount.opts.fs.truncate(path, attr.size); - } - } catch (e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - lookup(parent, name) { - try { - var path = PATH.join2(PROXYFS.realPath(parent), name); - var mode = parent.mount.opts.fs.lstat(path).mode; - var node = PROXYFS.createNode(parent, name, mode); - return node; - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - mknod(parent, name, mode, dev) { - var node = PROXYFS.createNode(parent, name, mode, dev); - // create the backing node for this in the fs root as well - var path = PROXYFS.realPath(node); - try { - if (FS.isDir(node.mode)) { - node.mount.opts.fs.mkdir(path, node.mode); - } else { - node.mount.opts.fs.writeFile(path, '', { mode: node.mode }); - } - } catch (e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - return node; - }, - rename(oldNode, newDir, newName) { - var oldPath = PROXYFS.realPath(oldNode); - var newPath = PATH.join2(PROXYFS.realPath(newDir), newName); - try { - oldNode.mount.opts.fs.rename(oldPath, newPath); - oldNode.name = newName; - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - unlink(parent, name) { - var path = PATH.join2(PROXYFS.realPath(parent), name); - try { - parent.mount.opts.fs.unlink(path); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - rmdir(parent, name) { - var path = PATH.join2(PROXYFS.realPath(parent), name); - try { - parent.mount.opts.fs.rmdir(path); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - readdir(node) { - var path = PROXYFS.realPath(node); - try { - return node.mount.opts.fs.readdir(path); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - symlink(parent, newName, oldPath) { - var newPath = PATH.join2(PROXYFS.realPath(parent), newName); - try { - parent.mount.opts.fs.symlink(oldPath, newPath); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - readlink(node) { - var path = PROXYFS.realPath(node); - try { - return node.mount.opts.fs.readlink(path); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - }, - stream_ops:{ - open(stream) { - var path = PROXYFS.realPath(stream.node); - try { - stream.nfd = stream.node.mount.opts.fs.open(path,stream.flags); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - close(stream) { - try { - stream.node.mount.opts.fs.close(stream.nfd); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - read(stream, buffer, offset, length, position) { - try { - return stream.node.mount.opts.fs.read(stream.nfd, buffer, offset, length, position); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - write(stream, buffer, offset, length, position) { - try { - return stream.node.mount.opts.fs.write(stream.nfd, buffer, offset, length, position); - } catch(e) { - if (!e.code) throw e; - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - }, - llseek(stream, offset, whence) { - var position = offset; - if (whence === 1) { - position += stream.position; - } else if (whence === 2) { - if (FS.isFile(stream.node.mode)) { - try { - var stat = stream.node.node_ops.getattr(stream.node); - position += stat.size; - } catch (e) { - throw new FS.ErrnoError(ERRNO_CODES[e.code]); - } - } - } - - if (position < 0) { - throw new FS.ErrnoError(ERRNO_CODES.EINVAL); - } - - return position; - }, - }, - }; - - - - var FS_createDataFile = (...args) => FS.createDataFile(...args); - - var getUniqueRunDependency = (id) => { - return id; - }; - - - - var FS_handledByPreloadPlugin = async (byteArray, fullname) => { - // Ensure plugins are ready. - if (typeof Browser != 'undefined') Browser.init(); - - for (var plugin of preloadPlugins) { - if (plugin['canHandle'](fullname)) { - return plugin['handle'](byteArray, fullname); - } - } - // In no plugin handled this file then return the original/unmodified - // byteArray. - return byteArray; - }; - var FS_preloadFile = async (parent, name, url, canRead, canWrite, 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 - addRunDependency(dep); - - try { - var byteArray = url; - if (typeof url == 'string') { - byteArray = await asyncLoad(url); - } - - byteArray = await FS_handledByPreloadPlugin(byteArray, fullname); - preFinish?.(); - if (!dontCreateFile) { - FS_createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); - } - } finally { - removeRunDependency(dep); - } - }; - var FS_createPreloadedFile = (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) => { - FS_preloadFile(parent, name, url, canRead, canWrite, dontCreateFile, canOwn, preFinish).then(onload).catch(onerror); - }; - var FS = { - root:null, - mounts:[], - devices:{ - }, - streams:[], - nextInode:1, - nameTable:null, - currentPath:"/", - initialized:false, - ignorePermissions:true, - filesystems:null, - syncFSRequests:0, - readFiles:{ - }, - ErrnoError:class { - 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) { - this.errno = errno; - } - }, - FSStream:class { - shared = {}; - get object() { - return this.node; - } - set object(val) { - this.node = val; - } - get isRead() { - return (this.flags & 2097155) !== 1; - } - get isWrite() { - return (this.flags & 2097155) !== 0; - } - get isAppend() { - return (this.flags & 1024); - } - get flags() { - return this.shared.flags; - } - set flags(val) { - this.shared.flags = val; - } - get position() { - return this.shared.position; - } - set position(val) { - this.shared.position = val; - } - }, - FSNode:class { - node_ops = {}; - stream_ops = {}; - readMode = 292 | 73; - writeMode = 146; - mounted = null; - constructor(parent, name, mode, rdev) { - if (!parent) { - parent = this; // root node sets parent to itself - } - 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); - if (FS.isRoot(current)) { - path = current_path + '/' + parts.slice(i + 1).join('/'); - // We're making progress here, don't let many consecutive ..'s - // lead to ELOOP - nlinks--; - continue linkloop; - } else { - current = current.parent; - } - continue; - } - - current_path = PATH.join2(current_path, parts[i]); - try { - current = FS.lookupNode(current, parts[i]); - } catch (e) { - // if 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) { - 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' // opening for write - || (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) { - - // 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) & 0xff), - makedev:(ma, mi) => ((ma) << 8 | (mi)), - registerDevice(dev, ops) { - FS.devices[dev] = { stream_ops: ops }; - }, - getDevice:(dev) => FS.devices[dev], - getMounts(mount) { - var mounts = []; - var check = [mount]; - - while (check.length) { - var m = check.pop(); - - mounts.push(m); - - check.push(...m.mounts); - } - - return mounts; - }, - syncfs(populate, callback) { - if (typeof populate == 'function') { - callback = populate; - populate = false; - } - - FS.syncFSRequests++; - - if (FS.syncFSRequests > 1) { - err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`); - } - - var mounts = FS.getMounts(FS.root.mount); - var completed = 0; - - function doCallback(errCode) { - FS.syncFSRequests--; - return callback(errCode); - } - - function done(errCode) { - if (errCode) { - if (!done.errored) { - done.errored = true; - return doCallback(errCode); - } - return; - } - if (++completed >= mounts.length) { - doCallback(null); - } - }; - - // sync all mounts - for (var mount of mounts) { - if (mount.type.syncfs) { - mount.type.syncfs(mount, populate, done); - } else { - done(null); - } - } - }, - mount(type, opts, mountpoint) { - var root = mountpoint === '/'; - var pseudo = !mountpoint; - var node; - - if (root && FS.root) { - throw new FS.ErrnoError(10); - } else if (!root && !pseudo) { - var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); - - mountpoint = lookup.path; // use the absolute path - node = lookup.node; - - if (FS.isMountpoint(node)) { - throw new FS.ErrnoError(10); - } - - - } - - 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); - - for (var [hash, current] of Object.entries(FS.nameTable)) { - while (current) { - var next = current.name_next; - - if (mounts.includes(current.mount)) { - FS.destroyNode(current); - } - - current = next; - } - } - - // no longer a mountpoint - node.mounted = null; - - // remove this mount from the child mounts - var idx = node.mount.mounts.indexOf(mount); - node.mount.mounts.splice(idx, 1); - }, - lookup(parent, name) { - return parent.node_ops.lookup(parent, name); - }, - mknod(path, mode, dev) { - var 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 = 0o666) { - mode &= 4095; - mode |= 32768; - return FS.mknod(path, mode, 0); - }, - mkdir(path, mode = 0o777) { - 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 = 0o666; - } - 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) { - // not fatal - } - // 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 - // we ignore the uid / gid for now - }); - }, - 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: atime, - mtime: mtime - }); - }, - open(path, flags, mode = 0o666) { - 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 | 0o777, 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 & 0o777); - } - 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) { - 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) { - 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) { - 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') { - abort(`Invalid encoding type "${opts.encoding}"`); - } - 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') { - buf = UTF8ArrayToString(buf); - } - FS.close(stream); - return buf; - }, - writeFile(path, data, opts = {}) { - opts.flags = opts.flags || 577; - var stream = FS.open(path, opts.flags, opts.mode); - if (typeof data == 'string') { - data = new Uint8Array(intArrayFromString(data, true)); - } - if (ArrayBuffer.isView(data)) { - FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); - } else { - abort('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); - }, - staticInit() { - FS.nameTable = new Array(4096); - - FS.mount(MEMFS, {}, '/'); - - FS.createDefaultDirectories(); - FS.createDefaultDevices(); - FS.createSpecialDirectories(); - - FS.filesystems = { - 'MEMFS': MEMFS, - 'NODEFS': NODEFS, - 'PROXYFS': PROXYFS, - }; - }, - init(input, output, error) { - 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 /* ignored */) { - 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 (globalThis.XMLHttpRequest) { - abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); - } else { // Command-line. - try { - obj.contents = readBinary(obj.url); - } 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)) abort("Couldn't load " + url + ". Status: " + xhr.status); - var datalength = Number(xhr.getResponseHeader("Content-length")); - var header; - var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; - var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; - - var 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) abort("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength-1) abort("only " + datalength + " bytes available! programmer error!"); - - // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); - - // 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)) abort("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') abort('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 (globalThis.XMLHttpRequest) { - if (!ENVIRONMENT_IS_WORKER) abort('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: 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 = {}; - for (const [key, fn] of Object.entries(node.stream_ops)) { - 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); - 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, HEAP8, ptr, length, position); - return { ptr, allocated: true }; - }; - node.stream_ops = stream_ops; - return node; - }, - }; - - - var findLibraryFS = (libName, rpath) => { - // If we're preloading a dynamic library, the runtime is not ready to call - // __wasmfs_identify or __emscripten_find_dylib. So just quit out. - // - // This means that DT_NEEDED for the main module and transitive dependencies - // of it won't work with this code path. Similarly, it means that calling - // loadDynamicLibrary in a preRun hook can't use this code path. - if (!runtimeInitialized) { - return undefined; - } - if (PATH.isAbs(libName)) { - try { - FS.lookupPath(libName); - return libName; - } catch (e) { - return undefined; - } - } - var rpathResolved = (rpath?.paths || []).map((p) => replaceORIGIN(rpath?.parentLibPath, p)); - return withStackSave(() => { - // In dylink.c we use: `char buf[2*NAME_MAX+2];` and NAME_MAX is 255. - // So we use the same size here. - var bufSize = 2*255 + 2; - var buf = stackAlloc(bufSize); - var rpathC = stringToUTF8OnStack(rpathResolved.join(':')); - var libNameC = stringToUTF8OnStack(libName); - var resLibNameC = __emscripten_find_dylib(buf, rpathC, libNameC, bufSize); - return resLibNameC ? UTF8ToString(resLibNameC) : undefined; - }); - }; - - var registerDynCallSymbols = (exports) => { - for (var [sym, exp] of Object.entries(exports)) { - if (sym.startsWith('dynCall_')) { - var sig = sym.substring(8); - if (!dynCalls.hasOwnProperty(sig)) { - dynCalls[sig] = exp; - } - } - } - }; - - /** - * @param {number=} handle - * @param {Object=} localScope - */ - function loadDynamicLibrary(libName, flags = {global: true, nodelete: true}, localScope, handle) { - // when loadDynamicLibrary did not have flags, libraries were loaded - // globally & permanently - - var dso = LDSO.loadedLibsByName[libName]; - if (dso) { - // the library is being loaded or has been loaded already. - if (!flags.global) { - if (localScope) { - Object.assign(localScope, dso.exports); - } - registerDynCallSymbols(dso.exports); - } else if (!dso.global) { - // The library was previously loaded only locally but not - // we have a request with global=true. - dso.global = true; - mergeLibSymbols(dso.exports, libName) - } - // same for "nodelete" - if (flags.nodelete && dso.refcount !== Infinity) { - dso.refcount = Infinity; - } - dso.refcount++ - if (handle) { - LDSO.loadedLibsByHandle[handle] = dso; - } - return flags.loadAsync ? Promise.resolve(true) : true; - } - - // allocate new DSO - dso = newDSO(libName, handle, 'loading'); - dso.refcount = flags.nodelete ? Infinity : 1; - dso.global = flags.global; - - // libName -> libData - function loadLibData() { - - // for wasm, we can use fetch for async, but for fs mode we can only imitate it - if (handle) { - var data = HEAPU32[(((handle)+(28))>>2)]; - var dataSize = HEAPU32[(((handle)+(32))>>2)]; - if (data && dataSize) { - var libData = HEAP8.slice(data, data + dataSize); - return flags.loadAsync ? Promise.resolve(libData) : libData; - } - } - - var f = findLibraryFS(libName, flags.rpath); - if (f) { - var libData = FS.readFile(f, {encoding: 'binary'}); - return flags.loadAsync ? Promise.resolve(libData) : libData; - } - - var libFile = locateFile(libName); - if (flags.loadAsync) { - return asyncLoad(libFile); - } - - // load the binary synchronously - if (!readBinary) { - throw new Error(`${libFile}: file not found, and synchronous loading of external files is not available`); - } - return readBinary(libFile); - } - - // libName -> exports - function getExports() { - // lookup preloaded cache first - var preloaded = preloadedWasm[libName]; - if (preloaded) { - return flags.loadAsync ? Promise.resolve(preloaded) : preloaded; - } - - // module not preloaded - load lib data and create new module from it - if (flags.loadAsync) { - return loadLibData().then((libData) => loadWebAssemblyModule(libData, flags, libName, localScope, handle)); - } - - return loadWebAssemblyModule(loadLibData(), flags, libName, localScope, handle); - } - - // module for lib is loaded - update the dso & global namespace - function moduleLoaded(exports) { - if (dso.global) { - mergeLibSymbols(exports, libName); - } else if (localScope) { - Object.assign(localScope, exports); - registerDynCallSymbols(exports); - } - dso.exports = exports; - } - - if (flags.loadAsync) { - return getExports().then((exports) => { - moduleLoaded(exports); - return true; - }); - } - - moduleLoaded(getExports()); - return true; - } - - - var reportUndefinedSymbols = () => { - for (var [symName, entry] of Object.entries(GOT)) { - if (entry.value == -1) { - var value = resolveGlobalSymbol(symName, true).sym; - if (!value && !entry.required) { - // Ignore undefined symbols that are imported as weak. - entry.value = 0; - continue; - } - if (typeof value == 'function') { - /** @suppress {checkTypes} */ - entry.value = addFunction(value, value.sig); - } else if (typeof value == 'number') { - entry.value = value; - } else { - throw new Error(`bad export type for '${symName}': ${typeof value} (${value})`); - } - } - } - }; - - - var loadDylibs = async () => { - if (!dynamicLibraries.length) { - reportUndefinedSymbols(); - return; - } - - addRunDependency('loadDylibs'); - - // Load binaries asynchronously - for (var lib of dynamicLibraries) { - await loadDynamicLibrary(lib, {loadAsync: true, global: true, nodelete: true, allowUndefined: true}) - } - // we got them all, wonderful - reportUndefinedSymbols(); - - removeRunDependency('loadDylibs'); - }; - - - var noExitRuntime = false; - - - - - - /** - * @param {number} ptr - * @param {number} value - * @param {string} type - */ - function setValue(ptr, value, type = 'i8') { - if (type.endsWith('*')) type = '*'; - switch (type) { - case 'i1': HEAP8[ptr] = value; break; - case 'i8': HEAP8[ptr] = value; break; - case 'i16': HEAP16[((ptr)>>1)] = value; break; - case 'i32': HEAP32[((ptr)>>2)] = value; break; - case 'i64': HEAP64[((ptr)>>3)] = BigInt(value); break; - case 'float': HEAPF32[((ptr)>>2)] = value; break; - case 'double': HEAPF64[((ptr)>>3)] = value; break; - case '*': HEAPU32[((ptr)>>2)] = value; break; - default: abort(`invalid type for setValue: ${type}`); - } - } + function _js_waitpid(pid, exitCodePtr) { + if (!PHPWASM.processTable[pid]) { + return -1; + } + return Asyncify.handleSleep((wakeUp) => { + const poll = function () { + if (PHPWASM.processTable[pid]?.exited) { + HEAPU32[exitCodePtr >> 2] = + PHPWASM.processTable[pid].exitCode; + wakeUp(pid); + } else { + setTimeout(poll, 50); + } + }; + poll(); + }); + } + function _random_get(buffer, size) { + try { + randomFill(HEAPU8.subarray(buffer, buffer + size)); + return 0; + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return e.errno; + } + } + _random_get.sig = 'ipp'; + + var arraySum = (array, index) => { + var sum = 0; + for (var i = 0; i <= index; sum += array[i++]) { + // no-op + } + return sum; + }; + + var MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + var MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + var addDays = (date, days) => { + var newDate = new Date(date.getTime()); + while (days > 0) { + var leap = isLeapYear(newDate.getFullYear()); + var currentMonth = newDate.getMonth(); + var daysInCurrentMonth = ( + leap ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR + )[currentMonth]; + + if (days > daysInCurrentMonth - newDate.getDate()) { + // we spill over to next month + days -= daysInCurrentMonth - newDate.getDate() + 1; + newDate.setDate(1); + if (currentMonth < 11) { + newDate.setMonth(currentMonth + 1); + } else { + newDate.setMonth(0); + newDate.setFullYear(newDate.getFullYear() + 1); + } + } else { + // we stay in current month + newDate.setDate(newDate.getDate() + days); + return newDate; + } + } + + return newDate; + }; + + var _strptime = (buf, format, tm) => { + // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html + var pattern = UTF8ToString(format); + + // escape special characters + // TODO: not sure we really need to escape all of these in JS regexps + var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.'; + for (var i = 0, ii = SPECIAL_CHARS.length; i < ii; ++i) { + pattern = pattern.replace( + new RegExp('\\' + SPECIAL_CHARS[i], 'g'), + '\\' + SPECIAL_CHARS[i] + ); + } + + // reduce number of matchers + var EQUIVALENT_MATCHERS = { + A: '%a', + B: '%b', + c: '%a %b %d %H:%M:%S %Y', + D: '%m\\/%d\\/%y', + e: '%d', + F: '%Y-%m-%d', + h: '%b', + R: '%H\\:%M', + r: '%I\\:%M\\:%S\\s%p', + T: '%H\\:%M\\:%S', + x: '%m\\/%d\\/(?:%y|%Y)', + X: '%H\\:%M\\:%S', + }; + // TODO: take care of locale + + var DATE_PATTERNS = { + /* weekday name */ a: '(?:Sun(?:day)?)|(?:Mon(?:day)?)|(?:Tue(?:sday)?)|(?:Wed(?:nesday)?)|(?:Thu(?:rsday)?)|(?:Fri(?:day)?)|(?:Sat(?:urday)?)', + /* month name */ b: '(?:Jan(?:uary)?)|(?:Feb(?:ruary)?)|(?:Mar(?:ch)?)|(?:Apr(?:il)?)|May|(?:Jun(?:e)?)|(?:Jul(?:y)?)|(?:Aug(?:ust)?)|(?:Sep(?:tember)?)|(?:Oct(?:ober)?)|(?:Nov(?:ember)?)|(?:Dec(?:ember)?)', + /* century */ C: '\\d\\d', + /* day of month */ d: '0[1-9]|[1-9](?!\\d)|1\\d|2\\d|30|31', + /* hour (24hr) */ H: '\\d(?!\\d)|[0,1]\\d|20|21|22|23', + /* hour (12hr) */ I: '\\d(?!\\d)|0\\d|10|11|12', + /* day of year */ j: '00[1-9]|0?[1-9](?!\\d)|0?[1-9]\\d(?!\\d)|[1,2]\\d\\d|3[0-6]\\d', + /* month */ m: '0[1-9]|[1-9](?!\\d)|10|11|12', + /* minutes */ M: '0\\d|\\d(?!\\d)|[1-5]\\d', + /* whitespace */ n: ' ', + /* AM/PM */ p: 'AM|am|PM|pm|A\\.M\\.|a\\.m\\.|P\\.M\\.|p\\.m\\.', + /* seconds */ S: '0\\d|\\d(?!\\d)|[1-5]\\d|60', + /* week number */ U: '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53', + /* week number */ W: '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53', + /* weekday number */ w: '[0-6]', + /* 2-digit year */ y: '\\d\\d', + /* 4-digit year */ Y: '\\d\\d\\d\\d', + /* whitespace */ t: ' ', + /* time zone */ z: 'Z|(?:[\\+\\-]\\d\\d:?(?:\\d\\d)?)', + }; + + var MONTH_NUMBERS = { + JAN: 0, + FEB: 1, + MAR: 2, + APR: 3, + MAY: 4, + JUN: 5, + JUL: 6, + AUG: 7, + SEP: 8, + OCT: 9, + NOV: 10, + DEC: 11, + }; + var DAY_NUMBERS_SUN_FIRST = { + SUN: 0, + MON: 1, + TUE: 2, + WED: 3, + THU: 4, + FRI: 5, + SAT: 6, + }; + var DAY_NUMBERS_MON_FIRST = { + MON: 0, + TUE: 1, + WED: 2, + THU: 3, + FRI: 4, + SAT: 5, + SUN: 6, + }; + + var capture = []; + var pattern_out = pattern + .replace(/%(.)/g, (m, c) => EQUIVALENT_MATCHERS[c] || m) + .replace(/%(.)/g, (_, c) => { + let pat = DATE_PATTERNS[c]; + if (pat) { + capture.push(c); + return `(${pat})`; + } else { + return c; + } + }) + .replace( + // any number of space or tab characters match zero or more spaces + /\s+/g, + '\\s*' + ); + + var matches = new RegExp('^' + pattern_out, 'i').exec( + UTF8ToString(buf) + ); + + function initDate() { + function fixup(value, min, max) { + return typeof value != 'number' || isNaN(value) + ? min + : value >= min + ? value <= max + ? value + : max + : min; + } + return { + year: fixup(HEAP32[(tm + 20) >> 2] + 1900, 1970, 9999), + month: fixup(HEAP32[(tm + 16) >> 2], 0, 11), + day: fixup(HEAP32[(tm + 12) >> 2], 1, 31), + hour: fixup(HEAP32[(tm + 8) >> 2], 0, 23), + min: fixup(HEAP32[(tm + 4) >> 2], 0, 59), + sec: fixup(HEAP32[tm >> 2], 0, 59), + gmtoff: 0, + }; + } + + if (matches) { + var date = initDate(); + var value; + + var getMatch = (symbol) => { + var pos = capture.indexOf(symbol); + // check if symbol appears in regexp + if (pos >= 0) { + // return matched value or null (falsy!) for non-matches + return matches[pos + 1]; + } + return; + }; + + // seconds + if ((value = getMatch('S'))) { + date.sec = Number(value); + } + + // minutes + if ((value = getMatch('M'))) { + date.min = Number(value); + } + + // hours + if ((value = getMatch('H'))) { + // 24h clock + date.hour = Number(value); + } else if ((value = getMatch('I'))) { + // AM/PM clock + var hour = Number(value); + if ((value = getMatch('p'))) { + hour += value.toUpperCase()[0] === 'P' ? 12 : 0; + } + date.hour = hour; + } + + // year + if ((value = getMatch('Y'))) { + // parse from four-digit year + date.year = Number(value); + } else if ((value = getMatch('y'))) { + // parse from two-digit year... + var year = Number(value); + if ((value = getMatch('C'))) { + // ...and century + year += Number(value) * 100; + } else { + // ...and rule-of-thumb + year += year < 69 ? 2000 : 1900; + } + date.year = year; + } + + // month + if ((value = getMatch('m'))) { + // parse from month number + date.month = Number(value) - 1; + } else if ((value = getMatch('b'))) { + // parse from month name + date.month = + MONTH_NUMBERS[value.substring(0, 3).toUpperCase()] || 0; + // TODO: derive month from day in year+year, week number+day of week+year + } + + // day + if ((value = getMatch('d'))) { + // get day of month directly + date.day = Number(value); + } else if ((value = getMatch('j'))) { + // get day of month from day of year ... + var day = Number(value); + var leapYear = isLeapYear(date.year); + for (var month = 0; month < 12; ++month) { + var daysUntilMonth = arraySum( + leapYear ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, + month - 1 + ); + if ( + day <= + daysUntilMonth + + (leapYear ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[ + month + ] + ) { + date.day = day - daysUntilMonth; + } + } + } else if ((value = getMatch('a'))) { + // get day of month from weekday ... + var weekDay = value.substring(0, 3).toUpperCase(); + if ((value = getMatch('U'))) { + // ... and week number (Sunday being first day of week) + // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. + // All days in a new year preceding the first Sunday are considered to be in week 0. + var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay]; + var weekNumber = Number(value); + + // January 1st + var janFirst = new Date(date.year, 0, 1); + var endDate; + if (janFirst.getDay() === 0) { + // Jan 1st is a Sunday, and, hence in the 1st CW + endDate = addDays( + janFirst, + weekDayNumber + 7 * (weekNumber - 1) + ); + } else { + // Jan 1st is not a Sunday, and, hence still in the 0th CW + endDate = addDays( + janFirst, + 7 - + janFirst.getDay() + + weekDayNumber + + 7 * (weekNumber - 1) + ); + } + date.day = endDate.getDate(); + date.month = endDate.getMonth(); + } else if ((value = getMatch('W'))) { + // ... and week number (Monday being first day of week) + // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. + // All days in a new year preceding the first Monday are considered to be in week 0. + var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay]; + var weekNumber = Number(value); + + // January 1st + var janFirst = new Date(date.year, 0, 1); + var endDate; + if (janFirst.getDay() === 1) { + // Jan 1st is a Monday, and, hence in the 1st CW + endDate = addDays( + janFirst, + weekDayNumber + 7 * (weekNumber - 1) + ); + } else { + // Jan 1st is not a Monday, and, hence still in the 0th CW + endDate = addDays( + janFirst, + 7 - + janFirst.getDay() + + 1 + + weekDayNumber + + 7 * (weekNumber - 1) + ); + } + + date.day = endDate.getDate(); + date.month = endDate.getMonth(); + } + } + + // time zone + if ((value = getMatch('z'))) { + // GMT offset as either 'Z' or +-HH:MM or +-HH or +-HHMM + if (value.toLowerCase() === 'z') { + date.gmtoff = 0; + } else { + var match = value.match(/^((?:\-|\+)\d\d):?(\d\d)?/); + date.gmtoff = match[1] * 3600; + if (match[2]) { + date.gmtoff += + date.gmtoff > 0 ? match[2] * 60 : -match[2] * 60; + } + } + } + + /* + tm_sec int seconds after the minute 0-61* + tm_min int minutes after the hour 0-59 + tm_hour int hours since midnight 0-23 + tm_mday int day of the month 1-31 + tm_mon int months since January 0-11 + tm_year int years since 1900 + tm_wday int days since Sunday 0-6 + tm_yday int days since January 1 0-365 + tm_isdst int Daylight Saving Time flag + tm_gmtoff long offset from GMT (seconds) + */ - + var fullDate = new Date( + date.year, + date.month, + date.day, + date.hour, + date.min, + date.sec, + 0 + ); + HEAP32[tm >> 2] = fullDate.getSeconds(); + HEAP32[(tm + 4) >> 2] = fullDate.getMinutes(); + HEAP32[(tm + 8) >> 2] = fullDate.getHours(); + HEAP32[(tm + 12) >> 2] = fullDate.getDate(); + HEAP32[(tm + 16) >> 2] = fullDate.getMonth(); + HEAP32[(tm + 20) >> 2] = fullDate.getFullYear() - 1900; + HEAP32[(tm + 24) >> 2] = fullDate.getDay(); + HEAP32[(tm + 28) >> 2] = + arraySum( + isLeapYear(fullDate.getFullYear()) + ? MONTH_DAYS_LEAP + : MONTH_DAYS_REGULAR, + fullDate.getMonth() - 1 + ) + + fullDate.getDate() - + 1; + HEAP32[(tm + 32) >> 2] = 0; + HEAP32[(tm + 36) >> 2] = date.gmtoff; + + // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F + // TODO: not sure that intArrayFromString handles all unicode characters correctly + return buf + lengthBytesUTF8(matches[0]); + } + + return 0; + }; + _strptime.sig = 'pppp'; + + function _wasm_close(socketd) { + return PHPWASM.shutdownSocket(socketd, 2); + } - var ___assert_fail = (condition, filename, line, func) => - abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']); - ___assert_fail.sig = 'vppip'; + function _wasm_setsockopt( + socketd, + level, + optionName, + optionValuePtr, + optionLen + ) { + const optionValue = HEAPU8[optionValuePtr]; + const SOL_SOCKET = 1; + const SO_KEEPALIVE = 9; + const IPPROTO_TCP = 6; + const TCP_NODELAY = 1; + const isSupported = + (level === SOL_SOCKET && optionName === SO_KEEPALIVE) || + (level === IPPROTO_TCP && optionName === TCP_NODELAY); + if (!isSupported) { + console.warn( + `Unsupported socket option: ${level}, ${optionName}, ${optionValue}` + ); + return -1; + } + const ws = PHPWASM.getAllWebSockets(socketd)[0]; + if (!ws) { + return -1; + } + ws.setSocketOpt(level, optionName, optionValuePtr); + return 0; + } - var ___asyncify_data = new WebAssembly.Global({'value': 'i32', 'mutable': true}, 0); + var runAndAbortIfError = (func) => { + try { + return func(); + } catch (e) { + abort(e); + } + }; + + var Asyncify = { + instrumentWasmImports(imports) { + var importPattern = + /^(invoke_i|invoke_ii|invoke_iii|invoke_iiii|invoke_iiiii|invoke_iiiiii|invoke_iiiiiii|invoke_iiiiiiii|invoke_iiiiiiiii|invoke_iiiiiiiiii|invoke_v|invoke_vi|invoke_vii|invoke_viidii|invoke_viii|invoke_viiii|invoke_viiiii|invoke_viiiiii|invoke_viiiiiii|invoke_viiiiiiiii|invoke_i|invoke_ii|invoke_iii|invoke_iiii|invoke_iiiii|invoke_iiiiii|invoke_iiiiiii|invoke_iiiiiiii|invoke_iiiiiiiiii|invoke_iij|invoke_iiji|invoke_iiij|invoke_iijii|invoke_iijiji|invoke_jii|invoke_jiii|invoke_viijii|invoke_vji|js_open_process|_js_open_process|_asyncjs__js_open_process|js_popen_to_file|_js_popen_to_file|_asyncjs__js_popen_to_file|__syscall_fcntl64|___syscall_fcntl64|_asyncjs___syscall_fcntl64|js_release_file_locks|_js_release_file_locks|_async_js_release_file_locks|js_flock|_js_flock|_async_js_flock|js_fd_read|_js_fd_read|fd_close|_fd_close|_asyncjs__fd_close|close|_close|js_module_onMessage|zend_hash_str_find|_js_module_onMessage|_asyncjs__js_module_onMessage|js_waitpid|_js_waitpid|_asyncjs__js_waitpid|wasm_poll_socket|_wasm_poll_socket|_asyncjs__wasm_poll_socket|_wasm_shutdown|_asyncjs__wasm_shutdown|__asyncjs__.*)$/; + + for (let [x, original] of Object.entries(imports)) { + if (typeof original == 'function') { + let isAsyncifyImport = + original.isAsync || importPattern.test(x); + } + } + }, + instrumentFunction(original) { + var wrapper = (...args) => { + Asyncify.exportCallStack.push(original); + try { + return original(...args); + } finally { + if (!ABORT) { + var top = Asyncify.exportCallStack.pop(); + Asyncify.maybeStopUnwind(); + } + } + }; + Asyncify.funcWrappers.set(original, wrapper); + wrapper.orig = original; + return wrapper; + }, + instrumentWasmExports(exports) { + var ret = {}; + for (let [x, original] of Object.entries(exports)) { + if (typeof original == 'function') { + var wrapper = Asyncify.instrumentFunction(original); + ret[x] = wrapper; + } else { + ret[x] = original; + } + } + return ret; + }, + State: { + Normal: 0, + Unwinding: 1, + Rewinding: 2, + Disabled: 3, + }, + state: 0, + StackSize: 4096, + currData: null, + handleSleepReturnValue: 0, + exportCallStack: [], + callstackFuncToId: new Map(), + callStackIdToFunc: new Map(), + funcWrappers: new Map(), + callStackId: 0, + asyncPromiseHandlers: null, + sleepCallbacks: [], + getCallStackId(func) { + if (!Asyncify.callstackFuncToId.has(func)) { + var id = Asyncify.callStackId++; + Asyncify.callstackFuncToId.set(func, id); + Asyncify.callStackIdToFunc.set(id, func); + } + return Asyncify.callstackFuncToId.get(func); + }, + 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() { + 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 (callStackIdToFunc[id] == wasm func) + // + // 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) { + HEAPU32[ptr >> 2] = stack; + HEAPU32[(ptr + 4) >> 2] = stack + stackSize; + }, + setDataRewindFunc(ptr) { + var bottomOfCallStack = Asyncify.exportCallStack[0]; + var rewindId = Asyncify.getCallStackId(bottomOfCallStack); + HEAP32[(ptr + 8) >> 2] = rewindId; + }, + getDataRewindFunc(ptr) { + var id = HEAP32[(ptr + 8) >> 2]; + var func = Asyncify.callStackIdToFunc.get(id); + return func; + }, + doRewind(ptr) { + var original = Asyncify.getDataRewindFunc(ptr); + var func = Asyncify.funcWrappers.get(original); + // Once we have rewound and the stack we no longer need to artificially + // keep the runtime alive. + runtimeKeepalivePop(); + return func(); + }, + handleSleep(startAsync) { + 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) => { + if (ABORT) return; + Asyncify.handleSleepReturnValue = handleSleepReturnValue; + reachedCallback = true; + if (!reachedAfterCallback) { + // We are happening synchronously, so no need for async. + return; + } + 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) => + 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 + return func; + }; + + var writeArrayToMemory = (array, buffer) => { + HEAP8.set(array, buffer); + }; + + /** + * @param {string|null=} returnType + * @param {Array=} argTypes + * @param {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; + 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) { + // 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. + 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; + }; + + var FS_createPath = (...args) => FS.createPath(...args); + + var FS_unlink = (...args) => FS.unlink(...args); + + var FS_createLazyFile = (...args) => FS.createLazyFile(...args); + + var FS_createDevice = (...args) => FS.createDevice(...args); + + var writeI53ToI64Clamped = (ptr, num) => { + if (num > 0x7fffffffffffffff) { + HEAPU32[ptr >> 2] = 4294967295; + HEAPU32[(ptr + 4) >> 2] = 2147483647; + } else if (num < -0x8000000000000000) { + HEAPU32[ptr >> 2] = 0; + HEAPU32[(ptr + 4) >> 2] = 2147483648; + } else { + writeI53ToI64(ptr, num); + } + }; + + var writeI53ToI64Signaling = (ptr, num) => { + if (num > 0x7fffffffffffffff || num < -0x8000000000000000) { + throw `RangeError: ${num}`; + } + writeI53ToI64(ptr, num); + }; + + var writeI53ToU64Clamped = (ptr, num) => { + if (num > 0xffffffffffffffff) { + HEAPU32[ptr >> 2] = 4294967295; + HEAPU32[(ptr + 4) >> 2] = 4294967295; + } else if (num < 0) { + HEAPU32[ptr >> 2] = 0; + HEAPU32[(ptr + 4) >> 2] = 0; + } else { + writeI53ToI64(ptr, num); + } + }; + + var writeI53ToU64Signaling = (ptr, num) => { + if (num < 0 || num > 0xffffffffffffffff) { + throw `RangeError: ${num}`; + } + writeI53ToI64(ptr, num); + }; + + var readI53FromU64 = (ptr) => { + return HEAPU32[ptr >> 2] + HEAPU32[(ptr + 4) >> 2] * 4294967296; + }; + + var convertI32PairToI53 = (lo, hi) => { + return (lo >>> 0) + hi * 4294967296; + }; + + var convertI32PairToI53Checked = (lo, hi) => { + return (hi + 0x200000) >>> 0 < 0x400001 - !!lo + ? (lo >>> 0) + hi * 4294967296 + : NaN; + }; + + var convertU32PairToI53 = (lo, hi) => { + return (lo >>> 0) + (hi >>> 0) * 4294967296; + }; + + var getTempRet0 = (val) => __emscripten_tempret_get(); + + var _stackAlloc = stackAlloc; + + var _stackSave = stackSave; + + var _stackRestore = stackSave; + + var _setTempRet0 = setTempRet0; + + var _getTempRet0 = getTempRet0; + + var ptrToString = (ptr) => { + // Convert to 32-bit unsigned value + ptr >>>= 0; + return '0x' + ptr.toString(16).padStart(8, '0'); + }; + + var _emscripten_notify_memory_growth = (memoryIndex) => { + updateMemoryViews(); + }; + _emscripten_notify_memory_growth.sig = 'vp'; + + var strError = (errno) => UTF8ToString(_strerror(errno)); + + var _endprotoent = () => { + // void endprotoent(void); + // We're not using a real protocol database so we don't do a real close. + }; + _endprotoent.sig = 'v'; + + var _getprotoent = (number) => { + // struct protoent *getprotoent(void); + // reads the next entry from the protocols 'database' or return NULL if 'eof' + if (_setprotoent.index === Protocols.list.length) { + return 0; + } + var result = Protocols.list[_setprotoent.index++]; + return result; + }; + _getprotoent.sig = 'p'; + + var Sockets = { + BUFFER_SIZE: 10240, + MAX_BUFFER_SIZE: 10485760, + nextFd: 1, + fds: {}, + nextport: 1, + maxport: 65535, + peer: null, + connections: {}, + portmap: {}, + localAddr: 4261412874, + addrPool: [ + 33554442, 50331658, 67108874, 83886090, 100663306, 117440522, + 134217738, 150994954, 167772170, 184549386, 201326602, 218103818, + 234881034, + ], + }; + + var _emscripten_run_script = (ptr) => { + eval(UTF8ToString(ptr)); + }; + _emscripten_run_script.sig = 'vp'; + + /** @suppress{checkTypes} */ + var _emscripten_run_script_int = (ptr) => { + return eval(UTF8ToString(ptr)) | 0; + }; + _emscripten_run_script_int.sig = 'ip'; + + var _emscripten_run_script_string = (ptr) => { + var s = eval(UTF8ToString(ptr)); + if (s == null) { + return 0; + } + s += ''; + var me = _emscripten_run_script_string; + me.bufferSize = lengthBytesUTF8(s) + 1; + me.buffer = _realloc(me.buffer ?? 0, me.bufferSize); + stringToUTF8(s, me.buffer, me.bufferSize); + return me.buffer; + }; + _emscripten_run_script_string.sig = 'pp'; + + var _emscripten_random = () => Math.random(); + _emscripten_random.sig = 'f'; + + var _emscripten_performance_now = () => performance.now(); + _emscripten_performance_now.sig = 'd'; + + var __emscripten_get_now_is_monotonic = () => nowIsMonotonic; + __emscripten_get_now_is_monotonic.sig = 'i'; + + var warnOnce = (text) => { + warnOnce.shown ||= {}; + if (!warnOnce.shown[text]) { + warnOnce.shown[text] = 1; + if (ENVIRONMENT_IS_NODE) text = 'warning: ' + text; + err(text); + } + }; + + var _emscripten_get_compiler_setting = (name) => + abort( + 'You must build with -sRETAIN_COMPILER_SETTINGS for getCompilerSetting or emscripten_get_compiler_setting to work' + ); + _emscripten_get_compiler_setting.sig = 'pp'; + + var _emscripten_has_asyncify = () => 1; + _emscripten_has_asyncify.sig = 'i'; + + var _emscripten_debugger = () => { + debugger; + }; + _emscripten_debugger.sig = 'v'; + + var _emscripten_print_double = (x, to, max) => { + var str = x + ''; + if (to) return stringToUTF8(str, to, max); + else return lengthBytesUTF8(str); + }; + _emscripten_print_double.sig = 'idpi'; + + var _emscripten_asm_const_double = (code, sigPtr, argbuf) => { + return runEmAsmFunction(code, sigPtr, argbuf); + }; + _emscripten_asm_const_double.sig = 'dppp'; + + var _emscripten_asm_const_ptr = (code, sigPtr, argbuf) => { + return runEmAsmFunction(code, sigPtr, argbuf); + }; + _emscripten_asm_const_ptr.sig = 'pppp'; + + var runMainThreadEmAsm = (emAsmAddr, sigPtr, argbuf, sync) => { + var args = readEmAsmArgs(sigPtr, argbuf); + return ASM_CONSTS[emAsmAddr](...args); + }; + + var _emscripten_asm_const_int_sync_on_main_thread = ( + emAsmAddr, + sigPtr, + argbuf + ) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1); + _emscripten_asm_const_int_sync_on_main_thread.sig = 'ippp'; + + var _emscripten_asm_const_ptr_sync_on_main_thread = ( + emAsmAddr, + sigPtr, + argbuf + ) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1); + _emscripten_asm_const_ptr_sync_on_main_thread.sig = 'pppp'; + + var _emscripten_asm_const_double_sync_on_main_thread = + _emscripten_asm_const_int_sync_on_main_thread; + _emscripten_asm_const_double_sync_on_main_thread.sig = 'dppp'; + + var _emscripten_asm_const_async_on_main_thread = ( + emAsmAddr, + sigPtr, + argbuf + ) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 0); + _emscripten_asm_const_async_on_main_thread.sig = 'vppp'; + + var __Unwind_Backtrace = (func, arg) => { + var trace = getCallstack(); + var parts = trace.split('\n'); + for (var i = 0; i < parts.length; i++) { + var ret = (( + a1, + a2 + ) => {}) /* a dynamic function call to signature iii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + 0, + arg + ); + if (ret !== 0) return; + } + }; + __Unwind_Backtrace.sig = 'ipp'; + + var __Unwind_GetIPInfo = (context, ipBefore) => abort('Unwind_GetIPInfo'); + __Unwind_GetIPInfo.sig = 'ppp'; + + var __Unwind_FindEnclosingFunction = (ip) => 0; + __Unwind_FindEnclosingFunction.sig = 'pp'; + + 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++; + throw exceptionLast; + }; + ___cxa_throw.sig = 'vppp'; + var __Unwind_RaiseException = (ex) => { + err('Warning: _Unwind_RaiseException is not correctly implemented'); + return ___cxa_throw(ex, 0, 0); + }; + __Unwind_RaiseException.sig = 'ip'; + + var __Unwind_DeleteException = (ex) => err('TODO: Unwind_DeleteException'); + __Unwind_DeleteException.sig = 'vp'; + + var getDynCaller = (sig, ptr, promising = false) => { + return (...args) => dynCall(sig, ptr, args, promising); + }; + + var _emscripten_exit_with_live_runtime = () => { + runtimeKeepalivePush(); + throw 'unwind'; + }; + _emscripten_exit_with_live_runtime.sig = 'v'; + + var _emscripten_force_exit = (status) => { + __emscripten_runtime_keepalive_clear(); + _exit(status); + }; + _emscripten_force_exit.sig = 'vi'; + + var _emscripten_outn = (str, len) => out(UTF8ToString(str, len)); + _emscripten_outn.sig = 'vpp'; + + var _emscripten_errn = (str, len) => err(UTF8ToString(str, len)); + _emscripten_errn.sig = 'vpp'; + + var _emscripten_throw_number = (number) => { + throw number; + }; + _emscripten_throw_number.sig = 'vd'; + + var _emscripten_throw_string = (str) => { + throw UTF8ToString(str); + }; + _emscripten_throw_string.sig = 'vp'; + + var _emscripten_runtime_keepalive_push = runtimeKeepalivePush; + _emscripten_runtime_keepalive_push.sig = 'v'; + + var _emscripten_runtime_keepalive_pop = runtimeKeepalivePop; + _emscripten_runtime_keepalive_pop.sig = 'v'; + + var _emscripten_runtime_keepalive_check = keepRuntimeAlive; + _emscripten_runtime_keepalive_check.sig = 'i'; + + var asmjsMangle = (x) => { + if (x == '__main_argc_argv') { + x = 'main'; + } + return x.startsWith('dynCall_') ? x : '_' + x; + }; + + var __emscripten_fs_load_embedded_files = (ptr) => { + do { + var name_addr = HEAPU32[ptr >> 2]; + ptr += 4; + var len = HEAPU32[ptr >> 2]; + ptr += 4; + var content = HEAPU32[ptr >> 2]; + ptr += 4; + var name = UTF8ToString(name_addr); + FS.createPath('/', PATH.dirname(name), true, true); + // canOwn this data in the filesystem, it is a slice of wasm memory that will never change + FS.createDataFile( + name, + null, + HEAP8.subarray(content, content + len), + true, + true, + true + ); + } while (HEAPU32[ptr >> 2]); + }; + __emscripten_fs_load_embedded_files.sig = 'vp'; + + var onInits = []; + + var addOnInit = (cb) => onInits.push(cb); + + var onMains = []; + + var addOnPreMain = (cb) => onMains.push(cb); + + var onExits = []; + + var addOnExit = (cb) => onExits.push(cb); + + var STACK_SIZE = 1048576; + + var STACK_ALIGN = 16; + + var POINTER_SIZE = 4; + + var ASSERTIONS = 0; + + /** + * @param {string=} returnType + * @param {Array=} argTypes + * @param {Object=} opts + */ + var cwrap = (ident, returnType, argTypes, opts) => { + // When the function takes numbers and returns a number, we can just return + // the original function + var numericArgs = + !argTypes || + argTypes.every((type) => type === 'number' || type === 'boolean'); + var numericRet = returnType !== 'string'; + if (numericRet && numericArgs && !opts) { + return getCFunc(ident); + } + return (...args) => ccall(ident, returnType, argTypes, args, opts); + }; - var ___asyncify_state = new WebAssembly.Global({'value': 'i32', 'mutable': true}, 0); + var removeFunction = (index) => { + functionsInTableMap.delete(getWasmTableEntry(index)); + setWasmTableEntry(index, null); + freeTableIndexes.push(index); + }; - var ___call_sighandler = (fp, sig) => ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(sig); - ___call_sighandler.sig = 'vpi'; + var _emscripten_math_cbrt = Math.cbrt; + _emscripten_math_cbrt.sig = 'dd'; + + var _emscripten_math_pow = Math.pow; + _emscripten_math_pow.sig = 'ddd'; + + var _emscripten_math_random = Math.random; + _emscripten_math_random.sig = 'd'; + + var _emscripten_math_sign = Math.sign; + _emscripten_math_sign.sig = 'dd'; + + var _emscripten_math_sqrt = Math.sqrt; + _emscripten_math_sqrt.sig = 'dd'; + + var _emscripten_math_exp = Math.exp; + _emscripten_math_exp.sig = 'dd'; + + var _emscripten_math_expm1 = Math.expm1; + _emscripten_math_expm1.sig = 'dd'; + + var _emscripten_math_fmod = (x, y) => x % y; + _emscripten_math_fmod.sig = 'ddd'; + + var _emscripten_math_log = Math.log; + _emscripten_math_log.sig = 'dd'; + + var _emscripten_math_log1p = Math.log1p; + _emscripten_math_log1p.sig = 'dd'; + + var _emscripten_math_log10 = Math.log10; + _emscripten_math_log10.sig = 'dd'; + + var _emscripten_math_log2 = Math.log2; + _emscripten_math_log2.sig = 'dd'; + + var _emscripten_math_round = Math.round; + _emscripten_math_round.sig = 'dd'; + + var _emscripten_math_acos = Math.acos; + _emscripten_math_acos.sig = 'dd'; + + var _emscripten_math_acosh = Math.acosh; + _emscripten_math_acosh.sig = 'dd'; + + var _emscripten_math_asin = Math.asin; + _emscripten_math_asin.sig = 'dd'; + + var _emscripten_math_asinh = Math.asinh; + _emscripten_math_asinh.sig = 'dd'; + + var _emscripten_math_atan = Math.atan; + _emscripten_math_atan.sig = 'dd'; + + var _emscripten_math_atanh = Math.atanh; + _emscripten_math_atanh.sig = 'dd'; + + var _emscripten_math_atan2 = Math.atan2; + _emscripten_math_atan2.sig = 'ddd'; + + var _emscripten_math_cos = Math.cos; + _emscripten_math_cos.sig = 'dd'; + + var _emscripten_math_cosh = Math.cosh; + _emscripten_math_cosh.sig = 'dd'; + + var _emscripten_math_hypot = (count, varargs) => { + var args = []; + for (var i = 0; i < count; ++i) { + args.push(HEAPF64[(varargs + i * 8) >> 3]); + } + return Math.hypot(...args); + }; + _emscripten_math_hypot.sig = 'dip'; + + var _emscripten_math_sin = Math.sin; + _emscripten_math_sin.sig = 'dd'; + + var _emscripten_math_sinh = Math.sinh; + _emscripten_math_sinh.sig = 'dd'; + + var _emscripten_math_tan = Math.tan; + _emscripten_math_tan.sig = 'dd'; + + var _emscripten_math_tanh = Math.tanh; + _emscripten_math_tanh.sig = 'dd'; + + var intArrayToString = (array) => { + var ret = []; + for (var i = 0; i < array.length; i++) { + var chr = array[i]; + if (chr > 0xff) { + chr &= 0xff; + } + ret.push(String.fromCharCode(chr)); + } + return ret.join(''); + }; + + var AsciiToString = (ptr) => { + var str = ''; + while (1) { + var ch = HEAPU8[ptr++]; + if (!ch) return str; + str += String.fromCharCode(ch); + } + }; + + var UTF16Decoder = globalThis.TextDecoder + ? new TextDecoder('utf-16le') + : undefined; + + var UTF16ToString = (ptr, maxBytesToRead, ignoreNul) => { + var idx = ptr >> 1; + var endIdx = findStringEnd(HEAPU16, idx, maxBytesToRead / 2, ignoreNul); + + // When using conditional TextDecoder, skip it for short strings as the overhead of the native call is not worth it. + if (endIdx - idx > 16 && UTF16Decoder) + return UTF16Decoder.decode(HEAPU16.subarray(idx, endIdx)); + + // 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 = idx; i < endIdx; ++i) { + var codeUnit = HEAPU16[i]; + // 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) => { + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + maxBytesToWrite ??= 0x7fffffff; + 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 + HEAP16[outPtr >> 1] = codeUnit; + outPtr += 2; + } + // Null-terminate the pointer to the HEAP. + HEAP16[outPtr >> 1] = 0; + return outPtr - startPtr; + }; + + var lengthBytesUTF16 = (str) => str.length * 2; + + var UTF32ToString = (ptr, maxBytesToRead, ignoreNul) => { + var str = ''; + var startIdx = ptr >> 2; + // If maxBytesToRead is not passed explicitly, it will be undefined, and this + // will always evaluate to true. This saves on code size. + for (var i = 0; !(i >= maxBytesToRead / 4); i++) { + var utf32 = HEAPU32[startIdx + i]; + if (!utf32 && !ignoreNul) break; + str += String.fromCodePoint(utf32); + } + return str; + }; + + var stringToUTF32 = (str, outPtr, maxBytesToWrite) => { + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + maxBytesToWrite ??= 0x7fffffff; + if (maxBytesToWrite < 4) return 0; + var startPtr = outPtr; + var endPtr = startPtr + maxBytesToWrite - 4; + for (var i = 0; i < str.length; ++i) { + var codePoint = str.codePointAt(i); + // Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16. + // We need to manually skip over the second code unit for correct iteration. + if (codePoint > 0xffff) { + i++; + } + HEAP32[outPtr >> 2] = codePoint; + outPtr += 4; + if (outPtr + 4 > endPtr) break; + } + // Null-terminate the pointer to the HEAP. + HEAP32[outPtr >> 2] = 0; + return outPtr - startPtr; + }; + + var lengthBytesUTF32 = (str) => { + var len = 0; + for (var i = 0; i < str.length; ++i) { + var codePoint = str.codePointAt(i); + // Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16. + // We need to manually skip over the second code unit for correct iteration. + if (codePoint > 0xffff) { + i++; + } + len += 4; + } + + return len; + }; + + var JSEvents = { + memcpy(target, src, size) { + HEAP8.set(HEAP8.subarray(src, src + size), target); + }, + removeAllEventListeners() { + while (JSEvents.eventHandlers.length) { + JSEvents._removeHandler(JSEvents.eventHandlers.length - 1); + } + JSEvents.deferredCalls = []; + }, + registerRemoveEventListeners() { + if (!JSEvents.removeEventListenersRegistered) { + addOnExit(JSEvents.removeAllEventListeners); + JSEvents.removeEventListenersRegistered = true; + } + }, + inEventHandler: 0, + deferredCalls: [], + deferCall(targetFunction, precedence, argsList) { + function arraysHaveEqualContent(arrA, arrB) { + if (arrA.length != arrB.length) return false; + + for (var i in arrA) { + if (arrA[i] != arrB[i]) return false; + } + return true; + } + // Test if the given call was already queued, and if so, don't add it again. + for (var call of JSEvents.deferredCalls) { + if ( + call.targetFunction == targetFunction && + arraysHaveEqualContent(call.argsList, argsList) + ) { + return; + } + } + JSEvents.deferredCalls.push({ + targetFunction, + precedence, + argsList, + }); + + JSEvents.deferredCalls.sort((x, y) => x.precedence < y.precedence); + }, + removeDeferredCalls(targetFunction) { + JSEvents.deferredCalls = JSEvents.deferredCalls.filter( + (call) => call.targetFunction != targetFunction + ); + }, + canPerformEventHandlerRequests() { + if (navigator.userActivation) { + // Verify against transient activation status from UserActivation API + // whether it is possible to perform a request here without needing to defer. See + // https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation + // and https://caniuse.com/mdn-api_useractivation + // At the time of writing, Firefox does not support this API: https://bugzil.la/1791079 + return navigator.userActivation.isActive; + } + + return ( + JSEvents.inEventHandler && + JSEvents.currentEventHandler.allowsDeferredCalls + ); + }, + runDeferredCalls() { + if (!JSEvents.canPerformEventHandlerRequests()) { + return; + } + var deferredCalls = JSEvents.deferredCalls; + JSEvents.deferredCalls = []; + for (var call of deferredCalls) { + call.targetFunction(...call.argsList); + } + }, + eventHandlers: [], + removeAllHandlersOnTarget: (target, eventTypeString) => { + for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if ( + JSEvents.eventHandlers[i].target == target && + (!eventTypeString || + eventTypeString == + JSEvents.eventHandlers[i].eventTypeString) + ) { + JSEvents._removeHandler(i--); + } + } + }, + _removeHandler(i) { + var h = JSEvents.eventHandlers[i]; + h.target.removeEventListener( + h.eventTypeString, + h.eventListenerFunc, + h.useCapture + ); + JSEvents.eventHandlers.splice(i, 1); + }, + registerOrRemoveHandler(eventHandler) { + if (!eventHandler.target) { + return -4; + } + if (eventHandler.callbackfunc) { + eventHandler.eventListenerFunc = function (event) { + // Increment nesting count for the event handler. + ++JSEvents.inEventHandler; + JSEvents.currentEventHandler = eventHandler; + // Process any old deferred calls the user has placed. + JSEvents.runDeferredCalls(); + // Process the actual event, calls back to user C code handler. + eventHandler.handlerFunc(event); + // Process any new deferred calls that were placed right now from this event handler. + JSEvents.runDeferredCalls(); + // Out of event handler - restore nesting count. + --JSEvents.inEventHandler; + }; + + eventHandler.target.addEventListener( + eventHandler.eventTypeString, + eventHandler.eventListenerFunc, + eventHandler.useCapture + ); + JSEvents.eventHandlers.push(eventHandler); + JSEvents.registerRemoveEventListeners(); + } else { + for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if ( + JSEvents.eventHandlers[i].target == + eventHandler.target && + JSEvents.eventHandlers[i].eventTypeString == + eventHandler.eventTypeString + ) { + JSEvents._removeHandler(i--); + } + } + } + return 0; + }, + getNodeNameForTarget(target) { + if (!target) return ''; + if (target == window) return '#window'; + if (target == screen) return '#screen'; + return target?.nodeName || ''; + }, + fullscreenEnabled() { + return document.fullscreenEnabled; + }, + }; + + function getFullscreenElement() { + return ( + document.fullscreenElement || + document.mozFullScreenElement || + document.webkitFullscreenElement || + document.webkitCurrentFullScreenElement || + document.msFullscreenElement + ); + } - var exceptionLast = 0; - - 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) { - HEAPU32[(((this.ptr)+(4))>>2)] = type; - } - - get_type() { - return HEAPU32[(((this.ptr)+(4))>>2)]; - } - - set_destructor(destructor) { - HEAPU32[(((this.ptr)+(8))>>2)] = destructor; - } - - get_destructor() { - return HEAPU32[(((this.ptr)+(8))>>2)]; - } - - set_caught(caught) { - caught = caught ? 1 : 0; - HEAP8[(this.ptr)+(12)] = caught; - } - - get_caught() { - return HEAP8[(this.ptr)+(12)] != 0; - } - - set_rethrown(rethrown) { - rethrown = rethrown ? 1 : 0; - HEAP8[(this.ptr)+(13)] = rethrown; - } - - get_rethrown() { - return HEAP8[(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) { - HEAPU32[(((this.ptr)+(16))>>2)] = adjustedPtr; - } - - get_adjusted_ptr() { - return HEAPU32[(((this.ptr)+(16))>>2)]; - } - } - - - var setTempRet0 = (val) => __emscripten_tempret_set(val); - var findMatchingCatch = (args) => { - var thrown = - exceptionLast; - if (!thrown) { - // just pass through the null ptr - setTempRet0(0); - return 0; - } - var info = new ExceptionInfo(thrown); - info.set_adjusted_ptr(thrown); - var thrownType = info.get_type(); - if (!thrownType) { - // just pass through the thrown ptr - setTempRet0(0); - return thrown; - } - - // can_catch receives a **, add indirection - // The different catch blocks are denoted by different types. - // Due to inheritance, those types may not precisely match the - // type of the thrown object. Find one which matches, and - // return the type of the catch block which should be called. - for (var caughtType of args) { - if (caughtType === 0 || caughtType === thrownType) { - // Catch all clause matched or exactly the same type is caught - break; - } - var adjusted_ptr_addr = info.ptr + 16; - if (___cxa_can_catch(caughtType, thrownType, adjusted_ptr_addr)) { - setTempRet0(caughtType); - return thrown; - } - } - setTempRet0(thrownType); - return thrown; - }; - var ___cxa_find_matching_catch_2 = () => findMatchingCatch([]); - ___cxa_find_matching_catch_2.sig = 'p'; - - var ___resumeException = (ptr) => { - if (!exceptionLast) { - exceptionLast = ptr; - } - throw exceptionLast; - }; - ___resumeException.sig = 'vp'; - - - - 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) { - HEAPU32[((buf)>>2)] = stat.dev; - HEAPU32[(((buf)+(4))>>2)] = stat.mode; - HEAPU32[(((buf)+(8))>>2)] = stat.nlink; - HEAPU32[(((buf)+(12))>>2)] = stat.uid; - HEAPU32[(((buf)+(16))>>2)] = stat.gid; - HEAPU32[(((buf)+(20))>>2)] = stat.rdev; - HEAP64[(((buf)+(24))>>3)] = BigInt(stat.size); - HEAP32[(((buf)+(32))>>2)] = 4096; - HEAP32[(((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 / 1000)); - HEAPU32[(((buf)+(48))>>2)] = (atime % 1000) * 1000 * 1000; - HEAP64[(((buf)+(56))>>3)] = BigInt(Math.floor(mtime / 1000)); - HEAPU32[(((buf)+(64))>>2)] = (mtime % 1000) * 1000 * 1000; - HEAP64[(((buf)+(72))>>3)] = BigInt(Math.floor(ctime / 1000)); - HEAPU32[(((buf)+(80))>>2)] = (ctime % 1000) * 1000 * 1000; - HEAP64[(((buf)+(88))>>3)] = BigInt(stat.ino); - return 0; - }, - writeStatFs(buf, stats) { - HEAPU32[(((buf)+(4))>>2)] = stats.bsize; - HEAPU32[(((buf)+(60))>>2)] = stats.bsize; - HEAP64[(((buf)+(8))>>3)] = BigInt(stats.blocks); - HEAP64[(((buf)+(16))>>3)] = BigInt(stats.bfree); - HEAP64[(((buf)+(24))>>3)] = BigInt(stats.bavail); - HEAP64[(((buf)+(32))>>3)] = BigInt(stats.files); - HEAP64[(((buf)+(40))>>3)] = BigInt(stats.ffree); - HEAPU32[(((buf)+(48))>>2)] = stats.fsid; - HEAPU32[(((buf)+(64))>>2)] = stats.flags; // ST_NOSUID - HEAPU32[(((buf)+(56))>>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 = HEAPU8.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; - }, - }; - var ___syscall__newselect = function (nfds, readfds, writefds, exceptfds, timeout) { - try { - - // readfds are supported, - // writefds checks socket open status - // exceptfds are supported, although on web, such exceptional conditions never arise in web sockets - // and so the exceptfds list will always return empty. - // timeout is supported, although on SOCKFS and PIPEFS these are ignored and always treated as 0 - fully async - - var total = 0; - - var srcReadLow = (readfds ? HEAP32[((readfds)>>2)] : 0), - srcReadHigh = (readfds ? HEAP32[(((readfds)+(4))>>2)] : 0); - var srcWriteLow = (writefds ? HEAP32[((writefds)>>2)] : 0), - srcWriteHigh = (writefds ? HEAP32[(((writefds)+(4))>>2)] : 0); - var srcExceptLow = (exceptfds ? HEAP32[((exceptfds)>>2)] : 0), - srcExceptHigh = (exceptfds ? HEAP32[(((exceptfds)+(4))>>2)] : 0); - - var dstReadLow = 0, - dstReadHigh = 0; - var dstWriteLow = 0, - dstWriteHigh = 0; - var dstExceptLow = 0, - dstExceptHigh = 0; - - var allLow = (readfds ? HEAP32[((readfds)>>2)] : 0) | - (writefds ? HEAP32[((writefds)>>2)] : 0) | - (exceptfds ? HEAP32[((exceptfds)>>2)] : 0); - var allHigh = (readfds ? HEAP32[(((readfds)+(4))>>2)] : 0) | - (writefds ? HEAP32[(((writefds)+(4))>>2)] : 0) | - (exceptfds ? HEAP32[(((exceptfds)+(4))>>2)] : 0); - - var check = (fd, low, high, val) => fd < 32 ? (low & val) : (high & val); - - for (var fd = 0; fd < nfds; fd++) { - var mask = 1 << (fd % 32); - if (!(check(fd, allLow, allHigh, mask))) { - continue; // index isn't in the set - } - - var stream = SYSCALLS.getStreamFromFD(fd); - - var flags = SYSCALLS.DEFAULT_POLLMASK; - - if (stream.stream_ops?.poll) { - var timeoutInMillis = -1; - if (timeout) { - // select(2) is declared to accept "struct timeval { time_t tv_sec; suseconds_t tv_usec; }". - // However, musl passes the two values to the syscall as an array of long values. - // Note that sizeof(time_t) != sizeof(long) in wasm32. The former is 8, while the latter is 4. - // This means using "C_STRUCTS.timeval.tv_usec" leads to a wrong offset. - // So, instead, we use POINTER_SIZE. - var tv_sec = (readfds ? HEAP32[((timeout)>>2)] : 0), - tv_usec = (readfds ? HEAP32[(((timeout)+(4))>>2)] : 0); - timeoutInMillis = (tv_sec + tv_usec / 1000000) * 1000; - } - flags = stream.stream_ops.poll(stream, timeoutInMillis); - } - - if ((flags & 1) && check(fd, srcReadLow, srcReadHigh, mask)) { - fd < 32 ? (dstReadLow = dstReadLow | mask) : (dstReadHigh = dstReadHigh | mask); - total++; - } - if ((flags & 4) && check(fd, srcWriteLow, srcWriteHigh, mask)) { - fd < 32 ? (dstWriteLow = dstWriteLow | mask) : (dstWriteHigh = dstWriteHigh | mask); - total++; - } - if ((flags & 2) && check(fd, srcExceptLow, srcExceptHigh, mask)) { - fd < 32 ? (dstExceptLow = dstExceptLow | mask) : (dstExceptHigh = dstExceptHigh | mask); - total++; - } - } - - if (readfds) { - HEAP32[((readfds)>>2)] = dstReadLow; - HEAP32[(((readfds)+(4))>>2)] = dstReadHigh; - } - if (writefds) { - HEAP32[((writefds)>>2)] = dstWriteLow; - HEAP32[(((writefds)+(4))>>2)] = dstWriteHigh; - } - if (exceptfds) { - HEAP32[((exceptfds)>>2)] = dstExceptLow; - HEAP32[(((exceptfds)+(4))>>2)] = dstExceptHigh; - } - - return total; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - }; - ___syscall__newselect.sig = 'iipppp'; - - var SOCKFS = { - websocketArgs:{ - }, - callbacks:{ - }, - on(event, callback) { - SOCKFS.callbacks[event] = callback; - }, - emit(event, param) { - SOCKFS.callbacks[event]?.(param); - }, - mount(mount) { - // The incomming Module['websocket'] can be used for configuring - // configuring subprotocol/url, etc - SOCKFS.websocketArgs = Module['websocket'] || {}; - // Add the Event registration mechanism to the exported websocket configuration - // object so we can register network callbacks from native JavaScript too. - // For more documentation see system/include/emscripten/emscripten.h - (Module['websocket'] ??= {})['on'] = SOCKFS.on; - - return FS.createNode(null, '/', 16895, 0); - }, - createSocket(family, type, protocol) { - // Emscripten only supports AF_INET - if (family != 2) { - throw new FS.ErrnoError(5); - } - type &= ~526336; // Some applications may pass it; it makes no sense for a single process. - // Emscripten only supports SOCK_STREAM and SOCK_DGRAM - if (type != 1 && type != 2) { - throw new FS.ErrnoError(28); - } - var streaming = type == 1; - if (streaming && protocol && protocol != 6) { - throw new FS.ErrnoError(66); // if SOCK_STREAM, must be tcp or 0. - } - - // create our internal socket structure - var sock = { - family, - type, - protocol, - server: null, - error: null, // Used in getsockopt for SOL_SOCKET/SO_ERROR test - peers: {}, - pending: [], - recv_queue: [], - sock_ops: SOCKFS.websocket_sock_ops - }; - - // create the filesystem node to store the socket structure - var name = SOCKFS.nextname(); - var node = FS.createNode(SOCKFS.root, name, 49152, 0); - node.sock = sock; - - // and the wrapping stream that enables library functions such - // as read and write to indirectly interact with the socket - var stream = FS.createStream({ - path: name, - node, - flags: 2, - seekable: false, - stream_ops: SOCKFS.stream_ops - }); - - // map the new stream to the socket structure (sockets have a 1:1 - // relationship with a stream) - sock.stream = stream; - - return sock; - }, - getSocket(fd) { - var stream = FS.getStream(fd); - if (!stream || !FS.isSocket(stream.node.mode)) { - return null; - } - return stream.node.sock; - }, - stream_ops:{ - poll(stream) { - var sock = stream.node.sock; - return sock.sock_ops.poll(sock); - }, - ioctl(stream, request, varargs) { - var sock = stream.node.sock; - return sock.sock_ops.ioctl(sock, request, varargs); - }, - read(stream, buffer, offset, length, position /* ignored */) { - var sock = stream.node.sock; - var msg = sock.sock_ops.recvmsg(sock, length); - if (!msg) { - // socket is closed - return 0; - } - buffer.set(msg.buffer, offset); - return msg.buffer.length; - }, - write(stream, buffer, offset, length, position /* ignored */) { - var sock = stream.node.sock; - return sock.sock_ops.sendmsg(sock, buffer, offset, length); - }, - close(stream) { - var sock = stream.node.sock; - sock.sock_ops.close(sock); - }, - }, - nextname() { - if (!SOCKFS.nextname.current) { - SOCKFS.nextname.current = 0; - } - return `socket[${SOCKFS.nextname.current++}]`; - }, - websocket_sock_ops:{ - createPeer(sock, addr, port) { - var ws; - - if (typeof addr == 'object') { - ws = addr; - addr = null; - port = null; - } - - if (ws) { - // for sockets that've already connected (e.g. we're the server) - // we can inspect the _socket property for the address - if (ws._socket) { - addr = ws._socket.remoteAddress; - port = ws._socket.remotePort; - } - // if we're just now initializing a connection to the remote, - // inspect the url property - else { - var result = /ws[s]?:\/\/([^:]+):(\d+)/.exec(ws.url); - if (!result) { - throw new Error('WebSocket URL must be in the format ws(s)://address:port'); - } - addr = result[1]; - port = parseInt(result[2], 10); - } - } else { - // create the actual websocket object and connect - try { - // The default value is 'ws://' the replace is needed because the compiler replaces '//' comments with '#' - // comments without checking context, so we'd end up with ws:#, the replace swaps the '#' for '//' again. - var url = 'ws://'.replace('#', '//'); - // Make the WebSocket subprotocol (Sec-WebSocket-Protocol) default to binary if no configuration is set. - var subProtocols = 'binary'; // The default value is 'binary' - // The default WebSocket options - var opts = undefined; - - // Fetch runtime WebSocket URL config. - if("function"===typeof SOCKFS.websocketArgs["url"]) { -url = SOCKFS.websocketArgs["url"](...arguments); -}else if ("string" === typeof SOCKFS.websocketArgs["url"]) { - url = SOCKFS.websocketArgs['url']; - } - // Fetch runtime WebSocket subprotocol config. - if (SOCKFS.websocketArgs['subprotocol']) { - subProtocols = SOCKFS.websocketArgs['subprotocol']; - } else if (SOCKFS.websocketArgs['subprotocol'] === null) { - subProtocols = 'null' - } - - if (url === 'ws://' || url === 'wss://') { // Is the supplied URL config just a prefix, if so complete it. - var parts = addr.split('/'); - url = url + parts[0] + ":" + port + "/" + parts.slice(1).join('/'); - } - - if (subProtocols !== 'null') { - // The regex trims the string (removes spaces at the beginning and end, then splits the string by - // , into an Array. Whitespace removal is important for Websockify and ws. - subProtocols = subProtocols.replace(/^ +| +$/g,"").split(/ *, */); - - opts = subProtocols; - } - - // If node we use the ws library. - var WebSocketConstructor; - if (ENVIRONMENT_IS_NODE) { - WebSocketConstructor = /** @type{(typeof WebSocket)} */(require('ws')); - } else - { - WebSocketConstructor = WebSocket; - } - if (Module['websocket']['decorator']) {WebSocketConstructor = Module['websocket']['decorator'](WebSocketConstructor);}ws = new WebSocketConstructor(url, opts); - ws.binaryType = 'arraybuffer'; - } catch (e) { - throw new FS.ErrnoError(23); - } - } - - var peer = { - addr, - port, - socket: ws, - msg_send_queue: [] - }; - - SOCKFS.websocket_sock_ops.addPeer(sock, peer); - SOCKFS.websocket_sock_ops.handlePeerEvents(sock, peer); - - // if this is a bound dgram socket, send the port number first to allow - // us to override the ephemeral port reported to us by remotePort on the - // remote end. - if (sock.type === 2 && typeof sock.sport != 'undefined') { - peer.msg_send_queue.push(new Uint8Array([ - 255, 255, 255, 255, - 'p'.charCodeAt(0), 'o'.charCodeAt(0), 'r'.charCodeAt(0), 't'.charCodeAt(0), - ((sock.sport & 0xff00) >> 8) , (sock.sport & 0xff) - ])); - } - - return peer; - }, - getPeer(sock, addr, port) { - return sock.peers[addr + ':' + port]; - }, - addPeer(sock, peer) { - sock.peers[peer.addr + ':' + peer.port] = peer; - }, - removePeer(sock, peer) { - delete sock.peers[peer.addr + ':' + peer.port]; - }, - handlePeerEvents(sock, peer) { - var first = true; - - var handleOpen = function () { - - sock.connecting = false; - SOCKFS.emit('open', sock.stream.fd); - - try { - var queued = peer.msg_send_queue.shift(); - while (queued) { - peer.socket.send(queued); - queued = peer.msg_send_queue.shift(); - } - } catch (e) { - // not much we can do here in the way of proper error handling as we've already - // lied and said this data was sent. shut it down. - peer.socket.close(); - } - }; - - function handleMessage(data) { - if (typeof data == 'string') { - var encoder = new TextEncoder(); // should be utf-8 - data = encoder.encode(data); // make a typed array from the string - } else { - if (data.byteLength == 0) { - // An empty ArrayBuffer will emit a pseudo disconnect event - // as recv/recvmsg will return zero which indicates that a socket - // has performed a shutdown although the connection has not been disconnected yet. - return; - } - data = new Uint8Array(data); // make a typed array view on the array buffer - } - - // if this is the port message, override the peer's port with it - var wasfirst = first; - first = false; - if (wasfirst && - data.length === 10 && - data[0] === 255 && data[1] === 255 && data[2] === 255 && data[3] === 255 && - data[4] === 'p'.charCodeAt(0) && data[5] === 'o'.charCodeAt(0) && data[6] === 'r'.charCodeAt(0) && data[7] === 't'.charCodeAt(0)) { - // update the peer's port and it's key in the peer map - var newport = ((data[8] << 8) | data[9]); - SOCKFS.websocket_sock_ops.removePeer(sock, peer); - peer.port = newport; - SOCKFS.websocket_sock_ops.addPeer(sock, peer); - return; - } - - sock.recv_queue.push({ addr: peer.addr, port: peer.port, data: data }); - SOCKFS.emit('message', sock.stream.fd); - }; - - if (ENVIRONMENT_IS_NODE) { - peer.socket.on('open', handleOpen); - peer.socket.on('message', function(data, isBinary) { - if (!isBinary) { - return; - } - handleMessage((new Uint8Array(data)).buffer); // copy from node Buffer -> ArrayBuffer - }); - peer.socket.on('close', function() { - SOCKFS.emit('close', sock.stream.fd); - }); - peer.socket.on('error', function(error) { - // Although the ws library may pass errors that may be more descriptive than - // ECONNREFUSED they are not necessarily the expected error code e.g. - // ENOTFOUND on getaddrinfo seems to be node.js specific, so using ECONNREFUSED - // is still probably the most useful thing to do. - sock.error = 14; // Used in getsockopt for SOL_SOCKET/SO_ERROR test. - SOCKFS.emit('error', [sock.stream.fd, sock.error, 'ECONNREFUSED: Connection refused']); - // don't throw - }); - } else { - peer.socket.onopen = handleOpen; - peer.socket.onclose = function() { - SOCKFS.emit('close', sock.stream.fd); - }; - peer.socket.onmessage = function peer_socket_onmessage(event) { - handleMessage(event.data); - }; - peer.socket.onerror = function(error) { - // The WebSocket spec only allows a 'simple event' to be thrown on error, - // so we only really know as much as ECONNREFUSED. - sock.error = 14; // Used in getsockopt for SOL_SOCKET/SO_ERROR test. - SOCKFS.emit('error', [sock.stream.fd, sock.error, 'ECONNREFUSED: Connection refused']); - }; - } - }, - poll(sock) { - if (sock.type === 1 && sock.server) { - // listen sockets should only say they're available for reading - // if there are pending clients. - return sock.pending.length ? (64 | 1) : 0; - } - - var mask = 0; - var dest = sock.type === 1 ? // we only care about the socket state for connection-based sockets - SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport) : - null; - - if (sock.recv_queue.length || - !dest || // connection-less sockets are always ready to read - (dest && dest.socket.readyState === dest.socket.CLOSING) || - (dest && dest.socket.readyState === dest.socket.CLOSED)) { // let recv return 0 once closed - mask |= (64 | 1); - } - - if (!dest || // connection-less sockets are always ready to write - (dest && dest.socket.readyState === dest.socket.OPEN)) { - mask |= 4; - } - - if ((dest && dest.socket.readyState === dest.socket.CLOSING) || - (dest && dest.socket.readyState === dest.socket.CLOSED)) { - // When an non-blocking connect fails mark the socket as writable. - // Its up to the calling code to then use getsockopt with SO_ERROR to - // retrieve the error. - // See https://man7.org/linux/man-pages/man2/connect.2.html - if (sock.connecting) { - mask |= 4; - } else { - mask |= 16; - } - } - - return mask; - }, - ioctl(sock, request, arg) { - switch (request) { - case 21531: - var bytes = 0; - if (sock.recv_queue.length) { - bytes = sock.recv_queue[0].data.length; - } - HEAP32[((arg)>>2)] = bytes; - return 0; - case 21537: - var on = HEAP32[((arg)>>2)]; - if (on) { - sock.stream.flags |= 2048; - } else { - sock.stream.flags &= ~2048; - } - return 0; - default: - return 28; - } - }, - close(sock) { - // if we've spawned a listen server, close it - if (sock.server) { - try { - sock.server.close(); - } catch (e) { - } - sock.server = null; - } - // close any peer connections - for (var peer of Object.values(sock.peers)) { - try { - peer.socket.close(); - } catch (e) { - } - SOCKFS.websocket_sock_ops.removePeer(sock, peer); - } - return 0; - }, - bind(sock, addr, port) { - if (typeof sock.saddr != 'undefined' || typeof sock.sport != 'undefined') { - throw new FS.ErrnoError(28); // already bound - } - sock.saddr = addr; - sock.sport = port; - // in order to emulate dgram sockets, we need to launch a listen server when - // binding on a connection-less socket - // note: this is only required on the server side - if (sock.type === 2) { - // close the existing server if it exists - if (sock.server) { - sock.server.close(); - sock.server = null; - } - // swallow error operation not supported error that occurs when binding in the - // browser where this isn't supported - try { - sock.sock_ops.listen(sock, 0); - } catch (e) { - if (!(e.name === 'ErrnoError')) throw e; - if (e.errno !== 138) throw e; - } - } - }, - connect(sock, addr, port) { - if (sock.server) { - throw new FS.ErrnoError(138); - } - - // TODO autobind - // if (!sock.addr && sock.type == 2) { - // } - - // early out if we're already connected / in the middle of connecting - if (typeof sock.daddr != 'undefined' && typeof sock.dport != 'undefined') { - var dest = SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport); - if (dest) { - if (dest.socket.readyState === dest.socket.CONNECTING) { - throw new FS.ErrnoError(7); - } else { - throw new FS.ErrnoError(30); - } - } - } - - // add the socket to our peer list and set our - // destination address / port to match - var peer = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port); - sock.daddr = peer.addr; - sock.dport = peer.port; - - // because we cannot synchronously block to wait for the WebSocket - // connection to complete, we return here pretending that the connection - // was a success. - sock.connecting = true; - }, - listen(sock, backlog) { - if (!ENVIRONMENT_IS_NODE) { - throw new FS.ErrnoError(138); - } - if (sock.server) { - throw new FS.ErrnoError(28); // already listening - } - var WebSocketServer = require('ws').Server; - var host = sock.saddr; - if (Module['websocket']['serverDecorator']) {WebSocketServer = Module['websocket']['serverDecorator'](WebSocketServer);}sock.server = new WebSocketServer({ - host, - port: sock.sport - // TODO support backlog - }); - SOCKFS.emit('listen', sock.stream.fd); // Send Event with listen fd. - - sock.server.on('connection', function(ws) { - if (sock.type === 1) { - var newsock = SOCKFS.createSocket(sock.family, sock.type, sock.protocol); - - // create a peer on the new socket - var peer = SOCKFS.websocket_sock_ops.createPeer(newsock, ws); - newsock.daddr = peer.addr; - newsock.dport = peer.port; - - // push to queue for accept to pick up - sock.pending.push(newsock); - SOCKFS.emit('connection', newsock.stream.fd); - } else { - // create a peer on the listen socket so calling sendto - // with the listen socket and an address will resolve - // to the correct client - SOCKFS.websocket_sock_ops.createPeer(sock, ws); - SOCKFS.emit('connection', sock.stream.fd); - } - }); - sock.server.on('close', function() { - SOCKFS.emit('close', sock.stream.fd); - sock.server = null; - }); - sock.server.on('error', function(error) { - // Although the ws library may pass errors that may be more descriptive than - // ECONNREFUSED they are not necessarily the expected error code e.g. - // ENOTFOUND on getaddrinfo seems to be node.js specific, so using EHOSTUNREACH - // is still probably the most useful thing to do. This error shouldn't - // occur in a well written app as errors should get trapped in the compiled - // app's own getaddrinfo call. - sock.error = 23; // Used in getsockopt for SOL_SOCKET/SO_ERROR test. - SOCKFS.emit('error', [sock.stream.fd, sock.error, 'EHOSTUNREACH: Host is unreachable']); - // don't throw - }); - }, - accept(listensock) { - if (!listensock.server || !listensock.pending.length) { - throw new FS.ErrnoError(28); - } - var newsock = listensock.pending.shift(); - newsock.stream.flags = listensock.stream.flags; - return newsock; - }, - getname(sock, peer) { - var addr, port; - if (peer) { - if (sock.daddr === undefined || sock.dport === undefined) { - throw new FS.ErrnoError(53); - } - addr = sock.daddr; - port = sock.dport; - } else { - // TODO saddr and sport will be set for bind()'d UDP sockets, but what - // should we be returning for TCP sockets that've been connect()'d? - addr = sock.saddr || 0; - port = sock.sport || 0; - } - return { addr, port }; - }, - sendmsg(sock, buffer, offset, length, addr, port) { - if (sock.type === 2) { - // connection-less sockets will honor the message address, - // and otherwise fall back to the bound destination address - if (addr === undefined || port === undefined) { - addr = sock.daddr; - port = sock.dport; - } - // if there was no address to fall back to, error out - if (addr === undefined || port === undefined) { - throw new FS.ErrnoError(17); - } - } else { - // connection-based sockets will only use the bound - addr = sock.daddr; - port = sock.dport; - } - - // find the peer for the destination address - var dest = SOCKFS.websocket_sock_ops.getPeer(sock, addr, port); - - // early out if not connected with a connection-based socket - if (sock.type === 1) { - if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) { - throw new FS.ErrnoError(53); - } - } - - // create a copy of the incoming data to send, as the WebSocket API - // doesn't work entirely with an ArrayBufferView, it'll just send - // the entire underlying buffer - if (ArrayBuffer.isView(buffer)) { - offset += buffer.byteOffset; - buffer = buffer.buffer; - } - - var data = buffer.slice(offset, offset + length); - - // if we don't have a cached connectionless UDP datagram connection, or - // the TCP socket is still connecting, queue the message to be sent upon - // connect, and lie, saying the data was sent now. - if (!dest || dest.socket.readyState !== dest.socket.OPEN) { - // if we're not connected, open a new connection - if (sock.type === 2) { - if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) { - dest = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port); - } - } - dest.msg_send_queue.push(data); - return length; - } - - try { - // send the actual data - dest.socket.send(data); - return length; - } catch (e) { - throw new FS.ErrnoError(28); - } - }, - recvmsg(sock, length, flags) { - // http://pubs.opengroup.org/onlinepubs/7908799/xns/recvmsg.html - if (sock.type === 1 && sock.server) { - // tcp servers should not be recv()'ing on the listen socket - throw new FS.ErrnoError(53); - } - - var queued = sock.recv_queue.shift(); - if (!queued) { - if (sock.type === 1) { - var dest = SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport); - - if (!dest) { - // if we have a destination address but are not connected, error out - throw new FS.ErrnoError(53); - } - if (dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) { - // return null if the socket has closed - return null; - } - // else, our socket is in a valid state but truly has nothing available - throw new FS.ErrnoError(6); - } - throw new FS.ErrnoError(6); - } - - // queued.data will be an ArrayBuffer if it's unadulterated, but if it's - // requeued TCP data it'll be an ArrayBufferView - var queuedLength = queued.data.byteLength || queued.data.length; - var queuedOffset = queued.data.byteOffset || 0; - var queuedBuffer = queued.data.buffer || queued.data; - var bytesRead = Math.min(length, queuedLength); - var res = { - buffer: new Uint8Array(queuedBuffer, queuedOffset, bytesRead), - addr: queued.addr, - port: queued.port - }; - - // push back any unread data for TCP connections - if (flags&2) {bytesRead = 0;} if (sock.type === 1 && bytesRead < queuedLength) { - var bytesRemaining = queuedLength - bytesRead; - queued.data = new Uint8Array(queuedBuffer, queuedOffset + bytesRead, bytesRemaining); - sock.recv_queue.unshift(queued); - } - - return res; - }, - }, - }; - - var getSocketFromFD = (fd) => { - var socket = SOCKFS.getSocket(fd); - if (!socket) throw new FS.ErrnoError(8); - return socket; - }; - - var inetPton4 = (str) => { - var b = str.split('.'); - for (var i = 0; i < 4; i++) { - var tmp = Number(b[i]); - if (isNaN(tmp)) return null; - b[i] = tmp; - } - return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0; - }; - - var inetPton6 = (str) => { - var words; - var w, offset, z, i; - /* http://home.deds.nl/~aeron/regex/ */ - var valid6regx = /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i - var parts = []; - if (!valid6regx.test(str)) { - return null; - } - if (str === "::") { - return [0, 0, 0, 0, 0, 0, 0, 0]; - } - // Z placeholder to keep track of zeros when splitting the string on ":" - if (str.startsWith("::")) { - str = str.replace("::", "Z:"); // leading zeros case - } else { - str = str.replace("::", ":Z:"); - } - - if (str.indexOf(".") > 0) { - // parse IPv4 embedded stress - str = str.replace(new RegExp('[.]', 'g'), ":"); - words = str.split(":"); - words[words.length-4] = Number(words[words.length-4]) + Number(words[words.length-3])*256; - words[words.length-3] = Number(words[words.length-2]) + Number(words[words.length-1])*256; - words = words.slice(0, words.length-2); - } else { - words = str.split(":"); - } - - offset = 0; z = 0; - for (w=0; w < words.length; w++) { - if (typeof words[w] == 'string') { - if (words[w] === 'Z') { - // compressed zeros - write appropriate number of zero words - for (z = 0; z < (8 - words.length+1); z++) { - parts[w+z] = 0; - } - offset = z-1; - } else { - // parse hex to field to 16-bit value and write it in network byte-order - parts[w+offset] = _htons(parseInt(words[w],16)); - } - } else { - // parsed IPv4 words - parts[w+offset] = words[w]; - } - } - return [ - (parts[1] << 16) | parts[0], - (parts[3] << 16) | parts[2], - (parts[5] << 16) | parts[4], - (parts[7] << 16) | parts[6] - ]; - }; - - - /** @param {number=} addrlen */ - var writeSockaddr = (sa, family, addr, port, addrlen) => { - switch (family) { - case 2: - addr = inetPton4(addr); - zeroMemory(sa, 16); - if (addrlen) { - HEAP32[((addrlen)>>2)] = 16; - } - HEAP16[((sa)>>1)] = family; - HEAP32[(((sa)+(4))>>2)] = addr; - HEAP16[(((sa)+(2))>>1)] = _htons(port); - break; - case 10: - addr = inetPton6(addr); - zeroMemory(sa, 28); - if (addrlen) { - HEAP32[((addrlen)>>2)] = 28; - } - HEAP32[((sa)>>2)] = family; - HEAP32[(((sa)+(8))>>2)] = addr[0]; - HEAP32[(((sa)+(12))>>2)] = addr[1]; - HEAP32[(((sa)+(16))>>2)] = addr[2]; - HEAP32[(((sa)+(20))>>2)] = addr[3]; - HEAP16[(((sa)+(2))>>1)] = _htons(port); - break; - default: - return 5; - } - return 0; - }; - - - var DNS = { - address_map:{ - id:1, - addrs:{ - }, - names:{ - }, - }, - lookup_name(name) { - // If the name is already a valid ipv4 / ipv6 address, don't generate a fake one. - var res = inetPton4(name); - if (res !== null) { - return name; - } - res = inetPton6(name); - if (res !== null) { - return name; - } - - // See if this name is already mapped. - var addr; - - if (DNS.address_map.addrs[name]) { - addr = DNS.address_map.addrs[name]; - } else { - var id = DNS.address_map.id++; - - addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00); - - DNS.address_map.names[addr] = name; - DNS.address_map.addrs[name] = addr; - } - - return addr; - }, - lookup_addr(addr) { - if (DNS.address_map.names[addr]) { - return DNS.address_map.names[addr]; - } - - return null; - }, - }; - function ___syscall_accept4(fd, addr, addrlen, flags, d1, d2) { - try { - - var sock = getSocketFromFD(fd); - var newsock = sock.sock_ops.accept(sock); - if (addr) { - var errno = writeSockaddr(addr, newsock.family, DNS.lookup_name(newsock.daddr), newsock.dport, addrlen); - } - return newsock.stream.fd; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_accept4.sig = 'iippiii'; - - - var inetNtop4 = (addr) => - (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff); - - - var inetNtop6 = (ints) => { - // ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4 - // Format for IPv4 compatible and mapped 128-bit IPv6 Addresses - // 128-bits are split into eight 16-bit words - // stored in network byte order (big-endian) - // | 80 bits | 16 | 32 bits | - // +-----------------------------------------------------------------+ - // | 10 bytes | 2 | 4 bytes | - // +--------------------------------------+--------------------------+ - // + 5 words | 1 | 2 words | - // +--------------------------------------+--------------------------+ - // |0000..............................0000|0000| IPv4 ADDRESS | (compatible) - // +--------------------------------------+----+---------------------+ - // |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped) - // +--------------------------------------+----+---------------------+ - var str = ""; - var word = 0; - var longest = 0; - var lastzero = 0; - var zstart = 0; - var len = 0; - var i = 0; - var parts = [ - ints[0] & 0xffff, - (ints[0] >> 16), - ints[1] & 0xffff, - (ints[1] >> 16), - ints[2] & 0xffff, - (ints[2] >> 16), - ints[3] & 0xffff, - (ints[3] >> 16) - ]; - - // Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses - - var hasipv4 = true; - var v4part = ""; - // check if the 10 high-order bytes are all zeros (first 5 words) - for (i = 0; i < 5; i++) { - if (parts[i] !== 0) { hasipv4 = false; break; } - } - - if (hasipv4) { - // low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words) - v4part = inetNtop4(parts[6] | (parts[7] << 16)); - // IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word) - if (parts[5] === -1) { - str = "::ffff:"; - str += v4part; - return str; - } - // IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word) - if (parts[5] === 0) { - str = "::"; - //special case IPv6 addresses - if (v4part === "0.0.0.0") v4part = ""; // any/unspecified address - if (v4part === "0.0.0.1") v4part = "1";// loopback address - str += v4part; - return str; - } - } - - // Handle all other IPv6 addresses - - // first run to find the longest contiguous zero words - for (word = 0; word < 8; word++) { - if (parts[word] === 0) { - if (word - lastzero > 1) { - len = 0; - } - lastzero = word; - len++; - } - if (len > longest) { - longest = len; - zstart = word - longest + 1; - } - } - - for (word = 0; word < 8; word++) { - if (longest > 1) { - // compress contiguous zeros - to produce "::" - if (parts[word] === 0 && word >= zstart && word < (zstart + longest) ) { - if (word === zstart) { - str += ":"; - if (zstart === 0) str += ":"; //leading zeros case - } - continue; - } - } - // converts 16-bit words from big-endian to little-endian before converting to hex string - str += Number(_ntohs(parts[word] & 0xffff)).toString(16); - str += word < 7 ? ":" : ""; - } - return str; - }; - - var readSockaddr = (sa, salen) => { - // family / port offsets are common to both sockaddr_in and sockaddr_in6 - var family = HEAP16[((sa)>>1)]; - var port = _ntohs(HEAPU16[(((sa)+(2))>>1)]); - var addr; - - switch (family) { - case 2: - if (salen !== 16) { - return { errno: 28 }; - } - addr = HEAP32[(((sa)+(4))>>2)]; - addr = inetNtop4(addr); - break; - case 10: - if (salen !== 28) { - return { errno: 28 }; - } - addr = [ - HEAP32[(((sa)+(8))>>2)], - HEAP32[(((sa)+(12))>>2)], - HEAP32[(((sa)+(16))>>2)], - HEAP32[(((sa)+(20))>>2)] - ]; - addr = inetNtop6(addr); - break; - default: - return { errno: 5 }; - } - - return { family: family, addr: addr, port: port }; - }; - - - var getSocketAddress = (addrp, addrlen) => { - var info = readSockaddr(addrp, addrlen); - if (info.errno) throw new FS.ErrnoError(info.errno); - info.addr = DNS.lookup_addr(info.addr) || info.addr; - return info; - }; - function ___syscall_bind(fd, addr, addrlen, d1, d2, d3) { - try { - - var sock = getSocketFromFD(fd); - var info = getSocketAddress(addr, addrlen); - sock.sock_ops.bind(sock, info.addr, info.port); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_bind.sig = 'iippiii'; - - function ___syscall_chdir(path) { - try { - - path = SYSCALLS.getStr(path); - FS.chdir(path); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_chdir.sig = 'ip'; - - function ___syscall_chmod(path, mode) { - try { - - path = SYSCALLS.getStr(path); - FS.chmod(path, mode); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_chmod.sig = 'ipi'; - - - function ___syscall_connect(fd, addr, addrlen, d1, d2, d3) { - try { - - var sock = getSocketFromFD(fd); - var info = getSocketAddress(addr, addrlen); - sock.sock_ops.connect(sock, info.addr, info.port); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_connect.sig = 'iippiii'; - - function ___syscall_dup(fd) { - try { - - var old = SYSCALLS.getStreamFromFD(fd); - return FS.dupStream(old).fd; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_dup.sig = 'ii'; - - function ___syscall_dup3(fd, newfd, flags) { - try { - - var old = SYSCALLS.getStreamFromFD(fd); - if (old.fd === newfd) return -28; - // Check newfd is within range of valid open file descriptors. - if (newfd < 0 || newfd >= FS.MAX_OPEN_FDS) return -8; - var existing = FS.getStream(newfd); - if (existing) FS.close(existing); - return FS.dupStream(old, newfd).fd; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_dup3.sig = 'iiii'; - - function ___syscall_faccessat(dirfd, path, amode, flags) { - try { - - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - if (amode & ~7) { - // need a valid mode - return -28; - } - var lookup = FS.lookupPath(path, { follow: true }); - var node = lookup.node; - if (!node) { - return -44; - } - var perms = ''; - if (amode & 4) perms += 'r'; - if (amode & 2) perms += 'w'; - if (amode & 1) perms += 'x'; - if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) { - return -2; - } - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_faccessat.sig = 'iipii'; - - var ___syscall_fadvise64 = (fd, offset, len, advice) => 0; - ___syscall_fadvise64.sig = 'iijji'; - - var INT53_MAX = 9007199254740992; - - var INT53_MIN = -9007199254740992; - var bigintToI53Checked = (num) => (num < INT53_MIN || num > INT53_MAX) ? NaN : Number(num); - function ___syscall_fallocate(fd, mode, offset, len) { - offset = bigintToI53Checked(offset); - len = bigintToI53Checked(len); - - - try { - - if (isNaN(offset) || isNaN(len)) return -61; - if (mode != 0) { - return -138 - } - if (offset < 0 || len < 0) { - return -28 - } - // We only support mode == 0, which means we can implement fallocate - // in terms of ftruncate. - var oldSize = FS.fstat(fd).size; - var newSize = offset + len; - if (newSize > oldSize) { - FS.ftruncate(fd, newSize); - } - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - ; - } - ___syscall_fallocate.sig = 'iiijj'; - - function ___syscall_fchdir(fd) { - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - FS.chdir(stream.path); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_fchdir.sig = 'ii'; - - function ___syscall_fchmod(fd, mode) { - try { - - FS.fchmod(fd, mode); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_fchmod.sig = 'iii'; - - function ___syscall_fchmodat2(dirfd, path, mode, flags) { - try { - - var nofollow = flags & 256; - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - FS.chmod(path, mode, nofollow); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_fchmodat2.sig = 'iipii'; - - function ___syscall_fchown32(fd, owner, group) { - try { - - FS.fchown(fd, owner, group); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_fchown32.sig = 'iiii'; - - function ___syscall_fchownat(dirfd, path, owner, group, flags) { - try { - - path = SYSCALLS.getStr(path); - var nofollow = flags & 256; - flags = flags & (~256); - path = SYSCALLS.calculateAt(dirfd, path); - (nofollow ? FS.lchown : FS.chown)(path, owner, group); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_fchownat.sig = 'iipiii'; - - var syscallGetVarargI = () => { - // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number. - var ret = HEAP32[((+SYSCALLS.varargs)>>2)]; - SYSCALLS.varargs += 4; - return ret; - }; - var syscallGetVarargP = syscallGetVarargI; - - - - - - - - - - - - - - var allocateUTF8OnStack = (...args) => stringToUTF8OnStack(...args); - - - var PHPWASM = { - O_APPEND:1024, - O_NONBLOCK:2048, - POLLHUP:16, - SETFL_MASK:3072, - init:function (phpWasmInitOptions) { - Module['ENV'] = Module['ENV'] || {}; - // Ensure a platform-level bin directory for a fallback `php` binary. - Module['ENV']['PATH'] = [ - Module['ENV']['PATH'], - '/internal/shared/bin', - ] - .filter(Boolean) - .join(':'); - - // The /request directory is required by the C module. It's where the - // stdout, stderr, and headers information are written for the JavaScript - // code to read later on. This is per-request state that is isolated to a - // single PHP process. - FS.mkdir('/request'); - // The /internal directory is shared amongst all PHP processes - // and contains the php.ini, constants definitions, etc. - FS.mkdir('/internal'); - - if (phpWasmInitOptions?.nativeInternalDirPath) { - FS.mount( - FS.filesystems.NODEFS, - { root: phpWasmInitOptions.nativeInternalDirPath }, - '/internal' - ); - } - - // The files from the shared directory are shared between all the - // PHP processes managed by PHPProcessManager. - FS.mkdirTree('/internal/shared'); - - // The files from the preload directory are preloaded using the - // auto_prepend_file php.ini directive. - FS.mkdirTree('/internal/shared/preload'); - // Platform-level bin directory for a fallback `php` binary. Without it, - // PHP may not populate the PHP_BINARY constant. - FS.mkdirTree('/internal/shared/bin'); - const originalOnRuntimeInitialized = Module['onRuntimeInitialized']; - Module['onRuntimeInitialized'] = () => { - const { node: phpBinaryNode } = FS.lookupPath( - '/internal/shared/bin/php', - { noent_okay: true }, - ); - if (!phpBinaryNode) { - // Dummy PHP binary for PHP to populate the PHP_BINARY constant. - FS.writeFile( - '/internal/shared/bin/php', - new TextEncoder().encode('#!/bin/sh\nphp "$@"') - ); - // It must be executable to be used by PHP. - FS.chmod('/internal/shared/bin/php', 0o755); - } - originalOnRuntimeInitialized(); - }; - - // Create stdout and stderr devices. We can't just use Emscripten's - // default stdout and stderr devices because they stop processing data - // on the first null byte. However, when dealing with binary data, - // null bytes are valid and common. - FS.registerDevice(FS.makedev(64, 0), { - open: () => {}, - close: () => {}, - read: () => 0, - write: (stream, buffer, offset, length, pos) => { - const chunk = buffer.subarray(offset, offset + length); - PHPWASM.onStdout(chunk); - return length; - }, - }); - FS.mkdev('/request/stdout', FS.makedev(64, 0)); - - FS.registerDevice(FS.makedev(63, 0), { - open: () => {}, - close: () => {}, - read: () => 0, - write: (stream, buffer, offset, length, pos) => { - const chunk = buffer.subarray(offset, offset + length); - PHPWASM.onStderr(chunk); - return length; - }, - }); - FS.mkdev('/request/stderr', FS.makedev(63, 0)); - - FS.registerDevice(FS.makedev(62, 0), { - open: () => {}, - close: () => {}, - read: () => 0, - write: (stream, buffer, offset, length, pos) => { - const chunk = buffer.subarray(offset, offset + length); - PHPWASM.onHeaders(chunk); - return length; - }, - }); - FS.mkdev('/request/headers', FS.makedev(62, 0)); - - // Handle events. - PHPWASM.EventEmitter = ENVIRONMENT_IS_NODE - ? require('events').EventEmitter - : class EventEmitter { - constructor() { - this.listeners = {}; - } - emit(eventName, data) { - if (this.listeners[eventName]) { - this.listeners[eventName].forEach( - (callback) => { - callback(data); - } - ); - } - } - once(eventName, callback) { - const self = this; - function removedCallback() { - callback(...arguments); - self.removeListener(eventName, removedCallback); - } - this.on(eventName, removedCallback); - } - removeAllListeners(eventName) { - if (eventName) { - delete this.listeners[eventName]; - } else { - this.listeners = {}; - } - } - removeListener(eventName, callback) { - if (this.listeners[eventName]) { - const idx = - this.listeners[eventName].indexOf(callback); - if (idx !== -1) { - this.listeners[eventName].splice(idx, 1); - } - } - } - }; - - PHPWASM.processTable = {}; - - PHPWASM.input_devices = {}; - const originalWrite = TTY.stream_ops.write; - TTY.stream_ops.write = function (stream, ...rest) { - const retval = originalWrite(stream, ...rest); - // Implicit flush since PHP's fflush() doesn't seem to trigger the fsync event - // @TODO: Fix this at the wasm level - stream.tty.ops.fsync(stream.tty); - return retval; - }; - const originalPutChar = TTY.stream_ops.put_char; - TTY.stream_ops.put_char = function (tty, val) { - /** - * Buffer newlines that Emscripten normally ignores. - * - * Emscripten doesn't do it by default because its default - * print function is console.log that implicitly adds a newline. We are overwriting - * it with an environment-specific function that outputs exaclty what it was given, - * e.g. in Node.js it's process.stdout.write(). Therefore, we need to mak sure - * all the newlines make it to the output buffer. - */ - if (val === 10) tty.output.push(val); - return originalPutChar(tty, val); - }; - }, - onHeaders:function (chunk) { - if (Module['onHeaders']) { - Module['onHeaders'](chunk); - return; - } - console.log('headers', { chunk }); - }, - onStdout:function (chunk) { - if (Module['onStdout']) { - Module['onStdout'](chunk); - return; - } - if (ENVIRONMENT_IS_NODE) { - process.stdout.write(chunk); - } else { - console.log('stdout', { chunk }); - } - }, - onStderr:function (chunk) { - if (Module['onStderr']) { - Module['onStderr'](chunk); - return; - } - if (ENVIRONMENT_IS_NODE) { - process.stderr.write(chunk); - } else { - console.warn('stderr', { chunk }); - } - }, - getAllWebSockets:function (sock) { - const webSockets = /* @__PURE__ */ new Set(); - if (sock.server) { - sock.server.clients.forEach((ws) => { - webSockets.add(ws); - }); - } - for (const peer of PHPWASM.getAllPeers(sock)) { - webSockets.add(peer.socket); - } - return Array.from(webSockets); - }, - getAllPeers:function (sock) { - const peers = new Set(); - if (sock.server) { - sock.pending - .filter((pending) => pending.peers) - .forEach((pending) => { - for (const peer of Object.values(pending.peers)) { - peers.add(peer); - } - }); - } - if (sock.peers) { - for (const peer of Object.values(sock.peers)) { - peers.add(peer); - } - } - return Array.from(peers); - }, - awaitData:function (ws) { - return PHPWASM.awaitEvent(ws, 'message'); - }, - awaitConnection:function (ws) { - if (ws.OPEN === ws.readyState) { - return [Promise.resolve(), PHPWASM.noop]; - } - return PHPWASM.awaitEvent(ws, 'open'); - }, - awaitClose:function (ws) { - if ([ws.CLOSING, ws.CLOSED].includes(ws.readyState)) { - return [Promise.resolve(), PHPWASM.noop]; - } - return PHPWASM.awaitEvent(ws, 'close'); - }, - awaitError:function (ws) { - if ([ws.CLOSING, ws.CLOSED].includes(ws.readyState)) { - return [Promise.resolve(), PHPWASM.noop]; - } - return PHPWASM.awaitEvent(ws, 'error'); - }, - awaitEvent:function (ws, event) { - let resolve; - const listener = () => { - resolve(); - }; - const promise = new Promise(function (_resolve) { - resolve = _resolve; - ws.once(event, listener); - }); - const cancel = () => { - ws.removeListener(event, listener); - // Rejecting the promises bubbles up and kills the entire - // node process. Let's resolve them on the next tick instead - // to give the caller some space to unbind any handlers. - setTimeout(resolve); - }; - return [promise, cancel]; - }, - noop:function () {}, - spawnProcess:function (command, args, options) { - if (Module['spawnProcess']) { - const spawnedPromise = Module['spawnProcess']( - command, - args, - options - ); - return Promise.resolve(spawnedPromise).then(function (spawned) { - if (!spawned || !spawned.on) { - throw new Error( - 'spawnProcess() must return an EventEmitter but returned a different type.' - ); - } - return spawned; - }); - } - - if (ENVIRONMENT_IS_NODE) { - return require('child_process').spawn(command, args, { - ...options, - shell: true, - stdio: ['pipe', 'pipe', 'pipe'], - }); - } - const e = new Error( - 'popen(), proc_open() etc. are unsupported in the browser. Call php.setSpawnHandler() ' + - 'and provide a callback to handle spawning processes, or disable a popen(), proc_open() ' + - 'and similar functions via php.ini.' - ); - e.code = 'SPAWN_UNSUPPORTED'; - throw e; - }, - shutdownSocket:function (socketd, how) { - // This implementation only supports websockets at the moment - const sock = getSocketFromFD(socketd); - const peer = Object.values(sock.peers)[0]; - - if (!peer) { - return -1; - } - - try { - peer.socket.close(); - SOCKFS.websocket_sock_ops.removePeer(sock, peer); - return 0; - } catch (e) { - console.log('Socket shutdown error', e); - return -1; - } - }, - }; - - function _js_getpid() { - return PHPLoader.processId ?? 42; - } - - - function _js_wasm_trace(format, ...args) { - if (PHPLoader.trace instanceof Function) { - PHPLoader.trace(_js_getpid(), format, ...args); - } - } - - - - - function _fd_close(fd) { - // We have to get the VFS path from the file descriptor - // before closing it. - const [vfsPath, vfsPathResolutionErrno] = - locking.get_vfs_path_from_fd(fd); - - const fdCloseResult = _builtin_fd_close(fd); - if (fdCloseResult !== 0 || !locking.maybeLockedFds.has(fd)) { - _js_wasm_trace('fd_close(%d) result %d', fd, fdCloseResult); - return fdCloseResult; - } - - if (vfsPathResolutionErrno !== 0) { - _js_wasm_trace( - 'fd_close(%d) get_vfs_path_from_fd error %d', - fd, - vfsPathResolutionErrno - ); - /* - * It looks like the file may have had an associated lock, - * but since we cannot look up the path, - * there is nothing more for us to do. - * - * NOTE: This seems possible for files that are locked and - * then unlinked before close. It is an opportunity for a - * lock to be orphaned in the lock manager. - * @TODO: Explore how to ensure cleanup in this case. - */ - return fdCloseResult; - } - - try { - const nativeFilePath = - locking.get_native_path_from_vfs_path(vfsPath); - PHPLoader.fileLockManager - .releaseLocksForProcessFd( - PHPLoader.processId, - fd, - nativeFilePath - ); - _js_wasm_trace( - 'fd_close(%d) release locks success', - fd - ); - } catch (e) { - _js_wasm_trace("fd_close(%d) error '%s'", fd, e); - } finally { - locking.maybeLockedFds.delete(fd); - } - return fdCloseResult; - } - _fd_close.sig = 'ii'; - function _builtin_fd_close(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 _builtin_fcntl64(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. - HEAP16[(((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; - } - } - - - - - var locking = { - maybeLockedFds:new Set, - F_RDLCK:0, - F_WRLCK:1, - F_UNLCK:2, - lockStateToFcntl:{ - shared:0, - exclusive:1, - unlocked:2, - }, - fcntlToLockState:{ - 0:"shared", - 1:"exclusive", - 2:"unlocked", - }, - is_path_to_shared_fs(path) { - _js_wasm_trace('is_path_to_shared_fs(%s)', path); - const { node } = FS.lookupPath( - path, - { noent_okay: true }, - ); - if (node.mount.type !== PROXYFS) { - return !!node.isSharedFS; - } - - // This looks like a PROXYFS node. Let's try a lookup. - const nodePath = PROXYFS.realPath(node); - const backingFs = node?.mount?.opts?.fs; - if (backingFs) { - // Tolerate ENOENT because looking up a MEMFS node by path always fails. - const { node: backingNode } = backingFs.lookupPath( - nodePath, - { noent_okay: true } - ); - return !!backingNode?.isSharedFS; - } - - return false; - }, - get_fd_access_mode(fd) { - const emscripten_F_GETFL = Number('3'); - const emscripten_O_ACCMODE = Number('2097155'); - - return ( - _builtin_fcntl64(fd, emscripten_F_GETFL) & emscripten_O_ACCMODE - ); - }, - get_vfs_path_from_fd(fd) { - try { - return [FS.readlink(`/proc/self/fd/${fd}`), 0]; - } catch (error) { - return [null, ERRNO_CODES.EBADF]; - } - }, - get_native_path_from_vfs_path(vfsPath) { - const { node } = FS.lookupPath(vfsPath, { - noent_okay: true, - }); - if (!node) { - throw new Error(`No node found for VFS path ${vfsPath}`); - } - if (node.mount.type === NODEFS) { - return NODEFS.realPath(node); - } else if (node.mount.type === PROXYFS) { - // TODO: Tolerate ENOENT here? - const { node: backingNode, path: backingPath } = node.mount.opts.fs.lookupPath(vfsPath); - _js_wasm_trace('backingNode for %s: %s', vfsPath, backingPath, backingNode); - return backingNode.mount.type.realPath(backingNode); - } else { - throw new Error(`Unsupported filesystem type for path ${vfsPath}`); - } - }, - check_lock_params(fd, l_type) { - const emscripten_O_RDONLY = Number('0'); - const emscripten_O_WRONLY = Number('1'); - - const accessMode = locking.get_fd_access_mode(fd); - if ( - (l_type === locking.F_WRLCK && - accessMode === emscripten_O_RDONLY) || - (l_type === locking.F_RDLCK && - accessMode === emscripten_O_WRONLY) - ) { - return ERRNO_CODES.EBADF; - } - - return 0; - }, - }; - - - - - function ___syscall_fcntl64(fd, cmd, varargs) { - if (!PHPLoader.fileLockManager) { - return _builtin_fcntl64(fd, cmd, varargs); - } - // Necessary to use varargs accessor - SYSCALLS.varargs = varargs; - - // These constants are replaced by Emscripten during the build process - const emscripten_F_SETFL = Number('4'); - const emscripten_F_GETLK = Number('12'); - const emscripten_F_SETLK = Number('13'); - const emscripten_F_SETLKW = Number('14'); - const emscripten_SEEK_SET = Number('0'); - - // NOTE: With the exception of l_type, these offsets are not exposed to - // JS by Emscripten, so we hardcode them here. - const emscripten_flock_l_type_offset = 0; - const emscripten_flock_l_whence_offset = 2; - const emscripten_flock_l_start_offset = 8; - const emscripten_flock_l_len_offset = 16; - const emscripten_flock_l_pid_offset = 24; - - /** - * Read the flock struct at the given address. - * - * @param {bigint} flockStructAddress - the address of the flock struct - * @returns the flock struct - */ - function read_flock_struct(flockStructAddress) { - /* - * NOTE: Since we are using HEAP vars like HEAP16 and HEAP64, - * we need to adjust offsets to address the word size of each HEAP. - * - * For example, an offset of 64 bytes is the following for each HEAP: - * - HEAP8: 64 (the 64th byte) - * - HEAP16: 32 (the 32nd 16-bit word) - * - HEAP32: 16 (the 16th 32-bit word) - * - HEAP64: 8 (the 8th 64-bit word) - * - * We get a word offset by dividing the byte offset by the word size. - */ - return { - l_type: HEAP16[ - // Shift right by 1 to divide by 2^1. - (flockStructAddress + emscripten_flock_l_type_offset) >> - 1 - ], - l_whence: - HEAP16[ - // Shift right by 1 to divide by 2^1. - (flockStructAddress + - emscripten_flock_l_whence_offset) >> - 1 - ], - l_start: - HEAP64[ - // Shift right by 3 to divide by 2^3. - (flockStructAddress + - emscripten_flock_l_start_offset) >> - 3 - ], - l_len: HEAP64[ - // Shift right by 3 to divide by 2^3. - (flockStructAddress + emscripten_flock_l_len_offset) >> - 3 - ], - l_pid: HEAP32[ - // Shift right by 2 to divide by 2^2. - (flockStructAddress + emscripten_flock_l_pid_offset) >> - 2 - ], - }; - } - - /** - * Update the flock struct at the given address with the given fields. - * - * @param {bigint} flockStructAddress - the address of the flock struct - * @param {object} fields - the fields to update - */ - function update_flock_struct(flockStructAddress, fields) { - /* - * NOTE: Since we are using HEAP vars like HEAP16 and HEAP64, - * we need to adjust offsets to address the word size of each HEAP. - * - * For example, an offset of 64 bytes is the following for each HEAP: - * - HEAP8: 64 (the 64th byte) - * - HEAP16: 32 (the 32nd 16-bit word) - * - HEAP32: 16 (the 16th 32-bit word) - * - HEAP64: 8 (the 8th 64-bit word) - * - * We get a word offset by dividing the byte offset by the word size. - */ - if (fields.l_type !== undefined) { - HEAP16[ - // Shift right by 1 to divide by 2^1. - (flockStructAddress + emscripten_flock_l_type_offset) >> - 1 - ] = fields.l_type; - } - if (fields.l_whence !== undefined) { - HEAP16[ - // Shift right by 1 to divide by 2^1. - (flockStructAddress + - emscripten_flock_l_whence_offset) >> - 1 - ] = fields.l_whence; - } - if (fields.l_start !== undefined) { - HEAP64[ - // Shift right by 3 to divide by 2^3. - (flockStructAddress + - emscripten_flock_l_start_offset) >> - 3 - ] = fields.l_start; - } - if (fields.l_len !== undefined) { - HEAP64[ - // Shift right by 3 to divide by 2^3. - (flockStructAddress + emscripten_flock_l_len_offset) >> - 3 - ] = fields.l_len; - } - if (fields.l_pid !== undefined) { - HEAP32[ - // Shift right by 2 to divide by 2^2. - (flockStructAddress + emscripten_flock_l_pid_offset) >> - 2 - ] = fields.l_pid; - } - } - - /** - * Resolve the base address of the range depending on the whence and start offset. - * - * @param {number} fd - the file descriptor - * @param {number} whence - what the start offset is relative to - * @param {bigint} startOffset - the offset from the whence - * @returns The resolved offset and the errno. If there is an error, - * the resolved offset is null, and the errno is non-zero. - */ - function get_base_address(fd, whence, startOffset) { - let baseAddress; - switch (whence) { - case emscripten_SEEK_SET: - baseAddress = 0n; - break; - case emscripten_SEEK_CUR: - baseAddress = FS.lseek(fd, 0, whence); - break; - case emscripten_SEEK_END: - baseAddress = _wasm_get_end_offset(fd); - break; - default: - return [null, ERRNO_CODES.EINVAL]; - } - - if (baseAddress == -1) { - // We cannot resolve the offset within the file. - // Let's treat this as a problem with the file descriptor. - return [null, ERRNO_CODES.EBADF]; - } - - const resolvedOffset = baseAddress + startOffset; - if (resolvedOffset < 0) { - // This is not a valid offset. Report args as invalid. - return [null, ERRNO_CODES.EINVAL]; - } - - return [resolvedOffset, 0]; - } - - const pid = PHPLoader.processId; - switch (cmd) { - case emscripten_F_GETLK: { - _js_wasm_trace('fcntl(%d, F_GETLK)', fd); - let vfsPath; - let errno; - - [vfsPath, errno] = locking.get_vfs_path_from_fd(fd); - if (errno !== 0) { - _js_wasm_trace( - 'fcntl(%d, F_GETLK) %s get_vfs_path_from_fd errno %d', - fd, - vfsPath, - errno - ); - return -ERRNO_CODES.EBADF; - } - - const flockStructAddr = syscallGetVarargP(); - - if (!locking.is_path_to_shared_fs(vfsPath)) { - _js_wasm_trace( - "fcntl(%d, F_GETLK) locking is not implemented for non-NodeFS path '%s'", - fd, - vfsPath - ); - - // If not a NodeFS path, we can't lock it. - // Default to succeeding as Emscripten does. - update_flock_struct(flockStructAddr, { - l_type: F_UNLCK, - }); - return 0; - } - - const flockStruct = read_flock_struct(flockStructAddr); - - if (!(flockStruct.l_type in locking.fcntlToLockState)) { - return -ERRNO_CODES.EINVAL; - } - - errno = locking.check_lock_params(fd, flockStruct.l_type); - if (errno !== 0) { - _js_wasm_trace( - 'fcntl(%d, F_GETLK) %s check_lock_params errno %d', - fd, - vfsPath, - errno - ); - return -ERRNO_CODES.EINVAL; - } - - const requestedLockType = - locking.fcntlToLockState[flockStruct.l_type]; - let absoluteStartOffset; - [absoluteStartOffset, errno] = get_base_address( - fd, - flockStruct.l_whence, - flockStruct.l_start - ); - if (errno !== 0) { - _js_wasm_trace( - 'fcntl(%d, F_GETLK) %s get_base_address errno %d', - fd, - vfsPath, - errno - ); - return -ERRNO_CODES.EINVAL; - } - - try { - const nativeFilePath = - locking.get_native_path_from_vfs_path(vfsPath); - const conflictingLock = - PHPLoader.fileLockManager - .findFirstConflictingByteRangeLock(nativeFilePath, { - type: requestedLockType, - start: absoluteStartOffset, - end: absoluteStartOffset + flockStruct.l_len, - pid, - }) - ; - if (conflictingLock === undefined) { - _js_wasm_trace( - 'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock type=unlocked start=0x%x end=0x%x', - fd, - vfsPath, - absoluteStartOffset, - absoluteStartOffset + flockStruct.l_len - ); - - update_flock_struct(flockStructAddr, { - l_type: F_UNLCK, - }); - return 0; - } - - _js_wasm_trace( - 'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock type=%s start=0x%x end=0x%x conflictingLock %d', - fd, - vfsPath, - conflictingLock.type, - conflictingLock.start, - conflictingLock.end, - conflictingLock.pid - ); - - const fcntlLockState = - locking.lockStateToFcntl[conflictingLock.type]; - update_flock_struct(flockStructAddr, { - l_type: fcntlLockState, - l_whence: emscripten_SEEK_SET, - l_start: conflictingLock.start, - l_len: - conflictingLock.end - conflictingLock.start, - l_pid: conflictingLock.pid, - }); - return 0; - } catch (e) { - _js_wasm_trace( - 'fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock error %s', - fd, - vfsPath, - e - ); - return -ERRNO_CODES.EINVAL; - } - } - case emscripten_F_SETLK: { - _js_wasm_trace('fcntl(%d, F_SETLK)', fd); - let vfsPath; - let errno; - [vfsPath, errno] = locking.get_vfs_path_from_fd(fd); - if (errno !== 0) { - _js_wasm_trace( - 'fcntl(%d, F_SETLK) %s get_vfs_path_from_fd errno %d', - fd, - vfsPath, - errno - ); - return -errno; - } - - if (!locking.is_path_to_shared_fs(vfsPath)) { - _js_wasm_trace( - 'fcntl(%d, F_SETLK) locking is not implemented for non-NodeFS path %s', - fd, - vfsPath - ); - - // If not a NodeFS path, we can't lock it. - // Default to succeeding as Emscripten does. - return 0; - } - - var flockStructAddr = syscallGetVarargP(); - const flockStruct = read_flock_struct(flockStructAddr); - - let absoluteStartOffset; - [absoluteStartOffset, errno] = get_base_address( - fd, - flockStruct.l_whence, - flockStruct.l_start - ); - if (errno !== 0) { - _js_wasm_trace( - 'fcntl(%d, F_SETLK) %s get_base_address errno %d', - fd, - vfsPath, - errno - ); - return -errno; - } - - if (!(flockStruct.l_type in locking.fcntlToLockState)) { - _js_wasm_trace( - 'fcntl(%d, F_SETLK) %s invalid lock type %d', - fd, - vfsPath, - flockStruct.l_type - ); - return -ERRNO_CODES.EINVAL; - } - - errno = locking.check_lock_params(fd, flockStruct.l_type); - if (errno !== 0) { - _js_wasm_trace( - 'fcntl(%d, F_SETLK) %s check_lock_params errno %d', - fd, - vfsPath, - errno - ); - return -errno; - } - - locking.maybeLockedFds.add(fd); - - const requestedLockType = - locking.fcntlToLockState[flockStruct.l_type]; - const rangeLock = { - type: requestedLockType, - start: absoluteStartOffset, - end: absoluteStartOffset + flockStruct.l_len, - pid, - }; - - try { - const nativeFilePath = - locking.get_native_path_from_vfs_path(vfsPath); - _js_wasm_trace( - 'fcntl(%d, F_SETLK) %s calling lockFileByteRange for range lock %s', - fd, - vfsPath, - rangeLock - ); - - const succeeded = ( - PHPLoader.fileLockManager - .lockFileByteRange(nativeFilePath, rangeLock) - ); - - _js_wasm_trace( - 'fcntl(%d, F_SETLK) %s lockFileByteRange returned %d for range lock %s', - fd, - vfsPath, - succeeded, - rangeLock - ); - return succeeded ? 0 : -ERRNO_CODES.EAGAIN; - } catch (e) { - _js_wasm_trace( - 'fcntl(%d, F_SETLK) %s lockFileByteRange error %s for range lock %s', - fd, - vfsPath, - e, - rangeLock - ); - return -ERRNO_CODES.EINVAL; - } - } - // @TODO: Implement waiting for lock - case emscripten_F_SETLKW: { - // We do not yet support the blocking form of flock(). - // We respond with EDEADLK to indicate failure - // because it is a known errno for a failed F_SETLKW command. - return -ERRNO_CODES.EDEADLK; - } - case emscripten_F_SETFL: { - /** - * Overrides the core Emscripten implementation to reflect what - * fcntl does in linux kernel. This implementation is still missing - * a bunch of nuance, but, unlike the core Emscripten implementation, - * it overrides the stream flags while preserving non-stream flags. - * - * @see fcntl.c: - * https://github.com/torvalds/linux/blob/a79a588fc1761dc12a3064fc2f648ae66cea3c5a/fs/fcntl.c#L39 - */ - const arg = varargs ? syscallGetVarargI() : 0; - const stream = SYSCALLS.getStreamFromFD(fd); - - // Update the stream flags - stream.flags = - (arg & PHPWASM.SETFL_MASK) | - (stream.flags & ~PHPWASM.SETFL_MASK); - - return 0; - } - default: - return _builtin_fcntl64(fd, cmd, varargs); - } - } - ___syscall_fcntl64.sig = 'iiip'; - - function ___syscall_fdatasync(fd) { - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - return 0; // we can't do anything synchronously; the in-memory FS is already synced to - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_fdatasync.sig = 'ii'; - - function ___syscall_fstat64(fd, buf) { - try { - - return SYSCALLS.writeStat(buf, FS.fstat(fd)); - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_fstat64.sig = 'iip'; - - function ___syscall_fstatfs64(fd, size, buf) { - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - SYSCALLS.writeStatFs(buf, FS.statfsStream(stream)); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_fstatfs64.sig = 'iipp'; - - function ___syscall_ftruncate64(fd, length) { - length = bigintToI53Checked(length); - - - try { - - if (isNaN(length)) return -61; - FS.ftruncate(fd, length); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - ; - } - ___syscall_ftruncate64.sig = 'iij'; - - - function ___syscall_getcwd(buf, size) { - try { - - if (size === 0) return -28; - var cwd = FS.cwd(); - var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1; - if (size < cwdLengthInBytes) return -68; - stringToUTF8(cwd, buf, size); - return cwdLengthInBytes; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_getcwd.sig = 'ipp'; - - - function ___syscall_getdents64(fd, dirp, count) { - try { - - var stream = SYSCALLS.getStreamFromFD(fd) - stream.getdents ||= FS.readdir(stream.path); - - var struct_size = 280; - var pos = 0; - var off = FS.llseek(stream, 0, 1); - - var startIdx = Math.floor(off / struct_size); - var endIdx = Math.min(stream.getdents.length, startIdx + Math.floor(count/struct_size)) - for (var idx = startIdx; idx < endIdx; idx++) { - var id; - var type; - var name = stream.getdents[idx]; - if (name === '.') { - id = stream.node.id; - type = 4; // DT_DIR - } - else if (name === '..') { - var lookup = FS.lookupPath(stream.path, { parent: true }); - id = lookup.node.id; - type = 4; // DT_DIR - } - else { - var child; - try { - child = FS.lookupNode(stream.node, name); - } catch (e) { - // If the entry is not a directory, file, or symlink, nodefs - // lookupNode will raise EINVAL. Skip these and continue. - if (e?.errno === 28) { - continue; - } - throw e; - } - id = child.id; - type = FS.isChrdev(child.mode) ? 2 : // DT_CHR, character device. - FS.isDir(child.mode) ? 4 : // DT_DIR, directory. - FS.isLink(child.mode) ? 10 : // DT_LNK, symbolic link. - 8; // DT_REG, regular file. - } - HEAP64[((dirp + pos)>>3)] = BigInt(id); - HEAP64[(((dirp + pos)+(8))>>3)] = BigInt((idx + 1) * struct_size); - HEAP16[(((dirp + pos)+(16))>>1)] = 280; - HEAP8[(dirp + pos)+(18)] = type; - stringToUTF8(name, dirp + pos + 19, 256); - pos += struct_size; - } - FS.llseek(stream, idx * struct_size, 0); - return pos; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_getdents64.sig = 'iipp'; - - - - function ___syscall_getpeername(fd, addr, addrlen, d1, d2, d3) { - try { - - var sock = getSocketFromFD(fd); - if (!sock.daddr) { - return -53; // The socket is not connected. - } - var errno = writeSockaddr(addr, sock.family, DNS.lookup_name(sock.daddr), sock.dport, addrlen); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_getpeername.sig = 'iippiii'; - - - - function ___syscall_getsockname(fd, addr, addrlen, d1, d2, d3) { - try { - - var sock = getSocketFromFD(fd); - // TODO: sock.saddr should never be undefined, see TODO in websocket_sock_ops.getname - var errno = writeSockaddr(addr, sock.family, DNS.lookup_name(sock.saddr || '0.0.0.0'), sock.sport, addrlen); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_getsockname.sig = 'iippiii'; - - function ___syscall_getsockopt(fd, level, optname, optval, optlen, d1) { - try { - - var sock = getSocketFromFD(fd); - // Minimal getsockopt aimed at resolving https://github.com/emscripten-core/emscripten/issues/2211 - // so only supports SOL_SOCKET with SO_ERROR. - if (level === 1) { - if (optname === 4) { - HEAP32[((optval)>>2)] = sock.error; - HEAP32[((optlen)>>2)] = 4; - sock.error = null; // Clear the error (The SO_ERROR option obtains and then clears this field). - return 0; - } - } - return -50; // The option is unknown at the level indicated. - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_getsockopt.sig = 'iiiippi'; - - - function ___syscall_ioctl(fd, op, varargs) { - SYSCALLS.varargs = varargs; - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - switch (op) { - case 21509: { - if (!stream.tty) return -59; - return 0; - } - case 21505: { - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tcgets) { - var termios = stream.tty.ops.ioctl_tcgets(stream); - var argp = syscallGetVarargP(); - HEAP32[((argp)>>2)] = termios.c_iflag || 0; - HEAP32[(((argp)+(4))>>2)] = termios.c_oflag || 0; - HEAP32[(((argp)+(8))>>2)] = termios.c_cflag || 0; - HEAP32[(((argp)+(12))>>2)] = termios.c_lflag || 0; - for (var i = 0; i < 32; i++) { - HEAP8[(argp + i)+(17)] = termios.c_cc[i] || 0; - } - return 0; - } - return 0; - } - case 21510: - case 21511: - case 21512: { - if (!stream.tty) return -59; - return 0; // no-op, not actually adjusting terminal settings - } - case 21506: - case 21507: - case 21508: { - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tcsets) { - var argp = syscallGetVarargP(); - var c_iflag = HEAP32[((argp)>>2)]; - var c_oflag = HEAP32[(((argp)+(4))>>2)]; - var c_cflag = HEAP32[(((argp)+(8))>>2)]; - var c_lflag = HEAP32[(((argp)+(12))>>2)]; - var c_cc = [] - for (var i = 0; i < 32; i++) { - c_cc.push(HEAP8[(argp + i)+(17)]); - } - return stream.tty.ops.ioctl_tcsets(stream.tty, op, { c_iflag, c_oflag, c_cflag, c_lflag, c_cc }); - } - return 0; // no-op, not actually adjusting terminal settings - } - case 21519: { - if (!stream.tty) return -59; - var argp = syscallGetVarargP(); - HEAP32[((argp)>>2)] = 0; - return 0; - } - case 21520: { - if (!stream.tty) return -59; - return -28; // not supported - } - case 21537: - case 21531: { - var argp = syscallGetVarargP(); - return FS.ioctl(stream, op, argp); - } - case 21523: { - // TODO: in theory we should write to the winsize struct that gets - // passed in, but for now musl doesn't read anything on it - if (!stream.tty) return -59; - if (stream.tty.ops.ioctl_tiocgwinsz) { - var winsize = stream.tty.ops.ioctl_tiocgwinsz(stream.tty); - var argp = syscallGetVarargP(); - HEAP16[((argp)>>1)] = winsize[0]; - HEAP16[(((argp)+(2))>>1)] = winsize[1]; - } - return 0; - } - case 21524: { - // TODO: technically, this ioctl call should change the window size. - // but, since emscripten doesn't have any concept of a terminal window - // yet, we'll just silently throw it away as we do TIOCGWINSZ - if (!stream.tty) return -59; - return 0; - } - case 21515: { - if (!stream.tty) return -59; - return 0; - } - default: return -28; // not supported - } - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_ioctl.sig = 'iiip'; - - function ___syscall_listen(fd, backlog) { - try { - - var sock = getSocketFromFD(fd); - sock.sock_ops.listen(sock, backlog); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_listen.sig = 'iiiiiii'; - - function ___syscall_lstat64(path, buf) { - try { - - path = SYSCALLS.getStr(path); - return SYSCALLS.writeStat(buf, FS.lstat(path)); - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_lstat64.sig = 'ipp'; - - function ___syscall_mkdirat(dirfd, path, mode) { - try { - - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - FS.mkdir(path, mode, 0); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_mkdirat.sig = 'iipi'; - - function ___syscall_mknodat(dirfd, path, mode, dev) { - try { - - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - // we don't want this in the JS API as it uses mknod to create all nodes. - switch (mode & 61440) { - case 32768: - case 8192: - case 24576: - case 4096: - case 49152: - break; - default: return -28; - } - FS.mknod(path, mode, dev); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_mknodat.sig = 'iipii'; - - function ___syscall_newfstatat(dirfd, path, buf, flags) { - try { - - path = SYSCALLS.getStr(path); - var nofollow = flags & 256; - var allowEmpty = flags & 4096; - flags = flags & (~6400); - path = SYSCALLS.calculateAt(dirfd, path, allowEmpty); - return SYSCALLS.writeStat(buf, nofollow ? FS.lstat(path) : FS.stat(path)); - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_newfstatat.sig = 'iippi'; - - - function ___syscall_openat(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; - } - } - ___syscall_openat.sig = 'iipip'; - - var PIPEFS = { - BUCKET_BUFFER_SIZE:8192, - mount(mount) { - // Do not pollute the real root directory or its child nodes with pipes - // Looks like it is OK to create another pseudo-root node not linked to the FS.root hierarchy this way - return FS.createNode(null, '/', 16384 | 0o777, 0); - }, - createPipe() { - var pipe = { - buckets: [], - // refcnt 2 because pipe has a read end and a write end. We need to be - // able to read from the read end after write end is closed. - refcnt : 2, - timestamp: new Date(), - }; - - pipe.buckets.push({ - buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), - offset: 0, - roffset: 0 - }); - - var rName = PIPEFS.nextname(); - var wName = PIPEFS.nextname(); - var rNode = FS.createNode(PIPEFS.root, rName, 4096, 0); - var wNode = FS.createNode(PIPEFS.root, wName, 4096, 0); - - rNode.pipe = pipe; - wNode.pipe = pipe; - - var readableStream = FS.createStream({ - path: rName, - node: rNode, - flags: 0, - seekable: false, - stream_ops: PIPEFS.stream_ops - }); - rNode.stream = readableStream; - - var writableStream = FS.createStream({ - path: wName, - node: wNode, - flags: 1, - seekable: false, - stream_ops: PIPEFS.stream_ops - }); - wNode.stream = writableStream; - - return { - readable_fd: readableStream.fd, - writable_fd: writableStream.fd - }; - }, - stream_ops:{ - getattr(stream) { - var node = stream.node; - var timestamp = node.pipe.timestamp; - return { - dev: 14, - ino: node.id, - mode: 0o10600, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - size: 0, - atime: timestamp, - mtime: timestamp, - ctime: timestamp, - blksize: 4096, - blocks: 0, - }; - }, - poll(stream) { - var pipe = stream.node.pipe; - - if ((stream.flags & 2097155) === 1) { - return (256 | 4); - } - for (var bucket of pipe.buckets) { - if (bucket.offset - bucket.roffset > 0) { - return (64 | 1); - } - } - - return 0; - }, - dup(stream) { - stream.node.pipe.refcnt++; - }, - ioctl(stream, request, varargs) { - return 28; - }, - fsync(stream) { - return 28; - }, - read(stream, buffer, offset, length, position /* ignored */) { - var pipe = stream.node.pipe; - var currentLength = 0; - - for (var bucket of pipe.buckets) { - currentLength += bucket.offset - bucket.roffset; - } - - var data = buffer.subarray(offset, offset + length); - - if (length <= 0) { - return 0; - } - if(currentLength==0){if(pipe.refcnt<2){return 0;}throw new FS.ErrnoError(6); - } - var toRead = Math.min(currentLength, length); - - var totalRead = toRead; - var toRemove = 0; - - for (var bucket of pipe.buckets) { - var bucketSize = bucket.offset - bucket.roffset; - - if (toRead <= bucketSize) { - var tmpSlice = bucket.buffer.subarray(bucket.roffset, bucket.offset); - if (toRead < bucketSize) { - tmpSlice = tmpSlice.subarray(0, toRead); - bucket.roffset += toRead; - } else { - toRemove++; - } - data.set(tmpSlice); - break; - } else { - var tmpSlice = bucket.buffer.subarray(bucket.roffset, bucket.offset); - data.set(tmpSlice); - data = data.subarray(tmpSlice.byteLength); - toRead -= tmpSlice.byteLength; - toRemove++; - } - } - - if (toRemove && toRemove == pipe.buckets.length) { - // Do not generate excessive garbage in use cases such as - // write several bytes, read everything, write several bytes, read everything... - toRemove--; - pipe.buckets[toRemove].offset = 0; - pipe.buckets[toRemove].roffset = 0; - } - - pipe.buckets.splice(0, toRemove); - - return totalRead; - }, - write(stream, buffer, offset, length, position /* ignored */) { - var pipe = stream.node.pipe; - - var data = buffer.subarray(offset, offset + length); - - var dataLen = data.byteLength; - if (dataLen <= 0) { - return 0; - } - - var currBucket = null; - - if (pipe.buckets.length == 0) { - currBucket = { - buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), - offset: 0, - roffset: 0 - }; - pipe.buckets.push(currBucket); - } else { - currBucket = pipe.buckets[pipe.buckets.length - 1]; - } - - var freeBytesInCurrBuffer = PIPEFS.BUCKET_BUFFER_SIZE - currBucket.offset; - if (freeBytesInCurrBuffer >= dataLen) { - currBucket.buffer.set(data, currBucket.offset); - currBucket.offset += dataLen; - return dataLen; - } else if (freeBytesInCurrBuffer > 0) { - currBucket.buffer.set(data.subarray(0, freeBytesInCurrBuffer), currBucket.offset); - currBucket.offset += freeBytesInCurrBuffer; - data = data.subarray(freeBytesInCurrBuffer, data.byteLength); - } - - var numBuckets = (data.byteLength / PIPEFS.BUCKET_BUFFER_SIZE) | 0; - var remElements = data.byteLength % PIPEFS.BUCKET_BUFFER_SIZE; - - for (var i = 0; i < numBuckets; i++) { - var newBucket = { - buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), - offset: PIPEFS.BUCKET_BUFFER_SIZE, - roffset: 0 - }; - pipe.buckets.push(newBucket); - newBucket.buffer.set(data.subarray(0, PIPEFS.BUCKET_BUFFER_SIZE)); - data = data.subarray(PIPEFS.BUCKET_BUFFER_SIZE, data.byteLength); - } - - if (remElements > 0) { - var newBucket = { - buffer: new Uint8Array(PIPEFS.BUCKET_BUFFER_SIZE), - offset: data.byteLength, - roffset: 0 - }; - pipe.buckets.push(newBucket); - newBucket.buffer.set(data); - } - - return dataLen; - }, - close(stream) { - var pipe = stream.node.pipe; - pipe.refcnt--; - if (pipe.refcnt === 0) { - pipe.buckets = null; - } - }, - }, - nextname() { - if (!PIPEFS.nextname.current) { - PIPEFS.nextname.current = 0; - } - return 'pipe[' + (PIPEFS.nextname.current++) + ']'; - }, - }; - function ___syscall_pipe(fdPtr) { - try { - - if (fdPtr == 0) { - throw new FS.ErrnoError(21); - } - - var res = PIPEFS.createPipe(); - - HEAP32[((fdPtr)>>2)] = res.readable_fd; - HEAP32[(((fdPtr)+(4))>>2)] = res.writable_fd; - - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_pipe.sig = 'ip'; - - function ___syscall_poll(fds, nfds, timeout) { - try { - - var nonzero = 0; - for (var i = 0; i < nfds; i++) { - var pollfd = fds + 8 * i; - var fd = HEAP32[((pollfd)>>2)]; - var events = HEAP16[(((pollfd)+(4))>>1)]; - var mask = 32; - var stream = FS.getStream(fd); - if (stream) { - mask = SYSCALLS.DEFAULT_POLLMASK; - if (stream.stream_ops?.poll) { - mask = stream.stream_ops.poll(stream, -1); - } - } - mask &= events | 8 | 16; - if (mask) nonzero++; - HEAP16[(((pollfd)+(6))>>1)] = mask; - } - return nonzero; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_poll.sig = 'ipii'; - - - - function ___syscall_readlinkat(dirfd, path, buf, bufsize) { - try { - - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - if (bufsize <= 0) return -28; - var ret = FS.readlink(path); - - var len = Math.min(bufsize, lengthBytesUTF8(ret)); - var endChar = HEAP8[buf+len]; - stringToUTF8(ret, buf, bufsize+1); - // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!) - // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write. - HEAP8[buf+len] = endChar; - return len; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_readlinkat.sig = 'iippp'; - - - - function ___syscall_recvfrom(fd, buf, len, flags, addr, addrlen) { - try { - - var sock = getSocketFromFD(fd); - var msg = sock.sock_ops.recvmsg(sock, len, typeof flags !== "undefined" ? flags : 0); - if (!msg) return 0; // socket is closed - if (addr) { - var errno = writeSockaddr(addr, sock.family, DNS.lookup_name(msg.addr), msg.port, addrlen); - } - HEAPU8.set(msg.buffer, buf); - return msg.buffer.byteLength; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_recvfrom.sig = 'iippipp'; - - - - function ___syscall_recvmsg(fd, message, flags, d1, d2, d3) { - try { - - var sock = getSocketFromFD(fd); - var iov = HEAPU32[(((message)+(8))>>2)]; - var num = HEAP32[(((message)+(12))>>2)]; - // get the total amount of data we can read across all arrays - var total = 0; - for (var i = 0; i < num; i++) { - total += HEAP32[(((iov)+((8 * i) + 4))>>2)]; - } - // try to read total data - var msg = sock.sock_ops.recvmsg(sock, total); - if (!msg) return 0; // socket is closed - - // TODO honor flags: - // MSG_OOB - // Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific. - // MSG_PEEK - // Peeks at the incoming message. - // MSG_WAITALL - // Requests that the function block until the full amount of data requested can be returned. The function may return a smaller amount of data if a signal is caught, if the connection is terminated, if MSG_PEEK was specified, or if an error is pending for the socket. - - // write the source address out - var name = HEAPU32[((message)>>2)]; - if (name) { - var errno = writeSockaddr(name, sock.family, DNS.lookup_name(msg.addr), msg.port); - } - // write the buffer out to the scatter-gather arrays - var bytesRead = 0; - var bytesRemaining = msg.buffer.byteLength; - for (var i = 0; bytesRemaining > 0 && i < num; i++) { - var iovbase = HEAPU32[(((iov)+((8 * i) + 0))>>2)]; - var iovlen = HEAP32[(((iov)+((8 * i) + 4))>>2)]; - if (!iovlen) { - continue; - } - var length = Math.min(iovlen, bytesRemaining); - var buf = msg.buffer.subarray(bytesRead, bytesRead + length); - HEAPU8.set(buf, iovbase + bytesRead); - bytesRead += length; - bytesRemaining -= length; - } - - // TODO set msghdr.msg_flags - // MSG_EOR - // End of record was received (if supported by the protocol). - // MSG_OOB - // Out-of-band data was received. - // MSG_TRUNC - // Normal data was truncated. - // MSG_CTRUNC - - return bytesRead; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_recvmsg.sig = 'iipiiii'; - - function ___syscall_renameat(olddirfd, oldpath, newdirfd, newpath) { - try { - - oldpath = SYSCALLS.getStr(oldpath); - newpath = SYSCALLS.getStr(newpath); - oldpath = SYSCALLS.calculateAt(olddirfd, oldpath); - newpath = SYSCALLS.calculateAt(newdirfd, newpath); - FS.rename(oldpath, newpath); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_renameat.sig = 'iipip'; - - function ___syscall_rmdir(path) { - try { - - path = SYSCALLS.getStr(path); - FS.rmdir(path); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_rmdir.sig = 'ip'; - - - function ___syscall_sendmsg(fd, message, flags, d1, d2, d3) { - try { - - var sock = getSocketFromFD(fd); - var iov = HEAPU32[(((message)+(8))>>2)]; - var num = HEAP32[(((message)+(12))>>2)]; - // read the address and port to send to - var addr, port; - var name = HEAPU32[((message)>>2)]; - var namelen = HEAP32[(((message)+(4))>>2)]; - if (name) { - var info = getSocketAddress(name, namelen); - port = info.port; - addr = info.addr; - } - // concatenate scatter-gather arrays into one message buffer - var total = 0; - for (var i = 0; i < num; i++) { - total += HEAP32[(((iov)+((8 * i) + 4))>>2)]; - } - var view = new Uint8Array(total); - var offset = 0; - for (var i = 0; i < num; i++) { - var iovbase = HEAPU32[(((iov)+((8 * i) + 0))>>2)]; - var iovlen = HEAP32[(((iov)+((8 * i) + 4))>>2)]; - for (var j = 0; j < iovlen; j++) { - view[offset++] = HEAP8[(iovbase)+(j)]; - } - } - // write the buffer - return sock.sock_ops.sendmsg(sock, view, 0, total, addr, port); - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_sendmsg.sig = 'iipippi'; - - - function ___syscall_sendto(fd, message, length, flags, addr, addr_len) { - try { - - var sock = getSocketFromFD(fd); - if (!addr) { - // send, no address provided - return FS.write(sock.stream, HEAP8, message, length); - } - var dest = getSocketAddress(addr, addr_len); - // sendto an address - return sock.sock_ops.sendmsg(sock, HEAP8, message, length, dest.addr, dest.port); - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_sendto.sig = 'iippipp'; - - function ___syscall_socket(domain, type, protocol) { - try { - - var sock = SOCKFS.createSocket(domain, type, protocol); - return sock.stream.fd; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_socket.sig = 'iiiiiii'; - - function ___syscall_stat64(path, buf) { - try { - - path = SYSCALLS.getStr(path); - return SYSCALLS.writeStat(buf, FS.stat(path)); - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_stat64.sig = 'ipp'; - - function ___syscall_statfs64(path, size, buf) { - try { - - SYSCALLS.writeStatFs(buf, FS.statfs(SYSCALLS.getStr(path))); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_statfs64.sig = 'ippp'; - - function ___syscall_symlinkat(target, dirfd, linkpath) { - try { - - target = SYSCALLS.getStr(target); - linkpath = SYSCALLS.getStr(linkpath); - linkpath = SYSCALLS.calculateAt(dirfd, linkpath); - FS.symlink(target, linkpath); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_symlinkat.sig = 'ipip'; - - - function ___syscall_truncate64(path, length) { - length = bigintToI53Checked(length); - - - try { - - if (isNaN(length)) return -61; - path = SYSCALLS.getStr(path); - FS.truncate(path, length); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - ; - } - ___syscall_truncate64.sig = 'ipj'; - - function ___syscall_unlinkat(dirfd, path, flags) { - try { - - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path); - if (!flags) { - FS.unlink(path); - } else if (flags === 512) { - FS.rmdir(path); - } else { - return -28; - } - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_unlinkat.sig = 'iipi'; - - var readI53FromI64 = (ptr) => { - return HEAPU32[((ptr)>>2)] + HEAP32[(((ptr)+(4))>>2)] * 4294967296; - }; - - function ___syscall_utimensat(dirfd, path, times, flags) { - try { - - path = SYSCALLS.getStr(path); - path = SYSCALLS.calculateAt(dirfd, path, true); - var now = Date.now(), atime, mtime; - if (!times) { - atime = now; - mtime = now; - } else { - var seconds = readI53FromI64(times); - var nanoseconds = HEAP32[(((times)+(8))>>2)]; - if (nanoseconds == 1073741823) { - atime = now; - } else if (nanoseconds == 1073741822) { - atime = null; - } else { - atime = (seconds*1000) + (nanoseconds/(1000*1000)); - } - times += 16; - seconds = readI53FromI64(times); - nanoseconds = HEAP32[(((times)+(8))>>2)]; - if (nanoseconds == 1073741823) { - mtime = now; - } else if (nanoseconds == 1073741822) { - mtime = null; - } else { - mtime = (seconds*1000) + (nanoseconds/(1000*1000)); - } - } - // null here means UTIME_OMIT was passed. If both were set to UTIME_OMIT then - // we can skip the call completely. - if ((mtime ?? atime) !== null) { - FS.utime(path, atime, mtime); - } - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_utimensat.sig = 'iippi'; - - var __abort_js = () => - abort(''); - __abort_js.sig = 'v'; - - - - - var dlSetError = (msg) => { - var sp = stackSave(); - var cmsg = stringToUTF8OnStack(msg); - ___dl_seterr(cmsg, 0); - stackRestore(sp); - }; - - - var dlopenInternal = (handle, jsflags) => { - // void *dlopen(const char *file, int mode); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html - var filename = UTF8ToString(handle + 36); - var flags = HEAP32[(((handle)+(4))>>2)]; - filename = PATH.normalize(filename); - var searchpaths = []; - - var global = Boolean(flags & 256); - var localScope = global ? null : {}; - - // We don't care about RTLD_NOW and RTLD_LAZY. - var combinedFlags = { - global, - nodelete: Boolean(flags & 4096), - loadAsync: jsflags.loadAsync, - } - - if (jsflags.loadAsync) { - return loadDynamicLibrary(filename, combinedFlags, localScope, handle); - } - - try { - return loadDynamicLibrary(filename, combinedFlags, localScope, handle) - } catch (e) { - dlSetError(`could not load dynamic lib: ${filename}\n${e}`); - return 0; - } - }; - function __dlopen_js(handle) { - var jsflags = { loadAsync: false } - return dlopenInternal(handle, jsflags); - } - __dlopen_js.sig = 'pp'; - - - - - var __dlsym_js = (handle, symbol, symbolIndex) => { - // void *dlsym(void *restrict handle, const char *restrict name); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html - symbol = UTF8ToString(symbol); - var result; - var newSymIndex; - - var lib = LDSO.loadedLibsByHandle[handle]; - newSymIndex = Object.keys(lib.exports).indexOf(symbol); - if (newSymIndex == -1 || lib.exports[symbol].stub) { - dlSetError(`Tried to lookup unknown symbol "${symbol}" in dynamic lib: ${lib.name}`) - return 0; - } - result = lib.exports[symbol]; - - if (typeof result == 'function') { - - // Asyncify wraps exports, and we need to look through those wrappers. - if (result.orig) { - result = result.orig; - } - var addr = getFunctionAddress(result); - if (addr) { - result = addr; - } else { - // Insert the function into the wasm table. If its a direct wasm - // function the second argument will not be needed. If its a JS - // function we rely on the `sig` attribute being set based on the - // `__sig` specified in library JS file. - result = addFunction(result, result.sig); - HEAPU32[((symbolIndex)>>2)] = newSymIndex; - } - } - return result; - }; - __dlsym_js.sig = 'pppp'; - - - 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; - } - quit_(1, e); - }; - - - var runtimeKeepaliveCounter = 0; - var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; - var _proc_exit = (code) => { - EXITSTATUS = code; - if (!keepRuntimeAlive()) { - Module['onExit']?.(code); - ABORT = true; - } - quit_(code, new ExitStatus(code)); - }; - _proc_exit.sig = 'vi'; - - /** @param {boolean|number=} implicit */ - var exitJS = (status, implicit) => { - EXITSTATUS = status; - - if (!keepRuntimeAlive()) { - exitRuntime(); - } - - _proc_exit(status); - }; - var _exit = exitJS; - _exit.sig = 'vi'; - - - var maybeExit = () => { - if (runtimeExited) { - return; - } - if (!keepRuntimeAlive()) { - try { - _exit(EXITSTATUS); - } catch (e) { - handleException(e); - } - } - }; - var callUserCallback = (func) => { - if (runtimeExited || ABORT) { - return; - } - try { - func(); - maybeExit(); - } catch (e) { - handleException(e); - } - }; - - - var runtimeKeepalivePush = () => { - runtimeKeepaliveCounter += 1; - }; - runtimeKeepalivePush.sig = 'v'; - - var runtimeKeepalivePop = () => { - runtimeKeepaliveCounter -= 1; - }; - runtimeKeepalivePop.sig = 'v'; - - var __emscripten_dlopen_js = (handle, onsuccess, onerror, user_data) => { - /** @param {Object=} e */ - function errorCallback(e) { - var filename = UTF8ToString(handle + 36); - dlSetError(`'Could not load dynamic lib: ${filename}\n${e}`); - runtimeKeepalivePop(); - callUserCallback(() => ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, user_data)); - } - function successCallback() { - runtimeKeepalivePop(); - callUserCallback(() => ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, user_data)); - } - - runtimeKeepalivePush(); - var promise = dlopenInternal(handle, { loadAsync: true }); - if (promise) { - promise.then(successCallback, errorCallback); - } else { - errorCallback(); - } - }; - __emscripten_dlopen_js.sig = 'vpppp'; - - var getExecutableName = () => thisProgram || './this.program'; - - var __emscripten_get_progname = (str, len) => stringToUTF8(getExecutableName(), str, len); - __emscripten_get_progname.sig = 'vpi'; - - var jsStackTrace = () => new Error().stack.toString(); - /** @param {number=} flags */ - var getCallstack = (flags) => { - var callstack = jsStackTrace(); - - // Process all lines: - var lines = callstack.split('\n'); - callstack = ''; - // Extract components of form: - // ' Object._main@http://server.com:4324:12' - var firefoxRe = new RegExp('\\s*(.*?)@(.*?):([0-9]+):([0-9]+)'); - // Extract components of form: - // ' at Object._main (http://server.com/file.html:4324:12)' - var chromeRe = new RegExp('\\s*at (.*?) \\\((.*):(.*):(.*)\\\)'); - - for (var line of lines) { - var symbolName = ''; - var file = ''; - var lineno = 0; - var column = 0; - - var parts = chromeRe.exec(line); - if (parts?.length == 5) { - symbolName = parts[1]; - file = parts[2]; - lineno = parts[3]; - column = parts[4]; - } else { - parts = firefoxRe.exec(line); - if (parts?.length >= 4) { - symbolName = parts[1]; - file = parts[2]; - lineno = parts[3]; - // Old Firefox doesn't carry column information, but in new FF30, it - // is present. See https://bugzil.la/762556 - column = parts[4]|0; - } else { - // Was not able to extract this line for demangling/sourcemapping - // purposes. Output it as-is. - callstack += line + '\n'; - continue; - } - } - - // Find the symbols in the callstack that corresponds to the functions that - // report callstack information, and remove everything up to these from the - // output. - if (symbolName == '_emscripten_log' || symbolName == '_emscripten_get_callstack') { - callstack = ''; - continue; - } - - if ((flags & 24)) { - if (flags & 64) { - file = file.substring(file.replace(/\\/g, "/").lastIndexOf('/')+1); - } - callstack += ` at ${symbolName} (${file}:${lineno}:${column})\n`; - } - } - // Trim extra whitespace at the end of the output. - callstack = callstack.replace(/\s+$/, ''); - return callstack; - }; - - var __emscripten_log_formatted = (flags, str) => { - str = UTF8ToString(str); - - if (flags & 24) { - str = str.replace(/\s+$/, ''); // Ensure the message and the callstack are joined cleanly with exactly one newline. - str += (str.length > 0 ? '\n' : '') + getCallstack(flags); - } - - if (flags & 1) { - if (flags & 4) { - console.error(str); - } else if (flags & 2) { - console.warn(str); - } else if (flags & 512) { - console.info(str); - } else if (flags & 256) { - console.debug(str); - } else { - console.log(str); - } - } else if (flags & 6) { - err(str); - } else { - out(str); - } - }; - __emscripten_log_formatted.sig = 'vip'; - - - - - var __emscripten_lookup_name = (name) => { - // uint32_t _emscripten_lookup_name(const char *name); - var nameString = UTF8ToString(name); - return inetPton4(DNS.lookup_name(nameString)); - }; - __emscripten_lookup_name.sig = 'ip'; - - var __emscripten_runtime_keepalive_clear = () => { - noExitRuntime = false; - runtimeKeepaliveCounter = 0; - }; - __emscripten_runtime_keepalive_clear.sig = 'v'; - - var __emscripten_system = (command) => { - if (ENVIRONMENT_IS_NODE) { - if (!command) return 1; // shell is available - - var cmdstr = UTF8ToString(command); - if (!cmdstr.length) return 0; // this is what glibc seems to do (shell works test?) - - var cp = require('child_process'); - var ret = cp.spawnSync(cmdstr, [], {shell:true, stdio:'inherit'}); - - var _W_EXITCODE = (ret, sig) => ((ret) << 8 | (sig)); - - // this really only can happen if process is killed by signal - if (ret.status === null) { - // sadly node doesn't expose such function - var signalToNumber = (sig) => { - // implement only the most common ones, and fallback to SIGINT - switch (sig) { - case 'SIGHUP': return 1; - case 'SIGQUIT': return 3; - case 'SIGFPE': return 8; - case 'SIGKILL': return 9; - case 'SIGALRM': return 14; - case 'SIGTERM': return 15; - default: return 2; - } - } - return _W_EXITCODE(0, signalToNumber(ret.signal)); - } - - return _W_EXITCODE(ret.status, 0); - } - // int system(const char *command); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html - // Can't call external programs. - if (!command) return 0; // no shell available - return -52; - }; - __emscripten_system.sig = 'ip'; - - var __emscripten_throw_longjmp = () => { - throw Infinity; - }; - __emscripten_throw_longjmp.sig = 'v'; - - function __gmtime_js(time, tmPtr) { - time = bigintToI53Checked(time); - - - var date = new Date(time * 1000); - HEAP32[((tmPtr)>>2)] = date.getUTCSeconds(); - HEAP32[(((tmPtr)+(4))>>2)] = date.getUTCMinutes(); - HEAP32[(((tmPtr)+(8))>>2)] = date.getUTCHours(); - HEAP32[(((tmPtr)+(12))>>2)] = date.getUTCDate(); - HEAP32[(((tmPtr)+(16))>>2)] = date.getUTCMonth(); - HEAP32[(((tmPtr)+(20))>>2)] = date.getUTCFullYear()-1900; - HEAP32[(((tmPtr)+(24))>>2)] = date.getUTCDay(); - var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); - var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0; - HEAP32[(((tmPtr)+(28))>>2)] = yday; - ; - } - __gmtime_js.sig = 'vjp'; - - 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*1000); - HEAP32[((tmPtr)>>2)] = date.getSeconds(); - HEAP32[(((tmPtr)+(4))>>2)] = date.getMinutes(); - HEAP32[(((tmPtr)+(8))>>2)] = date.getHours(); - HEAP32[(((tmPtr)+(12))>>2)] = date.getDate(); - HEAP32[(((tmPtr)+(16))>>2)] = date.getMonth(); - HEAP32[(((tmPtr)+(20))>>2)] = date.getFullYear()-1900; - HEAP32[(((tmPtr)+(24))>>2)] = date.getDay(); - - var yday = ydayFromDate(date)|0; - HEAP32[(((tmPtr)+(28))>>2)] = yday; - HEAP32[(((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; - HEAP32[(((tmPtr)+(32))>>2)] = dst; - ; - } - __localtime_js.sig = 'vjp'; - - - var __mktime_js = function(tmPtr) { - - var ret = (() => { - var date = new Date(HEAP32[(((tmPtr)+(20))>>2)] + 1900, - HEAP32[(((tmPtr)+(16))>>2)], - HEAP32[(((tmPtr)+(12))>>2)], - HEAP32[(((tmPtr)+(8))>>2)], - HEAP32[(((tmPtr)+(4))>>2)], - HEAP32[((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 = HEAP32[(((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. - HEAP32[(((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)*60000); - } - - HEAP32[(((tmPtr)+(24))>>2)] = date.getDay(); - var yday = ydayFromDate(date)|0; - HEAP32[(((tmPtr)+(28))>>2)] = yday; - // To match expected behavior, update fields from date - HEAP32[((tmPtr)>>2)] = date.getSeconds(); - HEAP32[(((tmPtr)+(4))>>2)] = date.getMinutes(); - HEAP32[(((tmPtr)+(8))>>2)] = date.getHours(); - HEAP32[(((tmPtr)+(12))>>2)] = date.getDate(); - HEAP32[(((tmPtr)+(16))>>2)] = date.getMonth(); - HEAP32[(((tmPtr)+(20))>>2)] = date.getYear(); - - var timeMs = date.getTime(); - if (isNaN(timeMs)) { - return -1; - } - // Return time in microseconds - return timeMs / 1000; - })(); - return BigInt(ret); - }; - __mktime_js.sig = 'jp'; - - - - - - - function __mmap_js(len, prot, flags, fd, offset, allocated, addr) { - offset = bigintToI53Checked(offset); - - - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - var res = FS.mmap(stream, len, offset, prot, flags); - var ptr = res.ptr; - HEAP32[((allocated)>>2)] = res.allocated; - HEAPU32[((addr)>>2)] = ptr; - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - ; - } - __mmap_js.sig = 'ipiiijpp'; - - - function __msync_js(addr, len, prot, flags, fd, offset) { - offset = bigintToI53Checked(offset); - - - try { - - if (isNaN(offset)) return -61; - SYSCALLS.doMsync(addr, SYSCALLS.getStreamFromFD(fd), len, flags, offset); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - ; - } - __msync_js.sig = 'ippiiij'; - - - function __munmap_js(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; - } - ; - } - __munmap_js.sig = 'ippiiij'; - - var timers = { - }; - - - - var _emscripten_get_now = () => performance.now(); - _emscripten_get_now.sig = 'd'; - var __setitimer_js = (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(() => { - delete timers[which]; - callUserCallback(() => __emscripten_timeout(which, _emscripten_get_now())); - }, timeout_ms); - timers[which] = { id, timeout_ms }; - return 0; - }; - __setitimer_js.sig = 'iid'; - - var __timegm_js = function(tmPtr) { - - var ret = (() => { - var time = Date.UTC(HEAP32[(((tmPtr)+(20))>>2)] + 1900, - HEAP32[(((tmPtr)+(16))>>2)], - HEAP32[(((tmPtr)+(12))>>2)], - HEAP32[(((tmPtr)+(8))>>2)], - HEAP32[(((tmPtr)+(4))>>2)], - HEAP32[((tmPtr)>>2)], - 0); - var date = new Date(time); - - HEAP32[(((tmPtr)+(24))>>2)] = date.getUTCDay(); - var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); - var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0; - HEAP32[(((tmPtr)+(28))>>2)] = yday; - - return date.getTime() / 1000; - })(); - return BigInt(ret); - }; - __timegm_js.sig = 'jp'; - - 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 - HEAPU32[((timezone)>>2)] = stdTimezoneOffset * 60; - - HEAP32[((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); - 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); - } - }; - __tzset_js.sig = 'vpppp'; - - - - var _emscripten_set_main_loop_timing = (mode, value) => { - MainLoop.timingMode = mode; - MainLoop.timingValue = value; - - if (!MainLoop.func) { - return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. - } - - if (!MainLoop.running) { - runtimeKeepalivePush(); - MainLoop.running = true; - } - if (mode == 0) { - MainLoop.scheduler = function MainLoop_scheduler_setTimeout() { - var timeUntilNextTick = Math.max(0, MainLoop.tickStartTime + value - _emscripten_get_now())|0; - setTimeout(MainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop - }; - MainLoop.method = 'timeout'; - } else if (mode == 1) { - MainLoop.scheduler = function MainLoop_scheduler_rAF() { - MainLoop.requestAnimationFrame(MainLoop.runner); - }; - MainLoop.method = 'rAF'; - } else if (mode == 2) { - if (!MainLoop.setImmediate) { - if (globalThis.setImmediate) { - MainLoop.setImmediate = setImmediate; - } else { - // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) - var setImmediates = []; - var emscriptenMainLoopMessageId = 'setimmediate'; - /** @param {Event} event */ - var MainLoop_setImmediate_messageHandler = (event) => { - // When called in current thread or Worker, the main loop ID is structured slightly different to accommodate for --proxy-to-worker runtime listening to Worker events, - // so check for both cases. - if (event.data === emscriptenMainLoopMessageId || event.data.target === emscriptenMainLoopMessageId) { - event.stopPropagation(); - setImmediates.shift()(); - } - }; - addEventListener("message", MainLoop_setImmediate_messageHandler, true); - MainLoop.setImmediate = /** @type{function(function(): ?, ...?): number} */((func) => { - setImmediates.push(func); - if (ENVIRONMENT_IS_WORKER) { - Module['setImmediates'] ??= []; - Module['setImmediates'].push(func); - postMessage({target: emscriptenMainLoopMessageId}); // In --proxy-to-worker, route the message via proxyClient.js - } else postMessage(emscriptenMainLoopMessageId, "*"); // On the main thread, can just send the message to itself. - }); - } - } - MainLoop.scheduler = function MainLoop_scheduler_setImmediate() { - MainLoop.setImmediate(MainLoop.runner); - }; - MainLoop.method = 'immediate'; - } - return 0; - }; - _emscripten_set_main_loop_timing.sig = 'iii'; - - - - - /** - * @param {number=} arg - * @param {boolean=} noSetTiming - */ - var setMainLoop = (iterFunc, fps, simulateInfiniteLoop, arg, noSetTiming) => { - MainLoop.func = iterFunc; - MainLoop.arg = arg; - - var thisMainLoopId = MainLoop.currentlyRunningMainloop; - function checkIsRunning() { - if (thisMainLoopId < MainLoop.currentlyRunningMainloop) { - runtimeKeepalivePop(); - maybeExit(); - return false; - } - return true; - } - - // We create the loop runner here but it is not actually running until - // _emscripten_set_main_loop_timing is called (which might happen a - // later time). This member signifies that the current runner has not - // yet been started so that we can call runtimeKeepalivePush when it - // gets it timing set for the first time. - MainLoop.running = false; - MainLoop.runner = function MainLoop_runner() { - if (ABORT) return; - if (MainLoop.queue.length > 0) { - var start = Date.now(); - var blocker = MainLoop.queue.shift(); - blocker.func(blocker.arg); - if (MainLoop.remainingBlockers) { - var remaining = MainLoop.remainingBlockers; - var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining); - if (blocker.counted) { - MainLoop.remainingBlockers = next; - } else { - // not counted, but move the progress along a tiny bit - next = next + 0.5; // do not steal all the next one's progress - MainLoop.remainingBlockers = (8*remaining + next)/9; - } - } - MainLoop.updateStatus(); - - // catches pause/resume main loop from blocker execution - if (!checkIsRunning()) return; - - setTimeout(MainLoop.runner, 0); - return; - } - - // catch pauses from non-main loop sources - if (!checkIsRunning()) return; - - // Implement very basic swap interval control - MainLoop.currentFrameNumber = MainLoop.currentFrameNumber + 1 | 0; - if (MainLoop.timingMode == 1 && MainLoop.timingValue > 1 && MainLoop.currentFrameNumber % MainLoop.timingValue != 0) { - // Not the scheduled time to render this frame - skip. - MainLoop.scheduler(); - return; - } else if (MainLoop.timingMode == 0) { - MainLoop.tickStartTime = _emscripten_get_now(); - } - - MainLoop.runIter(iterFunc); - - // catch pauses from the main loop itself - if (!checkIsRunning()) return; - - MainLoop.scheduler(); - } - - if (!noSetTiming) { - if (fps > 0) { - _emscripten_set_main_loop_timing(0, 1000.0 / fps); - } else { - // Do rAF by rendering each frame (no decimating) - _emscripten_set_main_loop_timing(1, 1); - } - - MainLoop.scheduler(); - } - - if (simulateInfiniteLoop) { - throw 'unwind'; - } - }; - - - var MainLoop = { - running:false, - scheduler:null, - method:"", - currentlyRunningMainloop:0, - func:null, - arg:0, - timingMode:0, - timingValue:0, - currentFrameNumber:0, - queue:[], - preMainLoop:[], - postMainLoop:[], - pause() { - MainLoop.scheduler = null; - // Incrementing this signals the previous main loop that it's now become old, and it must return. - MainLoop.currentlyRunningMainloop++; - }, - resume() { - MainLoop.currentlyRunningMainloop++; - var timingMode = MainLoop.timingMode; - var timingValue = MainLoop.timingValue; - var func = MainLoop.func; - MainLoop.func = null; - // do not set timing and call scheduler, we will do it on the next lines - setMainLoop(func, 0, false, MainLoop.arg, true); - _emscripten_set_main_loop_timing(timingMode, timingValue); - MainLoop.scheduler(); - }, - updateStatus() { - if (Module['setStatus']) { - var message = Module['statusMessage'] || 'Please wait...'; - var remaining = MainLoop.remainingBlockers ?? 0; - var expected = MainLoop.expectedBlockers ?? 0; - if (remaining) { - if (remaining < expected) { - Module['setStatus'](`{message} ({expected - remaining}/{expected})`); - } else { - Module['setStatus'](message); - } - } else { - Module['setStatus'](''); - } - } - }, - init() { - Module['preMainLoop'] && MainLoop.preMainLoop.push(Module['preMainLoop']); - Module['postMainLoop'] && MainLoop.postMainLoop.push(Module['postMainLoop']); - }, - runIter(func) { - if (ABORT) return; - for (var pre of MainLoop.preMainLoop) { - if (pre() === false) { - return; // |return false| skips a frame - } - } - callUserCallback(func); - for (var post of MainLoop.postMainLoop) { - post(); - } - }, - nextRAF:0, - fakeRequestAnimationFrame(func) { - // try to keep 60fps between calls to here - var now = Date.now(); - if (MainLoop.nextRAF === 0) { - MainLoop.nextRAF = now + 1000/60; - } else { - while (now + 2 >= MainLoop.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0 - MainLoop.nextRAF += 1000/60; - } - } - var delay = Math.max(MainLoop.nextRAF - now, 0); - setTimeout(func, delay); - }, - requestAnimationFrame(func) { - if (globalThis.requestAnimationFrame) { - requestAnimationFrame(func); - } else { - MainLoop.fakeRequestAnimationFrame(func); - } - }, - }; - - var AL = { - QUEUE_INTERVAL:25, - QUEUE_LOOKAHEAD:0.1, - DEVICE_NAME:"Emscripten OpenAL", - CAPTURE_DEVICE_NAME:"Emscripten OpenAL capture", - ALC_EXTENSIONS:{ - ALC_SOFT_pause_device:true, - ALC_SOFT_HRTF:true, - }, - AL_EXTENSIONS:{ - AL_EXT_float32:true, - AL_SOFT_loop_points:true, - AL_SOFT_source_length:true, - AL_EXT_source_distance_model:true, - AL_SOFT_source_spatialize:true, - }, - _alcErr:0, - alcErr:0, - deviceRefCounts:{ - }, - alcStringCache:{ - }, - paused:false, - stringCache:{ - }, - contexts:{ - }, - currentCtx:null, - buffers:{ - 0:{ - id:0, - refCount:0, - audioBuf:null, - frequency:0, - bytesPerSample:2, - channels:1, - length:0, - }, - }, - paramArray:[], - _nextId:1, - newId:() => AL.freeIds.length > 0 ? AL.freeIds.pop() : AL._nextId++, - freeIds:[], - scheduleContextAudio:(ctx) => { - // If we are animating using the requestAnimationFrame method, then the main loop does not run when in the background. - // To give a perfect glitch-free audio stop when switching from foreground to background, we need to avoid updating - // audio altogether when in the background, so detect that case and kill audio buffer streaming if so. - if (MainLoop.timingMode === 1 && document['visibilityState'] != 'visible') { - return; - } - - for (var i in ctx.sources) { - AL.scheduleSourceAudio(ctx.sources[i]); - } - }, - scheduleSourceAudio:(src, lookahead) => { - // See comment on scheduleContextAudio above. - if (MainLoop.timingMode === 1 && document['visibilityState'] != 'visible') { - return; - } - if (src.state !== 4114) { - return; - } - - var currentTime = AL.updateSourceTime(src); - - var startTime = src.bufStartTime; - var startOffset = src.bufOffset; - var bufCursor = src.bufsProcessed; - - // Advance past any audio that is already scheduled - for (var i = 0; i < src.audioQueue.length; i++) { - var audioSrc = src.audioQueue[i]; - startTime = audioSrc._startTime + audioSrc._duration; - startOffset = 0.0; - bufCursor += audioSrc._skipCount + 1; - } - - if (!lookahead) { - lookahead = AL.QUEUE_LOOKAHEAD; - } - var lookaheadTime = currentTime + lookahead; - var skipCount = 0; - while (startTime < lookaheadTime) { - if (bufCursor >= src.bufQueue.length) { - if (src.looping) { - bufCursor %= src.bufQueue.length; - } else { - break; - } - } - - var buf = src.bufQueue[bufCursor % src.bufQueue.length]; - // If the buffer contains no data, skip it - if (buf.length === 0) { - skipCount++; - // If we've gone through the whole queue and everything is 0 length, just give up - if (skipCount === src.bufQueue.length) { - break; - } - } else { - var audioSrc = src.context.audioCtx.createBufferSource(); - audioSrc.buffer = buf.audioBuf; - audioSrc.playbackRate.value = src.playbackRate; - if (buf.audioBuf._loopStart || buf.audioBuf._loopEnd) { - audioSrc.loopStart = buf.audioBuf._loopStart; - audioSrc.loopEnd = buf.audioBuf._loopEnd; - } - - var duration = 0.0; - // If the source is a looping static buffer, use native looping for gapless playback - if (src.type === 4136 && src.looping) { - duration = Number.POSITIVE_INFINITY; - audioSrc.loop = true; - if (buf.audioBuf._loopStart) { - audioSrc.loopStart = buf.audioBuf._loopStart; - } - if (buf.audioBuf._loopEnd) { - audioSrc.loopEnd = buf.audioBuf._loopEnd; - } - } else { - duration = (buf.audioBuf.duration - startOffset) / src.playbackRate; - } - - audioSrc._startOffset = startOffset; - audioSrc._duration = duration; - audioSrc._skipCount = skipCount; - skipCount = 0; - - audioSrc.connect(src.gain); - - if (typeof audioSrc.start != 'undefined') { - // Sample the current time as late as possible to mitigate drift - startTime = Math.max(startTime, src.context.audioCtx.currentTime); - audioSrc.start(startTime, startOffset); - } else if (typeof audioSrc.noteOn != 'undefined') { - startTime = Math.max(startTime, src.context.audioCtx.currentTime); - audioSrc.noteOn(startTime); - } - audioSrc._startTime = startTime; - src.audioQueue.push(audioSrc); - - startTime += duration; - } - - startOffset = 0.0; - bufCursor++; - } - }, - updateSourceTime:(src) => { - var currentTime = src.context.audioCtx.currentTime; - if (src.state !== 4114) { - return currentTime; - } - - // if the start time is unset, determine it based on the current offset. - // This will be the case when a source is resumed after being paused, and - // allows us to pretend that the source actually started playing some time - // in the past such that it would just now have reached the stored offset. - if (!isFinite(src.bufStartTime)) { - src.bufStartTime = currentTime - src.bufOffset / src.playbackRate; - src.bufOffset = 0.0; - } - - var nextStartTime = 0.0; - while (src.audioQueue.length) { - var audioSrc = src.audioQueue[0]; - src.bufsProcessed += audioSrc._skipCount; - nextStartTime = audioSrc._startTime + audioSrc._duration; // n.b. audioSrc._duration already factors in playbackRate, so no divide by src.playbackRate on it. - - if (currentTime < nextStartTime) { - break; - } - - src.audioQueue.shift(); - src.bufStartTime = nextStartTime; - src.bufOffset = 0.0; - src.bufsProcessed++; - } - - if (src.bufsProcessed >= src.bufQueue.length && !src.looping) { - // The source has played its entire queue and is non-looping, so just mark it as stopped. - AL.setSourceState(src, 4116); - } else if (src.type === 4136 && src.looping) { - // If the source is a looping static buffer, determine the buffer offset based on the loop points - var buf = src.bufQueue[0]; - if (buf.length === 0) { - src.bufOffset = 0.0; - } else { - var delta = (currentTime - src.bufStartTime) * src.playbackRate; - var loopStart = buf.audioBuf._loopStart || 0.0; - var loopEnd = buf.audioBuf._loopEnd || buf.audioBuf.duration; - if (loopEnd <= loopStart) { - loopEnd = buf.audioBuf.duration; - } - - if (delta < loopEnd) { - src.bufOffset = delta; - } else { - src.bufOffset = loopStart + (delta - loopStart) % (loopEnd - loopStart); - } - } - } else if (src.audioQueue[0]) { - // The source is still actively playing, so we just need to calculate where we are in the current buffer - // so it can be remembered if the source gets paused. - src.bufOffset = (currentTime - src.audioQueue[0]._startTime) * src.playbackRate; - } else { - // The source hasn't finished yet, but there is no scheduled audio left for it. This can be because - // the source has just been started/resumed, or due to an underrun caused by a long blocking operation. - // We need to determine what state we would be in by this point in time so that when we next schedule - // audio playback, it will be just as if no underrun occurred. - - if (src.type !== 4136 && src.looping) { - // if the source is a looping buffer queue, let's first calculate the queue duration, so we can - // quickly fast forward past any full loops of the queue and only worry about the remainder. - var srcDuration = AL.sourceDuration(src) / src.playbackRate; - if (srcDuration > 0.0) { - src.bufStartTime += Math.floor((currentTime - src.bufStartTime) / srcDuration) * srcDuration; - } - } - - // Since we've already skipped any full-queue loops if there were any, we just need to find - // out where in the queue the remaining time puts us, which won't require stepping through the - // entire queue more than once. - for (var i = 0; i < src.bufQueue.length; i++) { - if (src.bufsProcessed >= src.bufQueue.length) { - if (src.looping) { - src.bufsProcessed %= src.bufQueue.length; - } else { - AL.setSourceState(src, 4116); - break; - } - } - - var buf = src.bufQueue[src.bufsProcessed]; - if (buf.length > 0) { - nextStartTime = src.bufStartTime + buf.audioBuf.duration / src.playbackRate; - - if (currentTime < nextStartTime) { - src.bufOffset = (currentTime - src.bufStartTime) * src.playbackRate; - break; - } - - src.bufStartTime = nextStartTime; - } - - src.bufOffset = 0.0; - src.bufsProcessed++; - } - } - - return currentTime; - }, - cancelPendingSourceAudio:(src) => { - AL.updateSourceTime(src); - - for (var i = 1; i < src.audioQueue.length; i++) { - var audioSrc = src.audioQueue[i]; - audioSrc.stop(); - } - - if (src.audioQueue.length > 1) { - src.audioQueue.length = 1; - } - }, - stopSourceAudio:(src) => { - for (var i = 0; i < src.audioQueue.length; i++) { - src.audioQueue[i].stop(); - } - src.audioQueue.length = 0; - }, - setSourceState:(src, state) => { - if (state === 4114) { - if (src.state === 4114 || src.state == 4116) { - src.bufsProcessed = 0; - src.bufOffset = 0.0; - } else { - } - - AL.stopSourceAudio(src); - - src.state = 4114; - src.bufStartTime = Number.NEGATIVE_INFINITY; - AL.scheduleSourceAudio(src); - } else if (state === 4115) { - if (src.state === 4114) { - // Store off the current offset to restore with on resume. - AL.updateSourceTime(src); - AL.stopSourceAudio(src); - - src.state = 4115; - } - } else if (state === 4116) { - if (src.state !== 4113) { - src.state = 4116; - src.bufsProcessed = src.bufQueue.length; - src.bufStartTime = Number.NEGATIVE_INFINITY; - src.bufOffset = 0.0; - AL.stopSourceAudio(src); - } - } else if (state === 4113) { - if (src.state !== 4113) { - src.state = 4113; - src.bufsProcessed = 0; - src.bufStartTime = Number.NEGATIVE_INFINITY; - src.bufOffset = 0.0; - AL.stopSourceAudio(src); - } - } - }, - initSourcePanner:(src) => { - if (src.type === 0x1030 /* AL_UNDETERMINED */) { - return; - } - - // Find the first non-zero buffer in the queue to determine the proper format - var templateBuf = AL.buffers[0]; - for (var i = 0; i < src.bufQueue.length; i++) { - if (src.bufQueue[i].id !== 0) { - templateBuf = src.bufQueue[i]; - break; - } - } - // Create a panner if AL_SOURCE_SPATIALIZE_SOFT is set to true, or alternatively if it's set to auto and the source is mono - if (src.spatialize === 1 || (src.spatialize === 2 /* AL_AUTO_SOFT */ && templateBuf.channels === 1)) { - if (src.panner) { - return; - } - src.panner = src.context.audioCtx.createPanner(); - - AL.updateSourceGlobal(src); - AL.updateSourceSpace(src); - - src.panner.connect(src.context.gain); - src.gain.disconnect(); - src.gain.connect(src.panner); - } else { - if (!src.panner) { - return; - } - - src.panner.disconnect(); - src.gain.disconnect(); - src.gain.connect(src.context.gain); - src.panner = null; - } - }, - updateContextGlobal:(ctx) => { - for (var i in ctx.sources) { - AL.updateSourceGlobal(ctx.sources[i]); - } - }, - updateSourceGlobal:(src) => { - var panner = src.panner; - if (!panner) { - return; - } - - panner.refDistance = src.refDistance; - panner.maxDistance = src.maxDistance; - panner.rolloffFactor = src.rolloffFactor; - - panner.panningModel = src.context.hrtf ? 'HRTF' : 'equalpower'; - - // Use the source's distance model if AL_SOURCE_DISTANCE_MODEL is enabled - var distanceModel = src.context.sourceDistanceModel ? src.distanceModel : src.context.distanceModel; - switch (distanceModel) { - case 0: - panner.distanceModel = 'inverse'; - panner.refDistance = 3.40282e38 /* FLT_MAX */; - break; - case 0xd001 /* AL_INVERSE_DISTANCE */: - case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */: - panner.distanceModel = 'inverse'; - break; - case 0xd003 /* AL_LINEAR_DISTANCE */: - case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */: - panner.distanceModel = 'linear'; - break; - case 0xd005 /* AL_EXPONENT_DISTANCE */: - case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */: - panner.distanceModel = 'exponential'; - break; - } - }, - updateListenerSpace:(ctx) => { - var listener = ctx.audioCtx.listener; - if (listener.positionX) { - listener.positionX.value = ctx.listener.position[0]; - listener.positionY.value = ctx.listener.position[1]; - listener.positionZ.value = ctx.listener.position[2]; - } else { - listener.setPosition(ctx.listener.position[0], ctx.listener.position[1], ctx.listener.position[2]); - } - if (listener.forwardX) { - listener.forwardX.value = ctx.listener.direction[0]; - listener.forwardY.value = ctx.listener.direction[1]; - listener.forwardZ.value = ctx.listener.direction[2]; - listener.upX.value = ctx.listener.up[0]; - listener.upY.value = ctx.listener.up[1]; - listener.upZ.value = ctx.listener.up[2]; - } else { - listener.setOrientation( - ctx.listener.direction[0], ctx.listener.direction[1], ctx.listener.direction[2], - ctx.listener.up[0], ctx.listener.up[1], ctx.listener.up[2]); - } - - // Update sources that are relative to the listener - for (var i in ctx.sources) { - AL.updateSourceSpace(ctx.sources[i]); - } - }, - updateSourceSpace:(src) => { - if (!src.panner) { - return; - } - var panner = src.panner; - - var posX = src.position[0]; - var posY = src.position[1]; - var posZ = src.position[2]; - var dirX = src.direction[0]; - var dirY = src.direction[1]; - var dirZ = src.direction[2]; - - var listener = src.context.listener; - var lPosX = listener.position[0]; - var lPosY = listener.position[1]; - var lPosZ = listener.position[2]; - - // WebAudio does spatialization in world-space coordinates, meaning both the buffer sources and - // the listener position are in the same absolute coordinate system relative to a fixed origin. - // By default, OpenAL works this way as well, but it also provides a "listener relative" mode, where - // a buffer source's coordinate are interpreted not in absolute world space, but as being relative - // to the listener object itself, so as the listener moves the source appears to move with it - // with no update required. Since web audio does not support this mode, we must transform the source - // coordinates from listener-relative space to absolute world space. - // - // We do this via affine transformation matrices applied to the source position and source direction. - // A change-of-basis converts from listener-space displacements to world-space displacements, - // which must be done for both the source position and direction. Lastly, the source position must be - // added to the listener position to get the final source position, since the source position represents - // a displacement from the listener. - if (src.relative) { - // Negate the listener direction since forward is -Z. - var lBackX = -listener.direction[0]; - var lBackY = -listener.direction[1]; - var lBackZ = -listener.direction[2]; - var lUpX = listener.up[0]; - var lUpY = listener.up[1]; - var lUpZ = listener.up[2]; - - var inverseMagnitude = (x, y, z) => { - var length = Math.sqrt(x * x + y * y + z * z); - - if (length < Number.EPSILON) { - return 0.0; - } - - return 1.0 / length; - }; - - // Normalize the Back vector - var invMag = inverseMagnitude(lBackX, lBackY, lBackZ); - lBackX *= invMag; - lBackY *= invMag; - lBackZ *= invMag; - - // ...and the Up vector - invMag = inverseMagnitude(lUpX, lUpY, lUpZ); - lUpX *= invMag; - lUpY *= invMag; - lUpZ *= invMag; - - // Calculate the Right vector as the cross product of the Up and Back vectors - var lRightX = (lUpY * lBackZ - lUpZ * lBackY); - var lRightY = (lUpZ * lBackX - lUpX * lBackZ); - var lRightZ = (lUpX * lBackY - lUpY * lBackX); - - // Back and Up might not be exactly perpendicular, so the cross product also needs normalization - invMag = inverseMagnitude(lRightX, lRightY, lRightZ); - lRightX *= invMag; - lRightY *= invMag; - lRightZ *= invMag; - - // Recompute Up from the now orthonormal Right and Back vectors so we have a fully orthonormal basis - lUpX = (lBackY * lRightZ - lBackZ * lRightY); - lUpY = (lBackZ * lRightX - lBackX * lRightZ); - lUpZ = (lBackX * lRightY - lBackY * lRightX); - - var oldX = dirX; - var oldY = dirY; - var oldZ = dirZ; - - // Use our 3 vectors to apply a change-of-basis matrix to the source direction - dirX = oldX * lRightX + oldY * lUpX + oldZ * lBackX; - dirY = oldX * lRightY + oldY * lUpY + oldZ * lBackY; - dirZ = oldX * lRightZ + oldY * lUpZ + oldZ * lBackZ; - - oldX = posX; - oldY = posY; - oldZ = posZ; - - // ...and to the source position - posX = oldX * lRightX + oldY * lUpX + oldZ * lBackX; - posY = oldX * lRightY + oldY * lUpY + oldZ * lBackY; - posZ = oldX * lRightZ + oldY * lUpZ + oldZ * lBackZ; - - // The change-of-basis corrects the orientation, but the origin is still the listener. - // Translate the source position by the listener position to finish. - posX += lPosX; - posY += lPosY; - posZ += lPosZ; - } - - if (panner.positionX) { - // Assigning to panner.positionX/Y/Z unnecessarily seems to cause performance issues - // See https://github.com/emscripten-core/emscripten/issues/15847 - - if (posX != panner.positionX.value) panner.positionX.value = posX; - if (posY != panner.positionY.value) panner.positionY.value = posY; - if (posZ != panner.positionZ.value) panner.positionZ.value = posZ; - } else { - panner.setPosition(posX, posY, posZ); - } - if (panner.orientationX) { - // Assigning to panner.orientation/Y/Z unnecessarily seems to cause performance issues - // See https://github.com/emscripten-core/emscripten/issues/15847 - - if (dirX != panner.orientationX.value) panner.orientationX.value = dirX; - if (dirY != panner.orientationY.value) panner.orientationY.value = dirY; - if (dirZ != panner.orientationZ.value) panner.orientationZ.value = dirZ; - } else { - panner.setOrientation(dirX, dirY, dirZ); - } - - var oldShift = src.dopplerShift; - var velX = src.velocity[0]; - var velY = src.velocity[1]; - var velZ = src.velocity[2]; - var lVelX = listener.velocity[0]; - var lVelY = listener.velocity[1]; - var lVelZ = listener.velocity[2]; - if (posX === lPosX && posY === lPosY && posZ === lPosZ - || velX === lVelX && velY === lVelY && velZ === lVelZ) - { - src.dopplerShift = 1.0; - } else { - // Doppler algorithm from 1.1 spec - var speedOfSound = src.context.speedOfSound; - var dopplerFactor = src.context.dopplerFactor; - - var slX = lPosX - posX; - var slY = lPosY - posY; - var slZ = lPosZ - posZ; - - var magSl = Math.sqrt(slX * slX + slY * slY + slZ * slZ); - var vls = (slX * lVelX + slY * lVelY + slZ * lVelZ) / magSl; - var vss = (slX * velX + slY * velY + slZ * velZ) / magSl; - - vls = Math.min(vls, speedOfSound / dopplerFactor); - vss = Math.min(vss, speedOfSound / dopplerFactor); - - src.dopplerShift = (speedOfSound - dopplerFactor * vls) / (speedOfSound - dopplerFactor * vss); - } - if (src.dopplerShift !== oldShift) { - AL.updateSourceRate(src); - } - }, - updateSourceRate:(src) => { - if (src.state === 4114) { - // clear scheduled buffers - AL.cancelPendingSourceAudio(src); - - var audioSrc = src.audioQueue[0]; - if (!audioSrc) { - return; // It is possible that AL.scheduleContextAudio() has not yet fed the next buffer, if so, skip. - } - - var duration; - if (src.type === 4136 && src.looping) { - duration = Number.POSITIVE_INFINITY; - } else { - // audioSrc._duration is expressed after factoring in playbackRate, so when changing playback rate, need - // to recompute/rescale the rate to the new playback speed. - duration = (audioSrc.buffer.duration - audioSrc._startOffset) / src.playbackRate; - } - - audioSrc._duration = duration; - audioSrc.playbackRate.value = src.playbackRate; - - // reschedule buffers with the new playbackRate - AL.scheduleSourceAudio(src); - } - }, - sourceDuration:(src) => { - var length = 0.0; - for (var i = 0; i < src.bufQueue.length; i++) { - var audioBuf = src.bufQueue[i].audioBuf; - length += audioBuf ? audioBuf.duration : 0.0; - } - return length; - }, - sourceTell:(src) => { - AL.updateSourceTime(src); - - var offset = 0.0; - for (var i = 0; i < src.bufsProcessed; i++) { - if (src.bufQueue[i].audioBuf) { - offset += src.bufQueue[i].audioBuf.duration; - } - } - offset += src.bufOffset; - - return offset; - }, - sourceSeek:(src, offset) => { - var playing = src.state == 4114; - if (playing) { - AL.setSourceState(src, 4113); - } - - if (src.bufQueue[src.bufsProcessed].audioBuf !== null) { - src.bufsProcessed = 0; - while (offset > src.bufQueue[src.bufsProcessed].audioBuf.duration) { - offset -= src.bufQueue[src.bufsProcessed].audioBuf.duration; - src.bufsProcessed++; - } - - src.bufOffset = offset; - } - - if (playing) { - AL.setSourceState(src, 4114); - } - }, - getGlobalParam:(funcname, param) => { - if (!AL.currentCtx) { - return null; - } - - switch (param) { - case 49152: - return AL.currentCtx.dopplerFactor; - case 49155: - return AL.currentCtx.speedOfSound; - case 53248: - return AL.currentCtx.distanceModel; - default: - AL.currentCtx.err = 40962; - return null; - } - }, - setGlobalParam:(funcname, param, value) => { - if (!AL.currentCtx) { - return; - } - - switch (param) { - case 49152: - if (!Number.isFinite(value) || value < 0.0) { // Strictly negative values are disallowed - AL.currentCtx.err = 40963; - return; - } - - AL.currentCtx.dopplerFactor = value; - AL.updateListenerSpace(AL.currentCtx); - break; - case 49155: - if (!Number.isFinite(value) || value <= 0.0) { // Negative or zero values are disallowed - AL.currentCtx.err = 40963; - return; - } - - AL.currentCtx.speedOfSound = value; - AL.updateListenerSpace(AL.currentCtx); - break; - case 53248: - switch (value) { - case 0: - case 0xd001 /* AL_INVERSE_DISTANCE */: - case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */: - case 0xd003 /* AL_LINEAR_DISTANCE */: - case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */: - case 0xd005 /* AL_EXPONENT_DISTANCE */: - case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */: - AL.currentCtx.distanceModel = value; - AL.updateContextGlobal(AL.currentCtx); - break; - default: - AL.currentCtx.err = 40963; - return; - } - break; - default: - AL.currentCtx.err = 40962; - return; - } - }, - getListenerParam:(funcname, param) => { - if (!AL.currentCtx) { - return null; - } - - switch (param) { - case 4100: - return AL.currentCtx.listener.position; - case 4102: - return AL.currentCtx.listener.velocity; - case 4111: - return AL.currentCtx.listener.direction.concat(AL.currentCtx.listener.up); - case 4106: - return AL.currentCtx.gain.gain.value; - default: - AL.currentCtx.err = 40962; - return null; - } - }, - setListenerParam:(funcname, param, value) => { - if (!AL.currentCtx) { - return; - } - if (value === null) { - AL.currentCtx.err = 40962; - return; - } - - var listener = AL.currentCtx.listener; - switch (param) { - case 4100: - if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { - AL.currentCtx.err = 40963; - return; - } - - listener.position[0] = value[0]; - listener.position[1] = value[1]; - listener.position[2] = value[2]; - AL.updateListenerSpace(AL.currentCtx); - break; - case 4102: - if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { - AL.currentCtx.err = 40963; - return; - } - - listener.velocity[0] = value[0]; - listener.velocity[1] = value[1]; - listener.velocity[2] = value[2]; - AL.updateListenerSpace(AL.currentCtx); - break; - case 4106: - if (!Number.isFinite(value) || value < 0.0) { - AL.currentCtx.err = 40963; - return; - } - - AL.currentCtx.gain.gain.value = value; - break; - case 4111: - if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2]) - || !Number.isFinite(value[3]) || !Number.isFinite(value[4]) || !Number.isFinite(value[5]) - ) { - AL.currentCtx.err = 40963; - return; - } - - listener.direction[0] = value[0]; - listener.direction[1] = value[1]; - listener.direction[2] = value[2]; - listener.up[0] = value[3]; - listener.up[1] = value[4]; - listener.up[2] = value[5]; - AL.updateListenerSpace(AL.currentCtx); - break; - default: - AL.currentCtx.err = 40962; - return; - } - }, - getBufferParam:(funcname, bufferId, param) => { - if (!AL.currentCtx) { - return; - } - var buf = AL.buffers[bufferId]; - if (!buf || bufferId === 0) { - AL.currentCtx.err = 40961; - return; - } - - switch (param) { - case 0x2001 /* AL_FREQUENCY */: - return buf.frequency; - case 0x2002 /* AL_BITS */: - return buf.bytesPerSample * 8; - case 0x2003 /* AL_CHANNELS */: - return buf.channels; - case 0x2004 /* AL_SIZE */: - return buf.length * buf.bytesPerSample * buf.channels; - case 0x2015 /* AL_LOOP_POINTS_SOFT */: - if (buf.length === 0) { - return [0, 0]; - } - return [ - (buf.audioBuf._loopStart || 0.0) * buf.frequency, - (buf.audioBuf._loopEnd || buf.length) * buf.frequency - ]; - default: - AL.currentCtx.err = 40962; - return null; - } - }, - setBufferParam:(funcname, bufferId, param, value) => { - if (!AL.currentCtx) { - return; - } - var buf = AL.buffers[bufferId]; - if (!buf || bufferId === 0) { - AL.currentCtx.err = 40961; - return; - } - if (value === null) { - AL.currentCtx.err = 40962; - return; - } - - switch (param) { - case 0x2004 /* AL_SIZE */: - if (value !== 0) { - AL.currentCtx.err = 40963; - return; - } - - // Per the spec, setting AL_SIZE to 0 is a legal NOP. - break; - case 0x2015 /* AL_LOOP_POINTS_SOFT */: - if (value[0] < 0 || value[0] > buf.length || value[1] < 0 || value[1] > buf.Length || value[0] >= value[1]) { - AL.currentCtx.err = 40963; - return; - } - if (buf.refCount > 0) { - AL.currentCtx.err = 40964; - return; - } - - if (buf.audioBuf) { - buf.audioBuf._loopStart = value[0] / buf.frequency; - buf.audioBuf._loopEnd = value[1] / buf.frequency; - } - break; - default: - AL.currentCtx.err = 40962; - return; - } - }, - getSourceParam:(funcname, sourceId, param) => { - if (!AL.currentCtx) { - return null; - } - var src = AL.currentCtx.sources[sourceId]; - if (!src) { - AL.currentCtx.err = 40961; - return null; - } - - switch (param) { - case 0x202 /* AL_SOURCE_RELATIVE */: - return src.relative; - case 0x1001 /* AL_CONE_INNER_ANGLE */: - return src.coneInnerAngle; - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - return src.coneOuterAngle; - case 0x1003 /* AL_PITCH */: - return src.pitch; - case 4100: - return src.position; - case 4101: - return src.direction; - case 4102: - return src.velocity; - case 0x1007 /* AL_LOOPING */: - return src.looping; - case 0x1009 /* AL_BUFFER */: - if (src.type === 4136) { - return src.bufQueue[0].id; - } - return 0; - case 4106: - return src.gain.gain.value; - case 0x100D /* AL_MIN_GAIN */: - return src.minGain; - case 0x100E /* AL_MAX_GAIN */: - return src.maxGain; - case 0x1010 /* AL_SOURCE_STATE */: - return src.state; - case 0x1015 /* AL_BUFFERS_QUEUED */: - if (src.bufQueue.length === 1 && src.bufQueue[0].id === 0) { - return 0; - } - return src.bufQueue.length; - case 0x1016 /* AL_BUFFERS_PROCESSED */: - if ((src.bufQueue.length === 1 && src.bufQueue[0].id === 0) || src.looping) { - return 0; - } - return src.bufsProcessed; - case 0x1020 /* AL_REFERENCE_DISTANCE */: - return src.refDistance; - case 0x1021 /* AL_ROLLOFF_FACTOR */: - return src.rolloffFactor; - case 0x1022 /* AL_CONE_OUTER_GAIN */: - return src.coneOuterGain; - case 0x1023 /* AL_MAX_DISTANCE */: - return src.maxDistance; - case 0x1024 /* AL_SEC_OFFSET */: - return AL.sourceTell(src); - case 0x1025 /* AL_SAMPLE_OFFSET */: - var offset = AL.sourceTell(src); - if (offset > 0.0) { - offset *= src.bufQueue[0].frequency; - } - return offset; - case 0x1026 /* AL_BYTE_OFFSET */: - var offset = AL.sourceTell(src); - if (offset > 0.0) { - offset *= src.bufQueue[0].frequency * src.bufQueue[0].bytesPerSample; - } - return offset; - case 0x1027 /* AL_SOURCE_TYPE */: - return src.type; - case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: - return src.spatialize; - case 0x2009 /* AL_BYTE_LENGTH_SOFT */: - var length = 0; - var bytesPerFrame = 0; - for (var i = 0; i < src.bufQueue.length; i++) { - length += src.bufQueue[i].length; - if (src.bufQueue[i].id !== 0) { - bytesPerFrame = src.bufQueue[i].bytesPerSample * src.bufQueue[i].channels; - } - } - return length * bytesPerFrame; - case 0x200A /* AL_SAMPLE_LENGTH_SOFT */: - var length = 0; - for (var i = 0; i < src.bufQueue.length; i++) { - length += src.bufQueue[i].length; - } - return length; - case 0x200B /* AL_SEC_LENGTH_SOFT */: - return AL.sourceDuration(src); - case 53248: - return src.distanceModel; - default: - AL.currentCtx.err = 40962; - return null; - } - }, - setSourceParam:(funcname, sourceId, param, value) => { - if (!AL.currentCtx) { - return; - } - var src = AL.currentCtx.sources[sourceId]; - if (!src) { - AL.currentCtx.err = 40961; - return; - } - if (value === null) { - AL.currentCtx.err = 40962; - return; - } - - switch (param) { - case 0x202 /* AL_SOURCE_RELATIVE */: - if (value === 1) { - src.relative = true; - AL.updateSourceSpace(src); - } else if (value === 0) { - src.relative = false; - AL.updateSourceSpace(src); - } else { - AL.currentCtx.err = 40963; - return; - } - break; - case 0x1001 /* AL_CONE_INNER_ANGLE */: - if (!Number.isFinite(value)) { - AL.currentCtx.err = 40963; - return; - } - - src.coneInnerAngle = value; - if (src.panner) { - src.panner.coneInnerAngle = value % 360.0; - } - break; - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - if (!Number.isFinite(value)) { - AL.currentCtx.err = 40963; - return; - } - - src.coneOuterAngle = value; - if (src.panner) { - src.panner.coneOuterAngle = value % 360.0; - } - break; - case 0x1003 /* AL_PITCH */: - if (!Number.isFinite(value) || value <= 0.0) { - AL.currentCtx.err = 40963; - return; - } - - if (src.pitch === value) { - break; - } - - src.pitch = value; - AL.updateSourceRate(src); - break; - case 4100: - if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { - AL.currentCtx.err = 40963; - return; - } - - src.position[0] = value[0]; - src.position[1] = value[1]; - src.position[2] = value[2]; - AL.updateSourceSpace(src); - break; - case 4101: - if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { - AL.currentCtx.err = 40963; - return; - } - - src.direction[0] = value[0]; - src.direction[1] = value[1]; - src.direction[2] = value[2]; - AL.updateSourceSpace(src); - break; - case 4102: - if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) { - AL.currentCtx.err = 40963; - return; - } - - src.velocity[0] = value[0]; - src.velocity[1] = value[1]; - src.velocity[2] = value[2]; - AL.updateSourceSpace(src); - break; - case 0x1007 /* AL_LOOPING */: - if (value === 1) { - src.looping = true; - AL.updateSourceTime(src); - if (src.type === 4136 && src.audioQueue.length > 0) { - var audioSrc = src.audioQueue[0]; - audioSrc.loop = true; - audioSrc._duration = Number.POSITIVE_INFINITY; - } - } else if (value === 0) { - src.looping = false; - var currentTime = AL.updateSourceTime(src); - if (src.type === 4136 && src.audioQueue.length > 0) { - var audioSrc = src.audioQueue[0]; - audioSrc.loop = false; - audioSrc._duration = src.bufQueue[0].audioBuf.duration / src.playbackRate; - audioSrc._startTime = currentTime - src.bufOffset / src.playbackRate; - } - } else { - AL.currentCtx.err = 40963; - return; - } - break; - case 0x1009 /* AL_BUFFER */: - if (src.state === 4114 || src.state === 4115) { - AL.currentCtx.err = 40964; - return; - } - - if (value === 0) { - for (var i in src.bufQueue) { - src.bufQueue[i].refCount--; - } - src.bufQueue.length = 1; - src.bufQueue[0] = AL.buffers[0]; - - src.bufsProcessed = 0; - src.type = 0x1030 /* AL_UNDETERMINED */; - } else { - var buf = AL.buffers[value]; - if (!buf) { - AL.currentCtx.err = 40963; - return; - } - - for (var i in src.bufQueue) { - src.bufQueue[i].refCount--; - } - src.bufQueue.length = 0; - - buf.refCount++; - src.bufQueue = [buf]; - src.bufsProcessed = 0; - src.type = 4136; - } - - AL.initSourcePanner(src); - AL.scheduleSourceAudio(src); - break; - case 4106: - if (!Number.isFinite(value) || value < 0.0) { - AL.currentCtx.err = 40963; - return; - } - src.gain.gain.value = value; - break; - case 0x100D /* AL_MIN_GAIN */: - if (!Number.isFinite(value) || value < 0.0 || value > Math.min(src.maxGain, 1.0)) { - AL.currentCtx.err = 40963; - return; - } - src.minGain = value; - break; - case 0x100E /* AL_MAX_GAIN */: - if (!Number.isFinite(value) || value < Math.max(0.0, src.minGain) || value > 1.0) { - AL.currentCtx.err = 40963; - return; - } - src.maxGain = value; - break; - case 0x1020 /* AL_REFERENCE_DISTANCE */: - if (!Number.isFinite(value) || value < 0.0) { - AL.currentCtx.err = 40963; - return; - } - src.refDistance = value; - if (src.panner) { - src.panner.refDistance = value; - } - break; - case 0x1021 /* AL_ROLLOFF_FACTOR */: - if (!Number.isFinite(value) || value < 0.0) { - AL.currentCtx.err = 40963; - return; - } - src.rolloffFactor = value; - if (src.panner) { - src.panner.rolloffFactor = value; - } - break; - case 0x1022 /* AL_CONE_OUTER_GAIN */: - if (!Number.isFinite(value) || value < 0.0 || value > 1.0) { - AL.currentCtx.err = 40963; - return; - } - src.coneOuterGain = value; - if (src.panner) { - src.panner.coneOuterGain = value; - } - break; - case 0x1023 /* AL_MAX_DISTANCE */: - if (!Number.isFinite(value) || value < 0.0) { - AL.currentCtx.err = 40963; - return; - } - src.maxDistance = value; - if (src.panner) { - src.panner.maxDistance = value; - } - break; - case 0x1024 /* AL_SEC_OFFSET */: - if (value < 0.0 || value > AL.sourceDuration(src)) { - AL.currentCtx.err = 40963; - return; - } - - AL.sourceSeek(src, value); - break; - case 0x1025 /* AL_SAMPLE_OFFSET */: - var srcLen = AL.sourceDuration(src); - if (srcLen > 0.0) { - var frequency; - for (var bufId in src.bufQueue) { - if (bufId) { - frequency = src.bufQueue[bufId].frequency; - break; - } - } - value /= frequency; - } - if (value < 0.0 || value > srcLen) { - AL.currentCtx.err = 40963; - return; - } - - AL.sourceSeek(src, value); - break; - case 0x1026 /* AL_BYTE_OFFSET */: - var srcLen = AL.sourceDuration(src); - if (srcLen > 0.0) { - var bytesPerSec; - for (var bufId in src.bufQueue) { - if (bufId) { - var buf = src.bufQueue[bufId]; - bytesPerSec = buf.frequency * buf.bytesPerSample * buf.channels; - break; - } - } - value /= bytesPerSec; - } - if (value < 0.0 || value > srcLen) { - AL.currentCtx.err = 40963; - return; - } - - AL.sourceSeek(src, value); - break; - case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: - if (value !== 0 && value !== 1 && value !== 2 /* AL_AUTO_SOFT */) { - AL.currentCtx.err = 40963; - return; - } - - src.spatialize = value; - AL.initSourcePanner(src); - break; - case 0x2009 /* AL_BYTE_LENGTH_SOFT */: - case 0x200A /* AL_SAMPLE_LENGTH_SOFT */: - case 0x200B /* AL_SEC_LENGTH_SOFT */: - AL.currentCtx.err = 40964; - break; - case 53248: - switch (value) { - case 0: - case 0xd001 /* AL_INVERSE_DISTANCE */: - case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */: - case 0xd003 /* AL_LINEAR_DISTANCE */: - case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */: - case 0xd005 /* AL_EXPONENT_DISTANCE */: - case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */: - src.distanceModel = value; - if (AL.currentCtx.sourceDistanceModel) { - AL.updateContextGlobal(AL.currentCtx); - } - break; - default: - AL.currentCtx.err = 40963; - return; - } - break; - default: - AL.currentCtx.err = 40962; - return; - } - }, - captures:{ - }, - sharedCaptureAudioCtx:null, - requireValidCaptureDevice:(deviceId, funcname) => { - if (deviceId === 0) { - AL.alcErr = 40961; - return null; - } - var c = AL.captures[deviceId]; - if (!c) { - AL.alcErr = 40961; - return null; - } - var err = c.mediaStreamError; - if (err) { - AL.alcErr = 40961; - return null; - } - return c; - }, - }; - var _alBuffer3f = (bufferId, param, value0, value1, value2) => { - AL.setBufferParam('alBuffer3f', bufferId, param, null); - }; - _alBuffer3f.sig = 'viifff'; - - var _alBuffer3i = (bufferId, param, value0, value1, value2) => { - AL.setBufferParam('alBuffer3i', bufferId, param, null); - }; - _alBuffer3i.sig = 'viiiii'; - - var _alBufferData = (bufferId, format, pData, size, freq) => { - if (!AL.currentCtx) { - return; - } - var buf = AL.buffers[bufferId]; - if (!buf) { - AL.currentCtx.err = 40963; - return; - } - if (freq <= 0) { - AL.currentCtx.err = 40963; - return; - } - - var audioBuf = null; - try { - switch (format) { - case 0x1100 /* AL_FORMAT_MONO8 */: - if (size > 0) { - audioBuf = AL.currentCtx.audioCtx.createBuffer(1, size, freq); - var channel0 = audioBuf.getChannelData(0); - for (var i = 0; i < size; ++i) { - channel0[i] = HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0; - } - } - buf.bytesPerSample = 1; - buf.channels = 1; - buf.length = size; - break; - case 0x1101 /* AL_FORMAT_MONO16 */: - if (size > 0) { - audioBuf = AL.currentCtx.audioCtx.createBuffer(1, size >> 1, freq); - var channel0 = audioBuf.getChannelData(0); - pData >>= 1; - for (var i = 0; i < size >> 1; ++i) { - channel0[i] = HEAP16[pData++] * 0.000030517578125 /* 1/32768 */; - } - } - buf.bytesPerSample = 2; - buf.channels = 1; - buf.length = size >> 1; - break; - case 0x1102 /* AL_FORMAT_STEREO8 */: - if (size > 0) { - audioBuf = AL.currentCtx.audioCtx.createBuffer(2, size >> 1, freq); - var channel0 = audioBuf.getChannelData(0); - var channel1 = audioBuf.getChannelData(1); - for (var i = 0; i < size >> 1; ++i) { - channel0[i] = HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0; - channel1[i] = HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0; - } - } - buf.bytesPerSample = 1; - buf.channels = 2; - buf.length = size >> 1; - break; - case 0x1103 /* AL_FORMAT_STEREO16 */: - if (size > 0) { - audioBuf = AL.currentCtx.audioCtx.createBuffer(2, size >> 2, freq); - var channel0 = audioBuf.getChannelData(0); - var channel1 = audioBuf.getChannelData(1); - pData >>= 1; - for (var i = 0; i < size >> 2; ++i) { - channel0[i] = HEAP16[pData++] * 0.000030517578125 /* 1/32768 */; - channel1[i] = HEAP16[pData++] * 0.000030517578125 /* 1/32768 */; - } - } - buf.bytesPerSample = 2; - buf.channels = 2; - buf.length = size >> 2; - break; - case 0x10010 /* AL_FORMAT_MONO_FLOAT32 */: - if (size > 0) { - audioBuf = AL.currentCtx.audioCtx.createBuffer(1, size >> 2, freq); - var channel0 = audioBuf.getChannelData(0); - pData >>= 2; - for (var i = 0; i < size >> 2; ++i) { - channel0[i] = HEAPF32[pData++]; - } - } - buf.bytesPerSample = 4; - buf.channels = 1; - buf.length = size >> 2; - break; - case 0x10011 /* AL_FORMAT_STEREO_FLOAT32 */: - if (size > 0) { - audioBuf = AL.currentCtx.audioCtx.createBuffer(2, size >> 3, freq); - var channel0 = audioBuf.getChannelData(0); - var channel1 = audioBuf.getChannelData(1); - pData >>= 2; - for (var i = 0; i < size >> 3; ++i) { - channel0[i] = HEAPF32[pData++]; - channel1[i] = HEAPF32[pData++]; - } - } - buf.bytesPerSample = 4; - buf.channels = 2; - buf.length = size >> 3; - break; - default: - AL.currentCtx.err = 40963; - return; - } - buf.frequency = freq; - buf.audioBuf = audioBuf; - } catch (e) { - AL.currentCtx.err = 40963; - return; - } - }; - _alBufferData.sig = 'viipii'; + var registerKeyEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.keyEvent ||= _malloc(160); + + var keyEventHandlerFunc = (e) => { + var keyEventData = JSEvents.keyEvent; + HEAPF64[keyEventData >> 3] = e.timeStamp; + + var idx = keyEventData >> 2; + + HEAP32[idx + 2] = e.location; + HEAP8[keyEventData + 12] = e.ctrlKey; + HEAP8[keyEventData + 13] = e.shiftKey; + HEAP8[keyEventData + 14] = e.altKey; + HEAP8[keyEventData + 15] = e.metaKey; + HEAP8[keyEventData + 16] = e.repeat; + HEAP32[idx + 5] = e.charCode; + HEAP32[idx + 6] = e.keyCode; + HEAP32[idx + 7] = e.which; + stringToUTF8(e.key || '', keyEventData + 32, 32); + stringToUTF8(e.code || '', keyEventData + 64, 32); + stringToUTF8(e.char || '', keyEventData + 96, 32); + stringToUTF8(e.locale || '', keyEventData + 128, 32); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + keyEventData, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: keyEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_keypress_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerKeyEventCallback( + target, + userData, + useCapture, + callbackfunc, + 1, + 'keypress', + targetThread + ); + _emscripten_set_keypress_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_keydown_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerKeyEventCallback( + target, + userData, + useCapture, + callbackfunc, + 2, + 'keydown', + targetThread + ); + _emscripten_set_keydown_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_keyup_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerKeyEventCallback( + target, + userData, + useCapture, + callbackfunc, + 3, + 'keyup', + targetThread + ); + _emscripten_set_keyup_callback_on_thread.sig = 'ippipp'; + + var getBoundingClientRect = (e) => + specialHTMLTargets.indexOf(e) < 0 + ? e.getBoundingClientRect() + : { left: 0, top: 0 }; + + var fillMouseEventData = (eventStruct, e, target) => { + HEAPF64[eventStruct >> 3] = e.timeStamp; + var idx = eventStruct >> 2; + HEAP32[idx + 2] = e.screenX; + HEAP32[idx + 3] = e.screenY; + HEAP32[idx + 4] = e.clientX; + HEAP32[idx + 5] = e.clientY; + HEAP8[eventStruct + 24] = e.ctrlKey; + HEAP8[eventStruct + 25] = e.shiftKey; + HEAP8[eventStruct + 26] = e.altKey; + HEAP8[eventStruct + 27] = e.metaKey; + HEAP16[idx * 2 + 14] = e.button; + HEAP16[idx * 2 + 15] = e.buttons; + + HEAP32[idx + 8] = e['movementX']; + + HEAP32[idx + 9] = e['movementY']; + + // Note: rect contains doubles (truncated to placate SAFE_HEAP, which is the same behaviour when writing to HEAP32 anyway) + var rect = getBoundingClientRect(target); + HEAP32[idx + 10] = e.clientX - (rect.left | 0); + HEAP32[idx + 11] = e.clientY - (rect.top | 0); + }; + + var registerMouseEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.mouseEvent ||= _malloc(64); + target = findEventTarget(target); + + var mouseEventHandlerFunc = (e = event) => { + // TODO: Make this access thread safe, or this could update live while app is reading it. + fillMouseEventData(JSEvents.mouseEvent, e, target); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + JSEvents.mouseEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + allowsDeferredCalls: + eventTypeString != 'mousemove' && + eventTypeString != 'mouseenter' && + eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! + eventTypeString, + callbackfunc, + handlerFunc: mouseEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_click_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 4, + 'click', + targetThread + ); + _emscripten_set_click_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_mousedown_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 5, + 'mousedown', + targetThread + ); + _emscripten_set_mousedown_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_mouseup_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 6, + 'mouseup', + targetThread + ); + _emscripten_set_mouseup_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_dblclick_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 7, + 'dblclick', + targetThread + ); + _emscripten_set_dblclick_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_mousemove_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 8, + 'mousemove', + targetThread + ); + _emscripten_set_mousemove_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_mouseenter_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 33, + 'mouseenter', + targetThread + ); + _emscripten_set_mouseenter_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_mouseleave_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 34, + 'mouseleave', + targetThread + ); + _emscripten_set_mouseleave_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_mouseover_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 35, + 'mouseover', + targetThread + ); + _emscripten_set_mouseover_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_mouseout_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerMouseEventCallback( + target, + userData, + useCapture, + callbackfunc, + 36, + 'mouseout', + targetThread + ); + _emscripten_set_mouseout_callback_on_thread.sig = 'ippipp'; + + var _emscripten_get_mouse_status = (mouseState) => { + if (!JSEvents.mouseEvent) return -7; + // HTML5 does not really have a polling API for mouse events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler to any of the mouse function. + JSEvents.memcpy(mouseState, JSEvents.mouseEvent, 64); + return 0; + }; + _emscripten_get_mouse_status.sig = 'ip'; + + var registerWheelEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.wheelEvent ||= _malloc(96); + + // The DOM Level 3 events spec event 'wheel' + var wheelHandlerFunc = (e = event) => { + var wheelEvent = JSEvents.wheelEvent; + fillMouseEventData(wheelEvent, e, target); + HEAPF64[(wheelEvent + 64) >> 3] = e['deltaX']; + HEAPF64[(wheelEvent + 72) >> 3] = e['deltaY']; + HEAPF64[(wheelEvent + 80) >> 3] = e['deltaZ']; + HEAP32[(wheelEvent + 88) >> 2] = e['deltaMode']; + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + wheelEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + allowsDeferredCalls: true, + eventTypeString, + callbackfunc, + handlerFunc: wheelHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_wheel_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => { + target = findEventTarget(target); + if (!target) return -4; + if (typeof target.onwheel != 'undefined') { + return registerWheelEventCallback( + target, + userData, + useCapture, + callbackfunc, + 9, + 'wheel', + targetThread + ); + } else { + return -1; + } + }; + _emscripten_set_wheel_callback_on_thread.sig = 'ippipp'; + + var registerUiEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.uiEvent ||= _malloc(36); + + target = findEventTarget(target); + + var uiEventHandlerFunc = (e = event) => { + if (e.target != target) { + // Never take ui events such as scroll via a 'bubbled' route, but always from the direct element that + // was targeted. Otherwise e.g. if app logs a message in response to a page scroll, the Emscripten log + // message box could cause to scroll, generating a new (bubbled) scroll message, causing a new log print, + // causing a new scroll, etc.. + return; + } + var b = document.body; // Take document.body to a variable, Closure compiler does not outline access to it on its own. + if (!b) { + // During a page unload 'body' can be null, with "Cannot read property 'clientWidth' of null" being thrown + return; + } + var uiEvent = JSEvents.uiEvent; + HEAP32[uiEvent >> 2] = 0; // always zero for resize and scroll + HEAP32[(uiEvent + 4) >> 2] = b.clientWidth; + HEAP32[(uiEvent + 8) >> 2] = b.clientHeight; + HEAP32[(uiEvent + 12) >> 2] = innerWidth; + HEAP32[(uiEvent + 16) >> 2] = innerHeight; + HEAP32[(uiEvent + 20) >> 2] = outerWidth; + HEAP32[(uiEvent + 24) >> 2] = outerHeight; + HEAP32[(uiEvent + 28) >> 2] = pageXOffset | 0; // scroll offsets are float + HEAP32[(uiEvent + 32) >> 2] = pageYOffset | 0; + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + uiEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: uiEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_resize_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerUiEventCallback( + target, + userData, + useCapture, + callbackfunc, + 10, + 'resize', + targetThread + ); + _emscripten_set_resize_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_scroll_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerUiEventCallback( + target, + userData, + useCapture, + callbackfunc, + 11, + 'scroll', + targetThread + ); + _emscripten_set_scroll_callback_on_thread.sig = 'ippipp'; + + var registerFocusEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.focusEvent ||= _malloc(256); + + var focusEventHandlerFunc = (e = event) => { + var nodeName = JSEvents.getNodeNameForTarget(e.target); + var id = e.target.id ? e.target.id : ''; + + var focusEvent = JSEvents.focusEvent; + stringToUTF8(nodeName, focusEvent + 0, 128); + stringToUTF8(id, focusEvent + 128, 128); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + focusEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: focusEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_blur_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerFocusEventCallback( + target, + userData, + useCapture, + callbackfunc, + 12, + 'blur', + targetThread + ); + _emscripten_set_blur_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_focus_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerFocusEventCallback( + target, + userData, + useCapture, + callbackfunc, + 13, + 'focus', + targetThread + ); + _emscripten_set_focus_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_focusin_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerFocusEventCallback( + target, + userData, + useCapture, + callbackfunc, + 14, + 'focusin', + targetThread + ); + _emscripten_set_focusin_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_focusout_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerFocusEventCallback( + target, + userData, + useCapture, + callbackfunc, + 15, + 'focusout', + targetThread + ); + _emscripten_set_focusout_callback_on_thread.sig = 'ippipp'; + + var fillDeviceOrientationEventData = (eventStruct, e, target) => { + HEAPF64[eventStruct >> 3] = e.alpha; + HEAPF64[(eventStruct + 8) >> 3] = e.beta; + HEAPF64[(eventStruct + 16) >> 3] = e.gamma; + HEAP8[eventStruct + 24] = e.absolute; + }; + + var registerDeviceOrientationEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.deviceOrientationEvent ||= _malloc(32); + + var deviceOrientationEventHandlerFunc = (e = event) => { + fillDeviceOrientationEventData( + JSEvents.deviceOrientationEvent, + e, + target + ); // TODO: Thread-safety with respect to emscripten_get_deviceorientation_status() + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + JSEvents.deviceOrientationEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: deviceOrientationEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_deviceorientation_callback_on_thread = ( + userData, + useCapture, + callbackfunc, + targetThread + ) => { + return registerDeviceOrientationEventCallback( + 2, + userData, + useCapture, + callbackfunc, + 16, + 'deviceorientation', + targetThread + ); + }; + _emscripten_set_deviceorientation_callback_on_thread.sig = 'ipipp'; + + var _emscripten_get_deviceorientation_status = (orientationState) => { + if (!JSEvents.deviceOrientationEvent) return -7; + // HTML5 does not really have a polling API for device orientation events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler. + JSEvents.memcpy(orientationState, JSEvents.deviceOrientationEvent, 32); + return 0; + }; + _emscripten_get_deviceorientation_status.sig = 'ip'; + + var fillDeviceMotionEventData = (eventStruct, e, target) => { + var supportedFields = 0; + var a = e['acceleration']; + supportedFields |= a && 1; + var ag = e['accelerationIncludingGravity']; + supportedFields |= ag && 2; + var rr = e['rotationRate']; + supportedFields |= rr && 4; + a = a || {}; + ag = ag || {}; + rr = rr || {}; + HEAP32[(eventStruct + 72) >> 2] = supportedFields; + HEAPF64[eventStruct >> 3] = a['x']; + HEAPF64[(eventStruct + 8) >> 3] = a['y']; + HEAPF64[(eventStruct + 16) >> 3] = a['z']; + HEAPF64[(eventStruct + 24) >> 3] = ag['x']; + HEAPF64[(eventStruct + 32) >> 3] = ag['y']; + HEAPF64[(eventStruct + 40) >> 3] = ag['z']; + HEAPF64[(eventStruct + 48) >> 3] = rr['alpha']; + HEAPF64[(eventStruct + 56) >> 3] = rr['beta']; + HEAPF64[(eventStruct + 64) >> 3] = rr['gamma']; + }; + + var registerDeviceMotionEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.deviceMotionEvent ||= _malloc(80); + + var deviceMotionEventHandlerFunc = (e = event) => { + fillDeviceMotionEventData(JSEvents.deviceMotionEvent, e, target); // TODO: Thread-safety with respect to emscripten_get_devicemotion_status() + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + JSEvents.deviceMotionEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: deviceMotionEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_devicemotion_callback_on_thread = ( + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerDeviceMotionEventCallback( + 2, + userData, + useCapture, + callbackfunc, + 17, + 'devicemotion', + targetThread + ); + _emscripten_set_devicemotion_callback_on_thread.sig = 'ipipp'; + + var _emscripten_get_devicemotion_status = (motionState) => { + if (!JSEvents.deviceMotionEvent) return -7; + // HTML5 does not really have a polling API for device motion events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler. + JSEvents.memcpy(motionState, JSEvents.deviceMotionEvent, 80); + return 0; + }; + _emscripten_get_devicemotion_status.sig = 'ip'; + + var screenOrientation = () => { + if (!window.screen) return undefined; + return ( + screen.orientation || + screen['mozOrientation'] || + screen['webkitOrientation'] + ); + }; + + var fillOrientationChangeEventData = (eventStruct) => { + // OrientationType enum + var orientationsType1 = [ + 'portrait-primary', + 'portrait-secondary', + 'landscape-primary', + 'landscape-secondary', + ]; + // alternative selection from OrientationLockType enum + var orientationsType2 = [ + 'portrait', + 'portrait', + 'landscape', + 'landscape', + ]; + + var orientationIndex = 0; + var orientationAngle = 0; + var screenOrientObj = screenOrientation(); + if (typeof screenOrientObj === 'object') { + orientationIndex = orientationsType1.indexOf(screenOrientObj.type); + if (orientationIndex < 0) { + orientationIndex = orientationsType2.indexOf( + screenOrientObj.type + ); + } + if (orientationIndex >= 0) { + orientationIndex = 1 << orientationIndex; + } + orientationAngle = screenOrientObj.angle; + } + + HEAP32[eventStruct >> 2] = orientationIndex; + HEAP32[(eventStruct + 4) >> 2] = orientationAngle; + }; + + var registerOrientationChangeEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.orientationChangeEvent ||= _malloc(8); + + var orientationChangeEventHandlerFunc = (e = event) => { + var orientationChangeEvent = JSEvents.orientationChangeEvent; + + fillOrientationChangeEventData(orientationChangeEvent); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + orientationChangeEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: orientationChangeEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_orientationchange_callback_on_thread = ( + userData, + useCapture, + callbackfunc, + targetThread + ) => { + if (!window.screen || !screen.orientation) return -1; + return registerOrientationChangeEventCallback( + screen.orientation, + userData, + useCapture, + callbackfunc, + 18, + 'change', + targetThread + ); + }; + _emscripten_set_orientationchange_callback_on_thread.sig = 'ipipp'; + + var _emscripten_get_orientation_status = (orientationChangeEvent) => { + // screenOrientation() resolving standard, window.orientation being the deprecated mobile-only + if (!screenOrientation() && typeof orientation == 'undefined') + return -1; + fillOrientationChangeEventData(orientationChangeEvent); + return 0; + }; + _emscripten_get_orientation_status.sig = 'ip'; + + var _emscripten_lock_orientation = (allowedOrientations) => { + var orientations = []; + if (allowedOrientations & 1) orientations.push('portrait-primary'); + if (allowedOrientations & 2) orientations.push('portrait-secondary'); + if (allowedOrientations & 4) orientations.push('landscape-primary'); + if (allowedOrientations & 8) orientations.push('landscape-secondary'); + var succeeded; + if (screen.lockOrientation) { + succeeded = screen.lockOrientation(orientations); + } else if (screen.mozLockOrientation) { + succeeded = screen.mozLockOrientation(orientations); + } else if (screen.webkitLockOrientation) { + succeeded = screen.webkitLockOrientation(orientations); + } else { + return -1; + } + if (succeeded) { + return 0; + } + return -6; + }; + _emscripten_lock_orientation.sig = 'ii'; + + var _emscripten_unlock_orientation = () => { + if (screen.unlockOrientation) { + screen.unlockOrientation(); + } else if (screen.mozUnlockOrientation) { + screen.mozUnlockOrientation(); + } else if (screen.webkitUnlockOrientation) { + screen.webkitUnlockOrientation(); + } else { + return -1; + } + return 0; + }; + _emscripten_unlock_orientation.sig = 'i'; + + var fillFullscreenChangeEventData = (eventStruct) => { + var fullscreenElement = getFullscreenElement(); + var isFullscreen = !!fullscreenElement; + // Assigning a boolean to HEAP32 with expected type coercion. + /** @suppress{checkTypes} */ + HEAP8[eventStruct] = isFullscreen; + HEAP8[eventStruct + 1] = JSEvents.fullscreenEnabled(); + // If transitioning to fullscreen, report info about the element that is now fullscreen. + // If transitioning to windowed mode, report info about the element that just was fullscreen. + var reportedElement = isFullscreen + ? fullscreenElement + : JSEvents.previousFullscreenElement; + var nodeName = JSEvents.getNodeNameForTarget(reportedElement); + var id = reportedElement?.id || ''; + stringToUTF8(nodeName, eventStruct + 2, 128); + stringToUTF8(id, eventStruct + 130, 128); + HEAP32[(eventStruct + 260) >> 2] = reportedElement + ? reportedElement.clientWidth + : 0; + HEAP32[(eventStruct + 264) >> 2] = reportedElement + ? reportedElement.clientHeight + : 0; + HEAP32[(eventStruct + 268) >> 2] = screen.width; + HEAP32[(eventStruct + 272) >> 2] = screen.height; + if (isFullscreen) { + JSEvents.previousFullscreenElement = fullscreenElement; + } + }; + + var registerFullscreenChangeEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.fullscreenChangeEvent ||= _malloc(276); + + var fullscreenChangeEventhandlerFunc = (e = event) => { + var fullscreenChangeEvent = JSEvents.fullscreenChangeEvent; + + fillFullscreenChangeEventData(fullscreenChangeEvent); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + fullscreenChangeEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: fullscreenChangeEventhandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_fullscreenchange_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => { + if (!JSEvents.fullscreenEnabled()) return -1; + target = findEventTarget(target); + if (!target) return -4; + + return registerFullscreenChangeEventCallback( + target, + userData, + useCapture, + callbackfunc, + 19, + 'fullscreenchange', + targetThread + ); + }; + _emscripten_set_fullscreenchange_callback_on_thread.sig = 'ippipp'; + + var _emscripten_get_fullscreen_status = (fullscreenStatus) => { + if (!JSEvents.fullscreenEnabled()) return -1; + fillFullscreenChangeEventData(fullscreenStatus); + return 0; + }; + _emscripten_get_fullscreen_status.sig = 'ip'; + + var _emscripten_get_canvas_element_size = (target, width, height) => { + var canvas = findCanvasEventTarget(target); + if (!canvas) return -4; + HEAP32[width >> 2] = canvas.width; + HEAP32[height >> 2] = canvas.height; + }; + _emscripten_get_canvas_element_size.sig = 'ippp'; + + var getCanvasElementSize = (target) => { + var sp = stackSave(); + var w = stackAlloc(8); + var h = w + 4; + + var targetInt = stringToUTF8OnStack(target.id); + var ret = _emscripten_get_canvas_element_size(targetInt, w, h); + var size = [HEAP32[w >> 2], HEAP32[h >> 2]]; + stackRestore(sp); + return size; + }; + + var setCanvasElementSize = (target, width, height) => { + if (!target.controlTransferredOffscreen) { + target.width = width; + target.height = height; + } else { + // This function is being called from high-level JavaScript code instead of asm.js/Wasm, + // and it needs to synchronously proxy over to another thread, so marshal the string onto the heap to do the call. + var sp = stackSave(); + var targetInt = stringToUTF8OnStack(target.id); + _emscripten_set_canvas_element_size(targetInt, width, height); + stackRestore(sp); + } + }; + + var currentFullscreenStrategy = {}; + var registerRestoreOldStyle = (canvas) => { + var canvasSize = getCanvasElementSize(canvas); + var oldWidth = canvasSize[0]; + var oldHeight = canvasSize[1]; + var oldCssWidth = canvas.style.width; + var oldCssHeight = canvas.style.height; + var oldBackgroundColor = canvas.style.backgroundColor; // Chrome reads color from here. + var oldDocumentBackgroundColor = document.body.style.backgroundColor; // IE11 reads color from here. + // Firefox always has black background color. + var oldPaddingLeft = canvas.style.paddingLeft; // Chrome, FF, Safari + var oldPaddingRight = canvas.style.paddingRight; + var oldPaddingTop = canvas.style.paddingTop; + var oldPaddingBottom = canvas.style.paddingBottom; + var oldMarginLeft = canvas.style.marginLeft; // IE11 + var oldMarginRight = canvas.style.marginRight; + var oldMarginTop = canvas.style.marginTop; + var oldMarginBottom = canvas.style.marginBottom; + var oldDocumentBodyMargin = document.body.style.margin; + var oldDocumentOverflow = document.documentElement.style.overflow; // Chrome, Firefox + var oldDocumentScroll = document.body.scroll; // IE + var oldImageRendering = canvas.style.imageRendering; + + function restoreOldStyle() { + if (!getFullscreenElement()) { + document.removeEventListener( + 'fullscreenchange', + restoreOldStyle + ); + + setCanvasElementSize(canvas, oldWidth, oldHeight); + + canvas.style.width = oldCssWidth; + canvas.style.height = oldCssHeight; + canvas.style.backgroundColor = oldBackgroundColor; // Chrome + // IE11 hack: assigning 'undefined' or an empty string to document.body.style.backgroundColor has no effect, so first assign back the default color + // before setting the undefined value. Setting undefined value is also important, or otherwise we would later treat that as something that the user + // had explicitly set so subsequent fullscreen transitions would not set background color properly. + if (!oldDocumentBackgroundColor) + document.body.style.backgroundColor = 'white'; + document.body.style.backgroundColor = + oldDocumentBackgroundColor; // IE11 + canvas.style.paddingLeft = oldPaddingLeft; // Chrome, FF, Safari + canvas.style.paddingRight = oldPaddingRight; + canvas.style.paddingTop = oldPaddingTop; + canvas.style.paddingBottom = oldPaddingBottom; + canvas.style.marginLeft = oldMarginLeft; // IE11 + canvas.style.marginRight = oldMarginRight; + canvas.style.marginTop = oldMarginTop; + canvas.style.marginBottom = oldMarginBottom; + document.body.style.margin = oldDocumentBodyMargin; + document.documentElement.style.overflow = oldDocumentOverflow; // Chrome, Firefox + document.body.scroll = oldDocumentScroll; // IE + canvas.style.imageRendering = oldImageRendering; + if (canvas.GLctxObject) + canvas.GLctxObject.GLctx.viewport( + 0, + 0, + oldWidth, + oldHeight + ); + + if (currentFullscreenStrategy.canvasResizedCallback) { + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + 37, + 0, + currentFullscreenStrategy.canvasResizedCallbackUserData + ); + } + } + } + document.addEventListener('fullscreenchange', restoreOldStyle); + return restoreOldStyle; + }; + + var setLetterbox = (element, topBottom, leftRight) => { + // Cannot use margin to specify letterboxes in FF or Chrome, since those ignore margins in fullscreen mode. + element.style.paddingLeft = element.style.paddingRight = + leftRight + 'px'; + element.style.paddingTop = element.style.paddingBottom = + topBottom + 'px'; + }; + + var JSEvents_resizeCanvasForFullscreen = (target, strategy) => { + var restoreOldStyle = registerRestoreOldStyle(target); + var cssWidth = strategy.softFullscreen ? innerWidth : screen.width; + var cssHeight = strategy.softFullscreen ? innerHeight : screen.height; + var rect = getBoundingClientRect(target); + var windowedCssWidth = rect.width; + var windowedCssHeight = rect.height; + var canvasSize = getCanvasElementSize(target); + var windowedRttWidth = canvasSize[0]; + var windowedRttHeight = canvasSize[1]; + + if (strategy.scaleMode == 3) { + setLetterbox( + target, + (cssHeight - windowedCssHeight) / 2, + (cssWidth - windowedCssWidth) / 2 + ); + cssWidth = windowedCssWidth; + cssHeight = windowedCssHeight; + } else if (strategy.scaleMode == 2) { + if (cssWidth * windowedRttHeight < windowedRttWidth * cssHeight) { + var desiredCssHeight = + (windowedRttHeight * cssWidth) / windowedRttWidth; + setLetterbox(target, (cssHeight - desiredCssHeight) / 2, 0); + cssHeight = desiredCssHeight; + } else { + var desiredCssWidth = + (windowedRttWidth * cssHeight) / windowedRttHeight; + setLetterbox(target, 0, (cssWidth - desiredCssWidth) / 2); + cssWidth = desiredCssWidth; + } + } + + // If we are adding padding, must choose a background color or otherwise Chrome will give the + // padding a default white color. Do it only if user has not customized their own background color. + target.style.backgroundColor ||= 'black'; + // IE11 does the same, but requires the color to be set in the document body. + document.body.style.backgroundColor ||= 'black'; // IE11 + // Firefox always shows black letterboxes independent of style color. + + target.style.width = cssWidth + 'px'; + target.style.height = cssHeight + 'px'; + + if (strategy.filteringMode == 1) { + target.style.imageRendering = 'optimizeSpeed'; + target.style.imageRendering = '-moz-crisp-edges'; + target.style.imageRendering = '-o-crisp-edges'; + target.style.imageRendering = '-webkit-optimize-contrast'; + target.style.imageRendering = 'optimize-contrast'; + target.style.imageRendering = 'crisp-edges'; + target.style.imageRendering = 'pixelated'; + } + + var dpiScale = + strategy.canvasResolutionScaleMode == 2 ? devicePixelRatio : 1; + if (strategy.canvasResolutionScaleMode != 0) { + var newWidth = (cssWidth * dpiScale) | 0; + var newHeight = (cssHeight * dpiScale) | 0; + setCanvasElementSize(target, newWidth, newHeight); + if (target.GLctxObject) + target.GLctxObject.GLctx.viewport(0, 0, newWidth, newHeight); + } + return restoreOldStyle; + }; + var JSEvents_requestFullscreen = (target, strategy) => { + // EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT + EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE is a mode where no extra logic is performed to the DOM elements. + if ( + strategy.scaleMode != 0 || + strategy.canvasResolutionScaleMode != 0 + ) { + JSEvents_resizeCanvasForFullscreen(target, strategy); + } + + if (target.requestFullscreen) { + target.requestFullscreen(); + } else { + return JSEvents.fullscreenEnabled() ? -3 : -1; + } + + currentFullscreenStrategy = strategy; + + if (strategy.canvasResizedCallback) { + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + 37, + 0, + strategy.canvasResizedCallbackUserData + ); + } + + return 0; + }; + + var hideEverythingExceptGivenElement = (onlyVisibleElement) => { + var child = onlyVisibleElement; + var parent = child.parentNode; + var hiddenElements = []; + while (child != document.body) { + var children = parent.children; + for (var currChild of children) { + if (currChild != child) { + hiddenElements.push({ + node: currChild, + displayState: currChild.style.display, + }); + currChild.style.display = 'none'; + } + } + child = parent; + parent = parent.parentNode; + } + return hiddenElements; + }; + + var restoreHiddenElements = (hiddenElements) => { + for (var elem of hiddenElements) { + elem.node.style.display = elem.displayState; + } + }; + + var restoreOldWindowedStyle = null; + + var softFullscreenResizeWebGLRenderTarget = () => { + var dpr = devicePixelRatio; + var inHiDPIFullscreenMode = + currentFullscreenStrategy.canvasResolutionScaleMode == 2; + var inAspectRatioFixedFullscreenMode = + currentFullscreenStrategy.scaleMode == 2; + var inPixelPerfectFullscreenMode = + currentFullscreenStrategy.canvasResolutionScaleMode != 0; + var inCenteredWithoutScalingFullscreenMode = + currentFullscreenStrategy.scaleMode == 3; + var screenWidth = inHiDPIFullscreenMode + ? Math.round(innerWidth * dpr) + : innerWidth; + var screenHeight = inHiDPIFullscreenMode + ? Math.round(innerHeight * dpr) + : innerHeight; + var w = screenWidth; + var h = screenHeight; + var canvas = currentFullscreenStrategy.target; + var canvasSize = getCanvasElementSize(canvas); + var x = canvasSize[0]; + var y = canvasSize[1]; + var topMargin; + + if (inAspectRatioFixedFullscreenMode) { + if (w * y < x * h) h = ((w * y) / x) | 0; + else if (w * y > x * h) w = ((h * x) / y) | 0; + topMargin = ((screenHeight - h) / 2) | 0; + } + + if (inPixelPerfectFullscreenMode) { + setCanvasElementSize(canvas, w, h); + if (canvas.GLctxObject) + canvas.GLctxObject.GLctx.viewport(0, 0, w, h); + } + + // Back to CSS pixels. + if (inHiDPIFullscreenMode) { + topMargin /= dpr; + w /= dpr; + h /= dpr; + // Round to nearest 4 digits of precision. + w = Math.round(w * 1e4) / 1e4; + h = Math.round(h * 1e4) / 1e4; + topMargin = Math.round(topMargin * 1e4) / 1e4; + } + + if (inCenteredWithoutScalingFullscreenMode) { + var t = (innerHeight - jstoi_q(canvas.style.height)) / 2; + var b = (innerWidth - jstoi_q(canvas.style.width)) / 2; + setLetterbox(canvas, t, b); + } else { + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; + var b = (innerWidth - w) / 2; + setLetterbox(canvas, topMargin, b); + } + + if ( + !inCenteredWithoutScalingFullscreenMode && + currentFullscreenStrategy.canvasResizedCallback + ) { + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + 37, + 0, + currentFullscreenStrategy.canvasResizedCallbackUserData + ); + } + }; + + var doRequestFullscreen = (target, strategy) => { + if (!JSEvents.fullscreenEnabled()) return -1; + target = findEventTarget(target); + if (!target) return -4; + + if (!target.requestFullscreen) { + return -3; + } + + // Queue this function call if we're not currently in an event handler and + // the user saw it appropriate to do so. + if (!JSEvents.canPerformEventHandlerRequests()) { + if (strategy.deferUntilInEventHandler) { + JSEvents.deferCall( + JSEvents_requestFullscreen, + 1 /* priority over pointer lock */, + [target, strategy] + ); + return 1; + } + return -2; + } + + return JSEvents_requestFullscreen(target, strategy); + }; + + var _emscripten_request_fullscreen = (target, deferUntilInEventHandler) => { + var strategy = { + // These options perform no added logic, but just bare request fullscreen. + scaleMode: 0, + canvasResolutionScaleMode: 0, + filteringMode: 0, + deferUntilInEventHandler, + canvasResizedCallbackTargetThread: 2, + }; + return doRequestFullscreen(target, strategy); + }; + _emscripten_request_fullscreen.sig = 'ipi'; + + var _emscripten_request_fullscreen_strategy = ( + target, + deferUntilInEventHandler, + fullscreenStrategy + ) => { + var strategy = { + scaleMode: HEAP32[fullscreenStrategy >> 2], + canvasResolutionScaleMode: HEAP32[(fullscreenStrategy + 4) >> 2], + filteringMode: HEAP32[(fullscreenStrategy + 8) >> 2], + deferUntilInEventHandler, + canvasResizedCallback: HEAP32[(fullscreenStrategy + 12) >> 2], + canvasResizedCallbackUserData: + HEAP32[(fullscreenStrategy + 16) >> 2], + }; + + return doRequestFullscreen(target, strategy); + }; + _emscripten_request_fullscreen_strategy.sig = 'ipip'; + + var _emscripten_enter_soft_fullscreen = (target, fullscreenStrategy) => { + target = findEventTarget(target); + if (!target) return -4; + + var strategy = { + scaleMode: HEAP32[fullscreenStrategy >> 2], + canvasResolutionScaleMode: HEAP32[(fullscreenStrategy + 4) >> 2], + filteringMode: HEAP32[(fullscreenStrategy + 8) >> 2], + canvasResizedCallback: HEAP32[(fullscreenStrategy + 12) >> 2], + canvasResizedCallbackUserData: + HEAP32[(fullscreenStrategy + 16) >> 2], + target, + softFullscreen: true, + }; + + var restoreOldStyle = JSEvents_resizeCanvasForFullscreen( + target, + strategy + ); + + document.documentElement.style.overflow = 'hidden'; // Firefox, Chrome + document.body.scroll = 'no'; // IE11 + document.body.style.margin = '0px'; // Override default document margin area on all browsers. + + var hiddenElements = hideEverythingExceptGivenElement(target); + + function restoreWindowedState() { + restoreOldStyle(); + restoreHiddenElements(hiddenElements); + removeEventListener( + 'resize', + softFullscreenResizeWebGLRenderTarget + ); + if (strategy.canvasResizedCallback) { + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + 37, + 0, + strategy.canvasResizedCallbackUserData + ); + } + currentFullscreenStrategy = 0; + } + restoreOldWindowedStyle = restoreWindowedState; + currentFullscreenStrategy = strategy; + addEventListener('resize', softFullscreenResizeWebGLRenderTarget); + + // Inform the caller that the canvas size has changed. + if (strategy.canvasResizedCallback) { + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + 37, + 0, + strategy.canvasResizedCallbackUserData + ); + } + + return 0; + }; + _emscripten_enter_soft_fullscreen.sig = 'ipp'; + + var _emscripten_exit_soft_fullscreen = () => { + restoreOldWindowedStyle?.(); + restoreOldWindowedStyle = null; + + return 0; + }; + _emscripten_exit_soft_fullscreen.sig = 'i'; + + var _emscripten_exit_fullscreen = () => { + if (!JSEvents.fullscreenEnabled()) return -1; + // Make sure no queued up calls will fire after this. + JSEvents.removeDeferredCalls(JSEvents_requestFullscreen); + + var d = specialHTMLTargets[1]; + if (d.exitFullscreen) { + d.fullscreenElement && d.exitFullscreen(); + } else { + return -1; + } + + return 0; + }; + _emscripten_exit_fullscreen.sig = 'i'; + + var fillPointerlockChangeEventData = (eventStruct) => { + var pointerLockElement = document.pointerLockElement; + var isPointerlocked = !!pointerLockElement; + // Assigning a boolean to HEAP32 with expected type coercion. + /** @suppress{checkTypes} */ + HEAP8[eventStruct] = isPointerlocked; + var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement); + var id = pointerLockElement?.id || ''; + stringToUTF8(nodeName, eventStruct + 1, 128); + stringToUTF8(id, eventStruct + 129, 128); + }; + + var registerPointerlockChangeEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.pointerlockChangeEvent ||= _malloc(257); + + var pointerlockChangeEventHandlerFunc = (e = event) => { + var pointerlockChangeEvent = JSEvents.pointerlockChangeEvent; + fillPointerlockChangeEventData(pointerlockChangeEvent); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + pointerlockChangeEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: pointerlockChangeEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_pointerlockchange_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => { + if (!document.body?.requestPointerLock) { + return -1; + } + + target = findEventTarget(target); + if (!target) return -4; + return registerPointerlockChangeEventCallback( + target, + userData, + useCapture, + callbackfunc, + 20, + 'pointerlockchange', + targetThread + ); + }; + _emscripten_set_pointerlockchange_callback_on_thread.sig = 'ippipp'; + + var registerPointerlockErrorEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + var pointerlockErrorEventHandlerFunc = (e = event) => { + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + 0, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: pointerlockErrorEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_pointerlockerror_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => { + if (!document.body?.requestPointerLock) { + return -1; + } + + target = findEventTarget(target); + + if (!target) return -4; + return registerPointerlockErrorEventCallback( + target, + userData, + useCapture, + callbackfunc, + 38, + 'pointerlockerror', + targetThread + ); + }; + _emscripten_set_pointerlockerror_callback_on_thread.sig = 'ippipp'; + + var _emscripten_get_pointerlock_status = (pointerlockStatus) => { + if (pointerlockStatus) + fillPointerlockChangeEventData(pointerlockStatus); + if (!document.body?.requestPointerLock) { + return -1; + } + return 0; + }; + _emscripten_get_pointerlock_status.sig = 'ip'; + + var requestPointerLock = (target) => { + if (target.requestPointerLock) { + target.requestPointerLock(); + } else { + // document.body is known to accept pointer lock, so use that to differentiate if the user passed a bad element, + // or if the whole browser just doesn't support the feature. + if (document.body.requestPointerLock) { + return -3; + } + return -1; + } + return 0; + }; + + var _emscripten_request_pointerlock = ( + target, + deferUntilInEventHandler + ) => { + target = findEventTarget(target); + if (!target) return -4; + if (!target.requestPointerLock) { + return -1; + } + + // Queue this function call if we're not currently in an event handler and + // the user saw it appropriate to do so. + if (!JSEvents.canPerformEventHandlerRequests()) { + if (deferUntilInEventHandler) { + JSEvents.deferCall( + requestPointerLock, + 2 /* priority below fullscreen */, + [target] + ); + return 1; + } + return -2; + } + + return requestPointerLock(target); + }; + _emscripten_request_pointerlock.sig = 'ipi'; + + var _emscripten_exit_pointerlock = () => { + // Make sure no queued up calls will fire after this. + JSEvents.removeDeferredCalls(requestPointerLock); + if (!document.exitPointerLock) return -1; + document.exitPointerLock(); + return 0; + }; + _emscripten_exit_pointerlock.sig = 'i'; + + var _emscripten_vibrate = (msecs) => { + if (!navigator.vibrate) return -1; + navigator.vibrate(msecs); + return 0; + }; + _emscripten_vibrate.sig = 'ii'; + + var _emscripten_vibrate_pattern = (msecsArray, numEntries) => { + if (!navigator.vibrate) return -1; + + var vibrateList = []; + for (var i = 0; i < numEntries; ++i) { + var msecs = HEAP32[(msecsArray + i * 4) >> 2]; + vibrateList.push(msecs); + } + navigator.vibrate(vibrateList); + return 0; + }; + _emscripten_vibrate_pattern.sig = 'ipi'; + + var fillVisibilityChangeEventData = (eventStruct) => { + var visibilityStates = ['hidden', 'visible', 'prerender', 'unloaded']; + var visibilityState = visibilityStates.indexOf( + document.visibilityState + ); + + // Assigning a boolean to HEAP32 with expected type coercion. + /** @suppress{checkTypes} */ + HEAP8[eventStruct] = document.hidden; + HEAP32[(eventStruct + 4) >> 2] = visibilityState; + }; + + var registerVisibilityChangeEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.visibilityChangeEvent ||= _malloc(8); + + var visibilityChangeEventHandlerFunc = (e = event) => { + var visibilityChangeEvent = JSEvents.visibilityChangeEvent; + + fillVisibilityChangeEventData(visibilityChangeEvent); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + visibilityChangeEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + eventTypeString, + callbackfunc, + handlerFunc: visibilityChangeEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_visibilitychange_callback_on_thread = ( + userData, + useCapture, + callbackfunc, + targetThread + ) => { + if (!specialHTMLTargets[1]) { + return -4; + } + return registerVisibilityChangeEventCallback( + specialHTMLTargets[1], + userData, + useCapture, + callbackfunc, + 21, + 'visibilitychange', + targetThread + ); + }; + _emscripten_set_visibilitychange_callback_on_thread.sig = 'ipipp'; + + var _emscripten_get_visibility_status = (visibilityStatus) => { + if ( + typeof document.visibilityState == 'undefined' && + typeof document.hidden == 'undefined' + ) { + return -1; + } + fillVisibilityChangeEventData(visibilityStatus); + return 0; + }; + _emscripten_get_visibility_status.sig = 'ip'; + + var registerTouchEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.touchEvent ||= _malloc(1552); + + target = findEventTarget(target); + + var touchEventHandlerFunc = (e) => { + var t, + touches = {}, + et = e.touches; + // To ease marshalling different kinds of touches that browser reports (all touches are listed in e.touches, + // only changed touches in e.changedTouches, and touches on target at a.targetTouches), mark a boolean in + // each Touch object so that we can later loop only once over all touches we see to marshall over to Wasm. + + for (let t of et) { + // Browser might recycle the generated Touch objects between each frame (Firefox on Android), so reset any + // changed/target states we may have set from previous frame. + t.isChanged = t.onTarget = 0; + touches[t.identifier] = t; + } + // Mark which touches are part of the changedTouches list. + for (let t of e.changedTouches) { + t.isChanged = 1; + touches[t.identifier] = t; + } + // Mark which touches are part of the targetTouches list. + for (let t of e.targetTouches) { + touches[t.identifier].onTarget = 1; + } + + var touchEvent = JSEvents.touchEvent; + HEAPF64[touchEvent >> 3] = e.timeStamp; + HEAP8[touchEvent + 12] = e.ctrlKey; + HEAP8[touchEvent + 13] = e.shiftKey; + HEAP8[touchEvent + 14] = e.altKey; + HEAP8[touchEvent + 15] = e.metaKey; + var idx = touchEvent + 16; + var targetRect = getBoundingClientRect(target); + var numTouches = 0; + for (let t of Object.values(touches)) { + var idx32 = idx >> 2; // Pre-shift the ptr to index to HEAP32 to save code size + HEAP32[idx32 + 0] = t.identifier; + HEAP32[idx32 + 1] = t.screenX; + HEAP32[idx32 + 2] = t.screenY; + HEAP32[idx32 + 3] = t.clientX; + HEAP32[idx32 + 4] = t.clientY; + HEAP32[idx32 + 5] = t.pageX; + HEAP32[idx32 + 6] = t.pageY; + HEAP8[idx + 28] = t.isChanged; + HEAP8[idx + 29] = t.onTarget; + HEAP32[idx32 + 8] = t.clientX - (targetRect.left | 0); + HEAP32[idx32 + 9] = t.clientY - (targetRect.top | 0); + + idx += 48; + + if (++numTouches > 31) { + break; + } + } + HEAP32[(touchEvent + 8) >> 2] = numTouches; + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + touchEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target, + allowsDeferredCalls: + eventTypeString == 'touchstart' || + eventTypeString == 'touchend', + eventTypeString, + callbackfunc, + handlerFunc: touchEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_touchstart_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerTouchEventCallback( + target, + userData, + useCapture, + callbackfunc, + 22, + 'touchstart', + targetThread + ); + _emscripten_set_touchstart_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_touchend_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerTouchEventCallback( + target, + userData, + useCapture, + callbackfunc, + 23, + 'touchend', + targetThread + ); + _emscripten_set_touchend_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_touchmove_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerTouchEventCallback( + target, + userData, + useCapture, + callbackfunc, + 24, + 'touchmove', + targetThread + ); + _emscripten_set_touchmove_callback_on_thread.sig = 'ippipp'; + + var _emscripten_set_touchcancel_callback_on_thread = ( + target, + userData, + useCapture, + callbackfunc, + targetThread + ) => + registerTouchEventCallback( + target, + userData, + useCapture, + callbackfunc, + 25, + 'touchcancel', + targetThread + ); + _emscripten_set_touchcancel_callback_on_thread.sig = 'ippipp'; + + var fillGamepadEventData = (eventStruct, e) => { + HEAPF64[eventStruct >> 3] = e.timestamp; + for (var i = 0; i < e.axes.length; ++i) { + HEAPF64[(eventStruct + i * 8 + 16) >> 3] = e.axes[i]; + } + for (var i = 0; i < e.buttons.length; ++i) { + if (typeof e.buttons[i] == 'object') { + HEAPF64[(eventStruct + i * 8 + 528) >> 3] = e.buttons[i].value; + } else { + HEAPF64[(eventStruct + i * 8 + 528) >> 3] = e.buttons[i]; + } + } + for (var i = 0; i < e.buttons.length; ++i) { + if (typeof e.buttons[i] == 'object') { + HEAP8[eventStruct + i + 1040] = e.buttons[i].pressed; + } else { + // Assigning a boolean to HEAP32, that's ok, but Closure would like to warn about it: + /** @suppress {checkTypes} */ + HEAP8[eventStruct + i + 1040] = e.buttons[i] == 1; + } + } + HEAP8[eventStruct + 1104] = e.connected; + HEAP32[(eventStruct + 1108) >> 2] = e.index; + HEAP32[(eventStruct + 8) >> 2] = e.axes.length; + HEAP32[(eventStruct + 12) >> 2] = e.buttons.length; + stringToUTF8(e.id, eventStruct + 1112, 64); + stringToUTF8(e.mapping, eventStruct + 1176, 64); + }; + + var registerGamepadEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.gamepadEvent ||= _malloc(1240); + + var gamepadEventHandlerFunc = (e = event) => { + var gamepadEvent = JSEvents.gamepadEvent; + fillGamepadEventData(gamepadEvent, e['gamepad']); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + gamepadEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + allowsDeferredCalls: true, + eventTypeString, + callbackfunc, + handlerFunc: gamepadEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + /** @suppress {checkTypes} */ + var _emscripten_sample_gamepad_data = () => { + try { + if (navigator.getGamepads) + return (JSEvents.lastGamepadState = navigator.getGamepads()) + ? 0 + : -1; + } catch (e) { + navigator.getGamepads = null; // Disable getGamepads() so that it won't be attempted to be used again. + } + return -1; + }; + _emscripten_sample_gamepad_data.sig = 'i'; + var _emscripten_set_gamepadconnected_callback_on_thread = ( + userData, + useCapture, + callbackfunc, + targetThread + ) => { + if (_emscripten_sample_gamepad_data()) return -1; + return registerGamepadEventCallback( + 2, + userData, + useCapture, + callbackfunc, + 26, + 'gamepadconnected', + targetThread + ); + }; + _emscripten_set_gamepadconnected_callback_on_thread.sig = 'ipipp'; + + var _emscripten_set_gamepaddisconnected_callback_on_thread = ( + userData, + useCapture, + callbackfunc, + targetThread + ) => { + if (_emscripten_sample_gamepad_data()) return -1; + return registerGamepadEventCallback( + 2, + userData, + useCapture, + callbackfunc, + 27, + 'gamepaddisconnected', + targetThread + ); + }; + _emscripten_set_gamepaddisconnected_callback_on_thread.sig = 'ipipp'; + + var _emscripten_get_num_gamepads = () => { + // N.B. Do not call emscripten_get_num_gamepads() unless having first called emscripten_sample_gamepad_data(), and that has returned EMSCRIPTEN_RESULT_SUCCESS. + // Otherwise the following line will throw an exception. + return JSEvents.lastGamepadState.length; + }; + _emscripten_get_num_gamepads.sig = 'i'; + + var _emscripten_get_gamepad_status = (index, gamepadState) => { + // INVALID_PARAM is returned on a Gamepad index that never was there. + if (index < 0 || index >= JSEvents.lastGamepadState.length) return -5; + + // NO_DATA is returned on a Gamepad index that was removed. + // For previously disconnected gamepads there should be an empty slot (null/undefined/false) at the index. + // This is because gamepads must keep their original position in the array. + // For example, removing the first of two gamepads produces [null/undefined/false, gamepad]. + if (!JSEvents.lastGamepadState[index]) return -7; + + fillGamepadEventData(gamepadState, JSEvents.lastGamepadState[index]); + return 0; + }; + _emscripten_get_gamepad_status.sig = 'iip'; + + var registerBeforeUnloadEventCallback = ( + target, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString + ) => { + var beforeUnloadEventHandlerFunc = (e = event) => { + // Note: This is always called on the main browser thread, since it needs synchronously return a value! + var confirmationMessage = (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + 0, + userData + ); + + if (confirmationMessage) { + confirmationMessage = UTF8ToString(confirmationMessage); + } + if (confirmationMessage) { + e.preventDefault(); + e.returnValue = confirmationMessage; + return confirmationMessage; + } + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + callbackfunc, + handlerFunc: beforeUnloadEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_beforeunload_callback_on_thread = ( + userData, + callbackfunc, + targetThread + ) => { + if (typeof onbeforeunload == 'undefined') return -1; + // beforeunload callback can only be registered on the main browser thread, because the page will go away immediately after returning from the handler, + // and there is no time to start proxying it anywhere. + if (targetThread !== 1) return -5; + return registerBeforeUnloadEventCallback( + 2, + userData, + true, + callbackfunc, + 28, + 'beforeunload' + ); + }; + _emscripten_set_beforeunload_callback_on_thread.sig = 'ippp'; + + var fillBatteryEventData = (eventStruct, battery) => { + HEAPF64[eventStruct >> 3] = battery.chargingTime; + HEAPF64[(eventStruct + 8) >> 3] = battery.dischargingTime; + HEAPF64[(eventStruct + 16) >> 3] = battery.level; + HEAP8[eventStruct + 24] = battery.charging; + }; + + var hasBatteryAPI = () => + typeof navigator != 'undefined' && navigator.getBattery; + + var registerBatteryEventCallback = ( + battery, + userData, + useCapture, + callbackfunc, + eventTypeId, + eventTypeString, + targetThread + ) => { + JSEvents.batteryEvent ||= _malloc(32); + + var batteryEventHandlerFunc = (e = event) => { + var batteryEvent = JSEvents.batteryEvent; + fillBatteryEventData(batteryEvent, battery); + + if ( + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + eventTypeId, + batteryEvent, + userData + ) + ) + e.preventDefault(); + }; + + var eventHandler = { + target: battery, + eventTypeString, + callbackfunc, + handlerFunc: batteryEventHandlerFunc, + useCapture, + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + + var _emscripten_set_batterychargingchange_callback_on_thread = ( + userData, + callbackfunc, + targetThread + ) => { + if (!hasBatteryAPI()) return -1; + navigator.getBattery().then((b) => { + registerBatteryEventCallback( + b, + userData, + true, + callbackfunc, + 29, + 'chargingchange', + targetThread + ); + }); + }; + _emscripten_set_batterychargingchange_callback_on_thread.sig = 'ippp'; + + var _emscripten_set_batterylevelchange_callback_on_thread = ( + userData, + callbackfunc, + targetThread + ) => { + if (!hasBatteryAPI()) return -1; + navigator.getBattery().then((b) => { + registerBatteryEventCallback( + b, + userData, + true, + callbackfunc, + 30, + 'levelchange', + targetThread + ); + }); + }; + _emscripten_set_batterylevelchange_callback_on_thread.sig = 'ippp'; + + var batteryManager; + + var _emscripten_get_battery_status = (batteryState) => { + if (!hasBatteryAPI()) return -1; + if (!batteryManager) { + navigator.getBattery().then((b) => { + batteryManager = b; + }); + return -7; + } + fillBatteryEventData(batteryState, batteryManager); + return 0; + }; + _emscripten_get_battery_status.sig = 'ip'; + + var _emscripten_set_element_css_size = (target, width, height) => { + target = findEventTarget(target); + if (!target) return -4; + + target.style.width = width + 'px'; + target.style.height = height + 'px'; + + return 0; + }; + _emscripten_set_element_css_size.sig = 'ipdd'; + + var _emscripten_get_element_css_size = (target, width, height) => { + target = findEventTarget(target); + if (!target) return -4; + + var rect = getBoundingClientRect(target); + HEAPF64[width >> 3] = rect.width; + HEAPF64[height >> 3] = rect.height; + + return 0; + }; + _emscripten_get_element_css_size.sig = 'ippp'; + + var _emscripten_html5_remove_all_event_listeners = () => + JSEvents.removeAllEventListeners(); + _emscripten_html5_remove_all_event_listeners.sig = 'v'; + + var _emscripten_request_animation_frame = (cb, userData) => + requestAnimationFrame((timeStamp) => + (( + a1, + a2 + ) => {}) /* a dynamic function call to signature idi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + timeStamp, + userData + ) + ); + _emscripten_request_animation_frame.sig = 'ipp'; + + var _emscripten_cancel_animation_frame = (id) => cancelAnimationFrame(id); + _emscripten_cancel_animation_frame.sig = 'vi'; + + var _emscripten_request_animation_frame_loop = (cb, userData) => { + function tick(timeStamp) { + if ( + (( + a1, + a2 + ) => {}) /* a dynamic function call to signature idi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + timeStamp, + userData + ) + ) { + requestAnimationFrame(tick); + } + } + return requestAnimationFrame(tick); + }; + _emscripten_request_animation_frame_loop.sig = 'vpp'; + + var _emscripten_get_device_pixel_ratio = () => { + return (typeof devicePixelRatio == 'number' && devicePixelRatio) || 1.0; + }; + _emscripten_get_device_pixel_ratio.sig = 'd'; + + var _emscripten_get_callstack = (flags, str, maxbytes) => { + var callstack = getCallstack(flags); + // User can query the required amount of bytes to hold the callstack. + if (!str || maxbytes <= 0) { + return lengthBytesUTF8(callstack) + 1; + } + // Output callstack string as C string to HEAP. + var bytesWrittenExcludingNull = stringToUTF8(callstack, str, maxbytes); + + // Return number of bytes written, including null. + return bytesWrittenExcludingNull + 1; + }; + _emscripten_get_callstack.sig = 'iipi'; + + /** @returns {number} */ + var convertFrameToPC = (frame) => { + var match; + + if ((match = /\bwasm-function\[\d+\]:(0x[0-9a-f]+)/.exec(frame))) { + // Wasm engines give the binary offset directly, so we use that as return address + return +match[1]; + } else if ((match = /:(\d+):\d+(?:\)|$)/.exec(frame))) { + // If we are in js, we can use the js line number as the "return address". + // This should work for wasm2js. We tag the high bit to distinguish this + // from wasm addresses. + return 0x80000000 | +match[1]; + } + // return 0 if we can't find any + return 0; + }; + + var _emscripten_return_address = (level) => { + var callstack = jsStackTrace().split('\n'); + if (callstack[0] == 'Error') { + callstack.shift(); + } + // skip this function and the caller to get caller's return address + var caller = callstack[level + 3]; + return convertFrameToPC(caller); + }; + _emscripten_return_address.sig = 'pi'; + + var UNWIND_CACHE = {}; + + var saveInUnwindCache = (callstack) => { + for (var line of callstack) { + var pc = convertFrameToPC(line); + if (pc) { + UNWIND_CACHE[pc] = line; + } + } + }; + + var _emscripten_stack_snapshot = () => { + var callstack = jsStackTrace().split('\n'); + if (callstack[0] == 'Error') { + callstack.shift(); + } + saveInUnwindCache(callstack); + + // Caches the stack snapshot so that emscripten_stack_unwind_buffer() can + // unwind from this spot. + UNWIND_CACHE.last_addr = convertFrameToPC(callstack[3]); + UNWIND_CACHE.last_stack = callstack; + return UNWIND_CACHE.last_addr; + }; + _emscripten_stack_snapshot.sig = 'p'; + + var _emscripten_stack_unwind_buffer = (addr, buffer, count) => { + var stack; + if (UNWIND_CACHE.last_addr == addr) { + stack = UNWIND_CACHE.last_stack; + } else { + stack = jsStackTrace().split('\n'); + if (stack[0] == 'Error') { + stack.shift(); + } + saveInUnwindCache(stack); + } + + var offset = 3; + while (stack[offset] && convertFrameToPC(stack[offset]) != addr) { + ++offset; + } + + for (var i = 0; i < count && stack[i + offset]; ++i) { + HEAP32[(buffer + i * 4) >> 2] = convertFrameToPC(stack[i + offset]); + } + return i; + }; + _emscripten_stack_unwind_buffer.sig = 'ippi'; + + var _emscripten_pc_get_function = (pc) => { + var frame = UNWIND_CACHE[pc]; + if (!frame) return 0; + + var name; + var match; + // First try to match foo.wasm.sym files explcitly. e.g. + // + // at test_return_address.wasm.main (wasm://wasm/test_return_address.wasm-0012cc2a:wasm-function[26]:0x9f3 + // + // Then match JS symbols which don't include that module name: + // + // at invokeEntryPoint (.../test_return_address.js:1500:42) + // + // Finally match firefox format: + // + // Object._main@http://server.com:4324:12' + if ((match = /^\s+at .*\.wasm\.(.*) \(.*\)$/.exec(frame))) { + name = match[1]; + } else if ((match = /^\s+at (.*) \(.*\)$/.exec(frame))) { + name = match[1]; + } else if ((match = /^(.+?)@/.exec(frame))) { + name = match[1]; + } else { + return 0; + } + + _free(_emscripten_pc_get_function.ret ?? 0); + _emscripten_pc_get_function.ret = stringToNewUTF8(name); + return _emscripten_pc_get_function.ret; + }; + _emscripten_pc_get_function.sig = 'pp'; + + var convertPCtoSourceLocation = (pc) => { + if (UNWIND_CACHE.last_get_source_pc == pc) + return UNWIND_CACHE.last_source; + + var match; + var source; + + if (!source) { + var frame = UNWIND_CACHE[pc]; + if (!frame) return null; + // Example: at callMain (a.out.js:6335:22) + if ((match = /\((.*):(\d+):(\d+)\)$/.exec(frame))) { + source = { file: match[1], line: match[2], column: match[3] }; + // Example: main@a.out.js:1337:42 + } else if ((match = /@(.*):(\d+):(\d+)/.exec(frame))) { + source = { file: match[1], line: match[2], column: match[3] }; + } + } + UNWIND_CACHE.last_get_source_pc = pc; + UNWIND_CACHE.last_source = source; + return source; + }; + + var _emscripten_pc_get_file = (pc) => { + var result = convertPCtoSourceLocation(pc); + if (!result) return 0; + + _free(_emscripten_pc_get_file.ret ?? 0); + _emscripten_pc_get_file.ret = stringToNewUTF8(result.file); + return _emscripten_pc_get_file.ret; + }; + _emscripten_pc_get_file.sig = 'pp'; + + var _emscripten_pc_get_line = (pc) => { + var result = convertPCtoSourceLocation(pc); + return result ? result.line : 0; + }; + _emscripten_pc_get_line.sig = 'ip'; + + var _emscripten_pc_get_column = (pc) => { + var result = convertPCtoSourceLocation(pc); + return result ? result.column || 0 : 0; + }; + _emscripten_pc_get_column.sig = 'ip'; + + var _sched_yield = () => 0; + _sched_yield.sig = 'i'; + + var wasiRightsToMuslOFlags = (rights) => { + if (rights & 2 && rights & 64) { + return 2; + } + if (rights & 2) { + return 0; + } + if (rights & 64) { + return 1; + } + throw new FS.ErrnoError(28); + }; + + var wasiOFlagsToMuslOFlags = (oflags) => { + var musl_oflags = 0; + if (oflags & 1) { + musl_oflags |= 64; + } + if (oflags & 8) { + musl_oflags |= 512; + } + if (oflags & 2) { + musl_oflags |= 65536; + } + if (oflags & 4) { + musl_oflags |= 128; + } + return musl_oflags; + }; + + var _emscripten_unwind_to_js_event_loop = () => { + throw 'unwind'; + }; + _emscripten_unwind_to_js_event_loop.sig = 'v'; + + var setImmediateWrapped = (func) => { + setImmediateWrapped.mapping ||= []; + var id = setImmediateWrapped.mapping.length; + setImmediateWrapped.mapping[id] = setImmediate(() => { + setImmediateWrapped.mapping[id] = undefined; + func(); + }); + return id; + }; + + var safeRequestAnimationFrame = (func) => { + runtimeKeepalivePush(); + return MainLoop.requestAnimationFrame(() => { + runtimeKeepalivePop(); + callUserCallback(func); + }); + }; + + var clearImmediateWrapped = (id) => { + clearImmediate(setImmediateWrapped.mapping[id]); + setImmediateWrapped.mapping[id] = undefined; + }; + + var emClearImmediate; + var emSetImmediate; + + var emClearImmediate_deps = ['$emSetImmediate']; + + var _emscripten_set_immediate = (cb, userData) => { + runtimeKeepalivePush(); + return emSetImmediate(() => { + runtimeKeepalivePop(); + callUserCallback(() => + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + userData + ) + ); + }); + }; + _emscripten_set_immediate.sig = 'ipp'; + + var _emscripten_clear_immediate = (id) => { + runtimeKeepalivePop(); + emClearImmediate(id); + }; + _emscripten_clear_immediate.sig = 'vi'; + + var _emscripten_set_immediate_loop = (cb, userData) => { + function tick() { + callUserCallback(() => { + if ( + (( + a1 + ) => {}) /* a dynamic function call to signature ii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + userData + ) + ) { + emSetImmediate(tick); + } else { + runtimeKeepalivePop(); + } + }); + } + runtimeKeepalivePush(); + emSetImmediate(tick); + }; + _emscripten_set_immediate_loop.sig = 'vpp'; + + var _emscripten_set_timeout = (cb, msecs, userData) => + safeSetTimeout( + () => + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + userData + ), + msecs + ); + _emscripten_set_timeout.sig = 'ipdp'; + + var _emscripten_clear_timeout = clearTimeout; + _emscripten_clear_timeout.sig = 'vi'; + + var _emscripten_set_timeout_loop = (cb, msecs, userData) => { + function tick() { + var t = _emscripten_get_now(); + var n = t + msecs; + runtimeKeepalivePop(); + callUserCallback(() => { + if ( + (( + a1, + a2 + ) => {}) /* a dynamic function call to signature idi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + t, + userData + ) + ) { + runtimeKeepalivePush(); + // Save a little bit of code space: modern browsers should treat + // negative setTimeout as timeout of 0 + // (https://stackoverflow.com/questions/8430966/is-calling-settimeout-with-a-negative-delay-ok) + var remaining = n - _emscripten_get_now(); + // Recent revsions of node, however, give TimeoutNegativeWarning + remaining = Math.max(0, remaining); + setTimeout(tick, remaining); + } + }); + } + runtimeKeepalivePush(); + return setTimeout(tick, 0); + }; + _emscripten_set_timeout_loop.sig = 'vpdp'; + + var _emscripten_set_interval = (cb, msecs, userData) => { + runtimeKeepalivePush(); + return setInterval(() => { + callUserCallback(() => + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + userData + ) + ); + }, msecs); + }; + _emscripten_set_interval.sig = 'ipdp'; + + var _emscripten_clear_interval = (id) => { + runtimeKeepalivePop(); + clearInterval(id); + }; + _emscripten_clear_interval.sig = 'vi'; + + var _emscripten_async_call = (func, arg, millis) => { + var wrapper = () => + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + arg + ); + + if ( + millis >= 0 || + // node does not support requestAnimationFrame + ENVIRONMENT_IS_NODE + ) { + safeSetTimeout(wrapper, millis); + } else { + safeRequestAnimationFrame(wrapper); + } + }; + _emscripten_async_call.sig = 'vppi'; + + var registerPostMainLoop = (f) => { + // Does nothing unless $MainLoop is included/used. + typeof MainLoop != 'undefined' && MainLoop.postMainLoop.push(f); + }; + + var registerPreMainLoop = (f) => { + // Does nothing unless $MainLoop is included/used. + typeof MainLoop != 'undefined' && MainLoop.preMainLoop.push(f); + }; + + var _emscripten_get_main_loop_timing = (mode, value) => { + if (mode) HEAP32[mode >> 2] = MainLoop.timingMode; + if (value) HEAP32[value >> 2] = MainLoop.timingValue; + }; + _emscripten_get_main_loop_timing.sig = 'vpp'; + + var _emscripten_set_main_loop = (func, fps, simulateInfiniteLoop) => { + var iterFunc = + () => {} /* a dynamic function call to signature v, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */; + setMainLoop(iterFunc, fps, simulateInfiniteLoop); + }; + _emscripten_set_main_loop.sig = 'vpii'; + + var _emscripten_set_main_loop_arg = ( + func, + arg, + fps, + simulateInfiniteLoop + ) => { + var iterFunc = () => + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + arg + ); + setMainLoop(iterFunc, fps, simulateInfiniteLoop, arg); + }; + _emscripten_set_main_loop_arg.sig = 'vppii'; + + var _emscripten_cancel_main_loop = () => { + MainLoop.pause(); + MainLoop.func = null; + }; + _emscripten_cancel_main_loop.sig = 'v'; + + var _emscripten_pause_main_loop = () => MainLoop.pause(); + _emscripten_pause_main_loop.sig = 'v'; + + var _emscripten_resume_main_loop = () => MainLoop.resume(); + _emscripten_resume_main_loop.sig = 'v'; + + var __emscripten_push_main_loop_blocker = (func, arg, name) => { + MainLoop.queue.push({ + func: () => { + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + arg + ); + }, + name: UTF8ToString(name), + counted: true, + }); + MainLoop.updateStatus(); + }; + __emscripten_push_main_loop_blocker.sig = 'vppp'; + + var __emscripten_push_uncounted_main_loop_blocker = (func, arg, name) => { + MainLoop.queue.push({ + func: () => { + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + arg + ); + }, + name: UTF8ToString(name), + counted: false, + }); + MainLoop.updateStatus(); + }; + __emscripten_push_uncounted_main_loop_blocker.sig = 'vppp'; + + var _emscripten_set_main_loop_expected_blockers = (num) => { + MainLoop.expectedBlockers = num; + MainLoop.remainingBlockers = num; + MainLoop.updateStatus(); + }; + _emscripten_set_main_loop_expected_blockers.sig = 'vi'; + + var idsToPromises = (idBuf, size) => { + var promises = []; + for (var i = 0; i < size; i++) { + var id = HEAP32[(idBuf + i * 4) >> 2]; + promises[i] = getPromise(id); + } + return promises; + }; + + var makePromiseCallback = (callback, userData) => { + return (value) => { + runtimeKeepalivePop(); + var stack = stackSave(); + // Allocate space for the result value and initialize it to NULL. + var resultPtr = stackAlloc(POINTER_SIZE); + HEAPU32[resultPtr >> 2] = 0; + try { + var result = (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + resultPtr, + userData, + value + ); + var resultVal = HEAPU32[resultPtr >> 2]; + } catch (e) { + // If the thrown value is potentially a valid pointer, use it as the + // rejection reason. Otherwise use a null pointer as the reason. If we + // allow arbitrary objects to be thrown here, we will get a TypeError in + // MEMORY64 mode when they are later converted to void* rejection + // values. + if (typeof e != 'number') { + throw 0; + } + throw e; + } finally { + // Thrown errors will reject the promise, but at least we will restore + // the stack first. + stackRestore(stack); + } + switch (result) { + case 0: + return resultVal; + case 1: + return getPromise(resultVal); + case 2: + var ret = getPromise(resultVal); + _emscripten_promise_destroy(resultVal); + return ret; + case 3: + throw resultVal; + } + }; + }; + + var _emscripten_promise_then = (id, onFulfilled, onRejected, userData) => { + runtimeKeepalivePush(); + var promise = getPromise(id); + var newId = promiseMap.allocate({ + promise: promise.then( + makePromiseCallback(onFulfilled, userData), + makePromiseCallback(onRejected, userData) + ), + }); + return newId; + }; + _emscripten_promise_then.sig = 'ppppp'; + + var _emscripten_promise_all = (idBuf, resultBuf, size) => { + var promises = idsToPromises(idBuf, size); + var id = promiseMap.allocate({ + promise: Promise.all(promises).then((results) => { + if (resultBuf) { + for (var i = 0; i < size; i++) { + var result = results[i]; + HEAPU32[(resultBuf + i * 4) >> 2] = result; + } + } + return resultBuf; + }), + }); + return id; + }; + _emscripten_promise_all.sig = 'pppp'; + + var setPromiseResult = (ptr, fulfill, value) => { + var result = fulfill ? 0 : 3; + HEAP32[ptr >> 2] = result; + HEAPU32[(ptr + 4) >> 2] = value; + }; + + var _emscripten_promise_all_settled = (idBuf, resultBuf, size) => { + var promises = idsToPromises(idBuf, size); + var id = promiseMap.allocate({ + promise: Promise.allSettled(promises).then((results) => { + if (resultBuf) { + var offset = resultBuf; + for (var i = 0; i < size; i++, offset += 8) { + if (results[i].status === 'fulfilled') { + setPromiseResult(offset, true, results[i].value); + } else { + setPromiseResult(offset, false, results[i].reason); + } + } + } + return resultBuf; + }), + }); + return id; + }; + _emscripten_promise_all_settled.sig = 'pppp'; + + var _emscripten_promise_any = (idBuf, errorBuf, size) => { + var promises = idsToPromises(idBuf, size); + var id = promiseMap.allocate({ + promise: Promise.any(promises).catch((err) => { + if (errorBuf) { + for (var i = 0; i < size; i++) { + HEAPU32[(errorBuf + i * 4) >> 2] = err.errors[i]; + } + } + throw errorBuf; + }), + }); + return id; + }; + _emscripten_promise_any.sig = 'pppp'; + + var _emscripten_promise_race = (idBuf, size) => { + var promises = idsToPromises(idBuf, size); + var id = promiseMap.allocate({ + promise: Promise.race(promises), + }); + return id; + }; + _emscripten_promise_race.sig = 'ppp'; + + var _emscripten_promise_await = (returnValuePtr, id) => { + return Asyncify.handleAsync(() => + getPromise(id).then( + (value) => setPromiseResult(returnValuePtr, true, value), + (error) => setPromiseResult(returnValuePtr, false, error) + ) + ); + }; + _emscripten_promise_await.sig = 'vpp'; + _emscripten_promise_await.isAsync = true; + + var ___cxa_find_matching_catch_3 = (arg0) => findMatchingCatch([arg0]); + ___cxa_find_matching_catch_3.sig = 'pp'; + + var ___cxa_find_matching_catch_4 = (arg0, arg1) => + findMatchingCatch([arg0, arg1]); + ___cxa_find_matching_catch_4.sig = 'ppp'; + + var exceptionCaught = []; + + var ___cxa_rethrow = () => { + var info = exceptionCaught.pop(); + if (!info) { + abort('no exception to throw'); + } + var ptr = info.excPtr; + if (!info.get_rethrown()) { + // Only pop if the corresponding push was through rethrow_primary_exception + exceptionCaught.push(info); + info.set_rethrown(true); + info.set_caught(false); + uncaughtExceptionCount++; + } + exceptionLast = ptr; + throw exceptionLast; + }; + ___cxa_rethrow.sig = 'v'; + + var _llvm_eh_typeid_for = (type) => type; + _llvm_eh_typeid_for.sig = 'vp'; + + var ___cxa_begin_catch = (ptr) => { + var info = new ExceptionInfo(ptr); + if (!info.get_caught()) { + info.set_caught(true); + uncaughtExceptionCount--; + } + info.set_rethrown(false); + exceptionCaught.push(info); + ___cxa_increment_exception_refcount(ptr); + return ___cxa_get_exception_ptr(ptr); + }; + ___cxa_begin_catch.sig = 'pp'; + + var ___cxa_end_catch = () => { + // Clear state flag. + _setThrew(0, 0); + // Call destructor if one is registered then clear it. + var info = exceptionCaught.pop(); + + ___cxa_decrement_exception_refcount(info.excPtr); + exceptionLast = 0; // XXX in decRef? + }; + ___cxa_end_catch.sig = 'v'; + + var ___cxa_uncaught_exceptions = () => uncaughtExceptionCount; + ___cxa_uncaught_exceptions.sig = 'i'; + + var ___cxa_call_unexpected = (exception) => + abort( + 'Unexpected exception thrown, this is not properly supported - aborting' + ); + ___cxa_call_unexpected.sig = 'vp'; + + var ___cxa_current_primary_exception = () => { + if (!exceptionCaught.length) { + return 0; + } + var info = exceptionCaught[exceptionCaught.length - 1]; + ___cxa_increment_exception_refcount(info.excPtr); + return info.excPtr; + }; + ___cxa_current_primary_exception.sig = 'p'; + + function ___cxa_current_exception_type() { + if (!exceptionCaught.length) { + return 0; + } + var info = exceptionCaught[exceptionCaught.length - 1]; + return info.get_type(); + } + ___cxa_current_exception_type.sig = 'p'; + + var ___cxa_rethrow_primary_exception = (ptr) => { + if (!ptr) return; + var info = new ExceptionInfo(ptr); + exceptionCaught.push(info); + info.set_rethrown(true); + ___cxa_rethrow(); + }; + ___cxa_rethrow_primary_exception.sig = 'vp'; + + var Browser = { + useWebGL: false, + isFullscreen: false, + pointerLock: false, + moduleContextCreatedCallbacks: [], + workers: [], + preloadedImages: {}, + preloadedAudios: {}, + getCanvas: () => Module['canvas'], + init() { + if (Browser.initted) return; + Browser.initted = true; + + // Support for plugins that can process preloaded files. You can add more of these to + // your app by creating and appending to preloadPlugins. + // + // Each plugin is asked if it can handle a file based on the file's name. If it can, + // it is given the file's raw data. When it is done, it calls a callback with the file's + // (possibly modified) data. For example, a plugin might decompress a file, or it + // might create some side data structure for use later (like an Image element, etc.). + + var imagePlugin = {}; + imagePlugin['canHandle'] = function imagePlugin_canHandle(name) { + return ( + !Module['noImageDecoding'] && + /\.(jpg|jpeg|png|bmp|webp)$/i.test(name) + ); + }; + imagePlugin['handle'] = async function imagePlugin_handle( + byteArray, + name + ) { + var b = new Blob([byteArray], { + type: Browser.getMimetype(name), + }); + if (b.size !== byteArray.length) { + // Safari bug #118630 + // Safari's Blob can only take an ArrayBuffer + b = new Blob([new Uint8Array(byteArray).buffer], { + type: Browser.getMimetype(name), + }); + } + var url = URL.createObjectURL(b); + return new Promise((resolve, reject) => { + var img = new Image(); + img.onload = () => { + var canvas = /** @type {!HTMLCanvasElement} */ ( + document.createElement('canvas') + ); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + Browser.preloadedImages[name] = canvas; + URL.revokeObjectURL(url); + resolve(byteArray); + }; + img.onerror = (event) => { + err(`Image ${url} could not be decoded`); + reject(); + }; + img.src = url; + }); + }; + preloadPlugins.push(imagePlugin); + + var audioPlugin = {}; + audioPlugin['canHandle'] = function audioPlugin_canHandle(name) { + return ( + !Module['noAudioDecoding'] && + name.slice(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 } + ); + }; + audioPlugin['handle'] = async function audioPlugin_handle( + byteArray, + name + ) { + return new Promise((resolve, reject) => { + var done = false; + function finish(audio) { + if (done) return; + done = true; + Browser.preloadedAudios[name] = audio; + resolve(byteArray); + } + var b = new Blob([byteArray], { + type: Browser.getMimetype(name), + }); + var url = URL.createObjectURL(b); // XXX we never revoke this! + var audio = new Audio(); + audio.addEventListener( + 'canplaythrough', + () => finish(audio), + false + ); // use addEventListener due to chromium bug 124926 + audio.onerror = function audio_onerror(event) { + if (done) return; + err( + `warning: browser could not fully decode audio ${name}, trying slower base64 approach` + ); + function encode64(data) { + var BASE = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + var PAD = '='; + var ret = ''; + var leftchar = 0; + var leftbits = 0; + for (var i = 0; i < data.length; i++) { + leftchar = (leftchar << 8) | data[i]; + leftbits += 8; + while (leftbits >= 6) { + var curr = + (leftchar >> (leftbits - 6)) & 0x3f; + leftbits -= 6; + ret += BASE[curr]; + } + } + if (leftbits == 2) { + ret += BASE[(leftchar & 3) << 4]; + ret += PAD + PAD; + } else if (leftbits == 4) { + ret += BASE[(leftchar & 0xf) << 2]; + ret += PAD; + } + return ret; + } + audio.src = + 'data:audio/x-' + + name.slice(-3) + + ';base64,' + + encode64(byteArray); + finish(audio); // we don't wait for confirmation this worked - but it's worth trying + }; + audio.src = url; + // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror + safeSetTimeout(() => { + finish(audio); // try to use it even though it is not necessarily ready to play + }, 10000); + }); + }; + preloadPlugins.push(audioPlugin); + + // Canvas event setup + + function pointerLockChange() { + var canvas = Browser.getCanvas(); + Browser.pointerLock = document.pointerLockElement === canvas; + } + var canvas = Browser.getCanvas(); + if (canvas) { + // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module + // Module['forcedAspectRatio'] = 4 / 3; + + document.addEventListener( + 'pointerlockchange', + pointerLockChange, + false + ); + + if (Module['elementPointerLock']) { + canvas.addEventListener( + 'click', + (ev) => { + if ( + !Browser.pointerLock && + Browser.getCanvas().requestPointerLock + ) { + Browser.getCanvas().requestPointerLock(); + ev.preventDefault(); + } + }, + false + ); + } + } + }, + createContext( + /** @type {HTMLCanvasElement} */ canvas, + useWebGL, + setInModule, + webGLContextAttributes + ) { + if (useWebGL && Module['ctx'] && canvas == Browser.getCanvas()) + return Module['ctx']; // no need to recreate GL context if it's already been created for this canvas. + + var ctx; + var contextHandle; + if (useWebGL) { + // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults. + var contextAttributes = { + antialias: false, + alpha: false, + majorVersion: 1, + }; + + if (webGLContextAttributes) { + for (var attribute in webGLContextAttributes) { + contextAttributes[attribute] = + webGLContextAttributes[attribute]; + } + } + + // This check of existence of GL is here to satisfy Closure compiler, which yells if variable GL is referenced below but GL object is not + // actually compiled in because application is not doing any GL operations. TODO: Ideally if GL is not being used, this function + // Browser.createContext() should not even be emitted. + if (typeof GL != 'undefined') { + contextHandle = GL.createContext(canvas, contextAttributes); + if (contextHandle) { + ctx = GL.getContext(contextHandle).GLctx; + } + } + } else { + ctx = canvas.getContext('2d'); + } + + if (!ctx) return null; + + if (setInModule) { + Module['ctx'] = ctx; + if (useWebGL) GL.makeContextCurrent(contextHandle); + Browser.useWebGL = useWebGL; + Browser.moduleContextCreatedCallbacks.forEach((callback) => + callback() + ); + Browser.init(); + } + return ctx; + }, + fullscreenHandlersInstalled: false, + lockPointer: undefined, + resizeCanvas: undefined, + requestFullscreen(lockPointer, resizeCanvas) { + Browser.lockPointer = lockPointer; + Browser.resizeCanvas = resizeCanvas; + if (typeof Browser.lockPointer == 'undefined') + Browser.lockPointer = true; + if (typeof Browser.resizeCanvas == 'undefined') + Browser.resizeCanvas = false; + + var canvas = Browser.getCanvas(); + function fullscreenChange() { + Browser.isFullscreen = false; + var canvasContainer = canvas.parentNode; + if (getFullscreenElement() === canvasContainer) { + canvas.exitFullscreen = Browser.exitFullscreen; + if (Browser.lockPointer) canvas.requestPointerLock(); + Browser.isFullscreen = true; + if (Browser.resizeCanvas) { + Browser.setFullscreenCanvasSize(); + } else { + Browser.updateCanvasDimensions(canvas); + } + } else { + // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen + canvasContainer.parentNode.insertBefore( + canvas, + canvasContainer + ); + canvasContainer.parentNode.removeChild(canvasContainer); + + if (Browser.resizeCanvas) { + Browser.setWindowedCanvasSize(); + } else { + Browser.updateCanvasDimensions(canvas); + } + } + Module['onFullScreen']?.(Browser.isFullscreen); + Module['onFullscreen']?.(Browser.isFullscreen); + } + + if (!Browser.fullscreenHandlersInstalled) { + Browser.fullscreenHandlersInstalled = true; + document.addEventListener( + 'fullscreenchange', + fullscreenChange, + false + ); + document.addEventListener( + 'mozfullscreenchange', + fullscreenChange, + false + ); + document.addEventListener( + 'webkitfullscreenchange', + fullscreenChange, + false + ); + document.addEventListener( + 'MSFullscreenChange', + fullscreenChange, + false + ); + } + + // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root + var canvasContainer = document.createElement('div'); + canvas.parentNode.insertBefore(canvasContainer, canvas); + canvasContainer.appendChild(canvas); + + // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size) + canvasContainer.requestFullscreen = + canvasContainer['requestFullscreen'] || + canvasContainer['mozRequestFullScreen'] || + canvasContainer['msRequestFullscreen'] || + (canvasContainer['webkitRequestFullscreen'] + ? () => + canvasContainer['webkitRequestFullscreen']( + Element['ALLOW_KEYBOARD_INPUT'] + ) + : null) || + (canvasContainer['webkitRequestFullScreen'] + ? () => + canvasContainer['webkitRequestFullScreen']( + Element['ALLOW_KEYBOARD_INPUT'] + ) + : null); + + canvasContainer.requestFullscreen(); + }, + exitFullscreen() { + // This is workaround for chrome. Trying to exit from fullscreen + // not in fullscreen state will cause "TypeError: Document not active" + // in chrome. See https://github.com/emscripten-core/emscripten/pull/8236 + if (!Browser.isFullscreen) { + return false; + } + + var CFS = + document['exitFullscreen'] || + document['cancelFullScreen'] || + document['mozCancelFullScreen'] || + document['msExitFullscreen'] || + document['webkitCancelFullScreen'] || + (() => {}); + CFS.apply(document, []); + return true; + }, + safeSetTimeout(func, timeout) { + // Legacy function, this is used by the SDL2 port so we need to keep it + // around at least until that is updated. + // See https://github.com/libsdl-org/SDL/pull/6304 + return safeSetTimeout(func, timeout); + }, + getMimetype(name) { + return { + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + png: 'image/png', + bmp: 'image/bmp', + ogg: 'audio/ogg', + wav: 'audio/wav', + mp3: 'audio/mpeg', + }[name.slice(name.lastIndexOf('.') + 1)]; + }, + getUserMedia(func) { + window.getUserMedia ||= + navigator['getUserMedia'] || navigator['mozGetUserMedia']; + window.getUserMedia(func); + }, + getMovementX(event) { + return ( + event['movementX'] || + event['mozMovementX'] || + event['webkitMovementX'] || + 0 + ); + }, + getMovementY(event) { + return ( + event['movementY'] || + event['mozMovementY'] || + event['webkitMovementY'] || + 0 + ); + }, + getMouseWheelDelta(event) { + var delta = 0; + switch (event.type) { + case 'DOMMouseScroll': + // 3 lines make up a step + delta = event.detail / 3; + break; + case 'mousewheel': + // 120 units make up a step + delta = event.wheelDelta / 120; + break; + case 'wheel': + delta = event.deltaY; + switch (event.deltaMode) { + case 0: + // DOM_DELTA_PIXEL: 100 pixels make up a step + delta /= 100; + break; + case 1: + // DOM_DELTA_LINE: 3 lines make up a step + delta /= 3; + break; + case 2: + // DOM_DELTA_PAGE: A page makes up 80 steps + delta *= 80; + break; + default: + abort( + 'unrecognized mouse wheel delta mode: ' + + event.deltaMode + ); + } + break; + default: + abort('unrecognized mouse wheel event: ' + event.type); + } + return delta; + }, + mouseX: 0, + mouseY: 0, + mouseMovementX: 0, + mouseMovementY: 0, + touches: {}, + lastTouches: {}, + calculateMouseCoords(pageX, pageY) { + // Calculate the movement based on the changes + // in the coordinates. + var canvas = Browser.getCanvas(); + var rect = canvas.getBoundingClientRect(); + + // Neither .scrollX or .pageXOffset are defined in a spec, but + // we prefer .scrollX because it is currently in a spec draft. + // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/) + var scrollX = + typeof window.scrollX != 'undefined' + ? window.scrollX + : window.pageXOffset; + var scrollY = + typeof window.scrollY != 'undefined' + ? window.scrollY + : window.pageYOffset; + var adjustedX = pageX - (scrollX + rect.left); + var adjustedY = pageY - (scrollY + rect.top); + + // the canvas might be CSS-scaled compared to its backbuffer; + // SDL-using content will want mouse coordinates in terms + // of backbuffer units. + adjustedX = adjustedX * (canvas.width / rect.width); + adjustedY = adjustedY * (canvas.height / rect.height); + + return { x: adjustedX, y: adjustedY }; + }, + setMouseCoords(pageX, pageY) { + const { x, y } = Browser.calculateMouseCoords(pageX, pageY); + Browser.mouseMovementX = x - Browser.mouseX; + Browser.mouseMovementY = y - Browser.mouseY; + Browser.mouseX = x; + Browser.mouseY = y; + }, + calculateMouseEvent(event) { + // event should be mousemove, mousedown or mouseup + if (Browser.pointerLock) { + // When the pointer is locked, calculate the coordinates + // based on the movement of the mouse. + // Workaround for Firefox bug 764498 + if (event.type != 'mousemove' && 'mozMovementX' in event) { + Browser.mouseMovementX = Browser.mouseMovementY = 0; + } else { + Browser.mouseMovementX = Browser.getMovementX(event); + Browser.mouseMovementY = Browser.getMovementY(event); + } + + // add the mouse delta to the current absolute mouse position + Browser.mouseX += Browser.mouseMovementX; + Browser.mouseY += Browser.mouseMovementY; + } else { + if ( + event.type === 'touchstart' || + event.type === 'touchend' || + event.type === 'touchmove' + ) { + var touch = event.touch; + if (touch === undefined) { + return; // the "touch" property is only defined in SDL + } + var coords = Browser.calculateMouseCoords( + touch.pageX, + touch.pageY + ); + + if (event.type === 'touchstart') { + Browser.lastTouches[touch.identifier] = coords; + Browser.touches[touch.identifier] = coords; + } else if ( + event.type === 'touchend' || + event.type === 'touchmove' + ) { + var last = Browser.touches[touch.identifier]; + last ||= coords; + Browser.lastTouches[touch.identifier] = last; + Browser.touches[touch.identifier] = coords; + } + return; + } + + Browser.setMouseCoords(event.pageX, event.pageY); + } + }, + resizeListeners: [], + updateResizeListeners() { + var canvas = Browser.getCanvas(); + Browser.resizeListeners.forEach((listener) => + listener(canvas.width, canvas.height) + ); + }, + setCanvasSize(width, height, noUpdates) { + var canvas = Browser.getCanvas(); + Browser.updateCanvasDimensions(canvas, width, height); + if (!noUpdates) Browser.updateResizeListeners(); + }, + windowedWidth: 0, + windowedHeight: 0, + setFullscreenCanvasSize() { + // check if SDL is available + if (typeof SDL != 'undefined') { + var flags = HEAPU32[SDL.screen >> 2]; + flags = flags | 0x00800000; // set SDL_FULLSCREEN flag + HEAP32[SDL.screen >> 2] = flags; + } + Browser.updateCanvasDimensions(Browser.getCanvas()); + Browser.updateResizeListeners(); + }, + setWindowedCanvasSize() { + // check if SDL is available + if (typeof SDL != 'undefined') { + var flags = HEAPU32[SDL.screen >> 2]; + flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag + HEAP32[SDL.screen >> 2] = flags; + } + Browser.updateCanvasDimensions(Browser.getCanvas()); + Browser.updateResizeListeners(); + }, + updateCanvasDimensions(canvas, wNative, hNative) { + if (wNative && hNative) { + canvas.widthNative = wNative; + canvas.heightNative = hNative; + } else { + wNative = canvas.widthNative; + hNative = canvas.heightNative; + } + var w = wNative; + var h = hNative; + if (Module['forcedAspectRatio'] > 0) { + if (w / h < Module['forcedAspectRatio']) { + w = Math.round(h * Module['forcedAspectRatio']); + } else { + h = Math.round(w / Module['forcedAspectRatio']); + } + } + if ( + getFullscreenElement() === canvas.parentNode && + typeof screen != 'undefined' + ) { + var factor = Math.min(screen.width / w, screen.height / h); + w = Math.round(w * factor); + h = Math.round(h * factor); + } + if (Browser.resizeCanvas) { + if (canvas.width != w) canvas.width = w; + if (canvas.height != h) canvas.height = h; + if (typeof canvas.style != 'undefined') { + canvas.style.removeProperty('width'); + canvas.style.removeProperty('height'); + } + } else { + if (canvas.width != wNative) canvas.width = wNative; + if (canvas.height != hNative) canvas.height = hNative; + if (typeof canvas.style != 'undefined') { + if (w != wNative || h != hNative) { + canvas.style.setProperty( + 'width', + w + 'px', + 'important' + ); + canvas.style.setProperty( + 'height', + h + 'px', + 'important' + ); + } else { + canvas.style.removeProperty('width'); + canvas.style.removeProperty('height'); + } + } + } + }, + }; + + var requestFullscreen = Browser.requestFullscreen; + + var setCanvasSize = Browser.setCanvasSize; + + var getUserMedia = Browser.getUserMedia; + + var createContext = Browser.createContext; + + var _emscripten_run_preload_plugins = (file, onload, onerror) => { + runtimeKeepalivePush(); + + var _file = UTF8ToString(file); + var data = FS.analyzePath(_file); + if (!data.exists) return -1; + FS.createPreloadedFile( + PATH.dirname(_file), + PATH.basename(_file), + // TODO: This copy is not needed if the contents are already a Uint8Array, + // which they often are (and always are in WasmFS). + new Uint8Array(data.object.contents), + true, + true, + () => { + runtimeKeepalivePop(); + if (onload) + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + file + ); + }, + () => { + runtimeKeepalivePop(); + if (onerror) + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + file + ); + }, + true // don'tCreateFile - it's already there + ); + return 0; + }; + _emscripten_run_preload_plugins.sig = 'ippp'; + + var Browser_asyncPrepareDataCounter = 0; + + var _emscripten_run_preload_plugins_data = ( + data, + size, + suffix, + arg, + onload, + onerror + ) => { + runtimeKeepalivePush(); + + var _suffix = UTF8ToString(suffix); + var name = + 'prepare_data_' + Browser_asyncPrepareDataCounter++ + '.' + _suffix; + var cname = stringToNewUTF8(name); + FS.createPreloadedFile( + '/', + name, + HEAPU8.subarray(data, data + size), + true, + true, + () => { + runtimeKeepalivePop(); + if (onload) + (( + a1, + a2 + ) => {}) /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + arg, + cname + ); + }, + () => { + runtimeKeepalivePop(); + if (onerror) + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + arg + ); + }, + true // don'tCreateFile - it's already there + ); + }; + _emscripten_run_preload_plugins_data.sig = 'vpipppp'; + + var _emscripten_async_run_script = (script, millis) => { + // TODO: cache these to avoid generating garbage + safeSetTimeout(() => _emscripten_run_script(script), millis); + }; + _emscripten_async_run_script.sig = 'vpi'; + + var _emscripten_async_load_script = async (url, onload, onerror) => { + url = UTF8ToString(url); + runtimeKeepalivePush(); + + var loadDone = () => { + runtimeKeepalivePop(); + if (onload) { + var onloadCallback = () => + callUserCallback( + () => {} /* a dynamic function call to signature v, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */ + ); + if (runDependencies > 0) { + dependenciesFulfilled = onloadCallback; + } else { + onloadCallback(); + } + } + }; + + var loadError = () => { + runtimeKeepalivePop(); + if (onerror) { + callUserCallback( + () => {} /* a dynamic function call to signature v, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */ + ); + } + }; + + if (ENVIRONMENT_IS_NODE) { + try { + var data = await readAsync(url, false); + eval(data); + loadDone(); + } catch (e) { + err(e); + loadError(); + } + return; + } + + var script = document.createElement('script'); + script.onload = loadDone; + script.onerror = loadError; + script.src = url; + document.body.appendChild(script); + }; + _emscripten_async_load_script.sig = 'vppp'; + + var _emscripten_get_window_title = () => { + var buflen = 256; + + if (!_emscripten_get_window_title.buffer) { + _emscripten_get_window_title.buffer = _malloc(buflen); + } + + stringToUTF8( + document.title, + _emscripten_get_window_title.buffer, + buflen + ); + + return _emscripten_get_window_title.buffer; + }; + _emscripten_get_window_title.sig = 'p'; + + var _emscripten_set_window_title = (title) => + (document.title = UTF8ToString(title)); + _emscripten_set_window_title.sig = 'vp'; + + var _emscripten_get_screen_size = (width, height) => { + HEAP32[width >> 2] = screen.width; + HEAP32[height >> 2] = screen.height; + }; + _emscripten_get_screen_size.sig = 'vpp'; + + var _emscripten_hide_mouse = () => { + var styleSheet = document.styleSheets[0]; + var rules = styleSheet.cssRules; + for (var i = 0; i < rules.length; i++) { + if (rules[i].cssText.startsWith('canvas')) { + styleSheet.deleteRule(i); + i--; + } + } + styleSheet.insertRule( + 'canvas.emscripten { border: 1px solid black; cursor: none; }', + 0 + ); + }; + _emscripten_hide_mouse.sig = 'v'; + + var _emscripten_set_canvas_size = (width, height) => + Browser.setCanvasSize(width, height); + _emscripten_set_canvas_size.sig = 'vii'; + + var _emscripten_get_canvas_size = (width, height, isFullscreen) => { + var canvas = Browser.getCanvas(); + HEAP32[width >> 2] = canvas.width; + HEAP32[height >> 2] = canvas.height; + HEAP32[isFullscreen >> 2] = Browser.isFullscreen ? 1 : 0; + }; + _emscripten_get_canvas_size.sig = 'vppp'; + + var _emscripten_create_worker = (url) => { + url = UTF8ToString(url); + var id = Browser.workers.length; + var info = { + worker: new Worker(url), + callbacks: [], + awaited: 0, + buffer: 0, + }; + info.worker.onmessage = function info_worker_onmessage(msg) { + if (ABORT) return; + var info = Browser.workers[id]; + if (!info) return; // worker was destroyed meanwhile + var callbackId = msg.data['callbackId']; + var callbackInfo = info.callbacks[callbackId]; + if (!callbackInfo) return; // no callback or callback removed meanwhile + // Don't trash our callback state if we expect additional calls. + if (msg.data['finalResponse']) { + info.awaited--; + info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this + runtimeKeepalivePop(); + } + var data = msg.data['data']; + if (data) { + if (!data.byteLength) data = new Uint8Array(data); + info.buffer = _realloc(info.buffer, data.length); + HEAPU8.set(data, info.buffer); + callbackInfo.func(info.buffer, data.length, callbackInfo.arg); + } else { + callbackInfo.func(0, 0, callbackInfo.arg); + } + }; + Browser.workers.push(info); + return id; + }; + _emscripten_create_worker.sig = 'ip'; + + var _emscripten_destroy_worker = (id) => { + var info = Browser.workers[id]; + info.worker.terminate(); + _free(info.buffer); + Browser.workers[id] = null; + }; + _emscripten_destroy_worker.sig = 'vi'; + + var _emscripten_call_worker = (id, funcName, data, size, callback, arg) => { + funcName = UTF8ToString(funcName); + var info = Browser.workers[id]; + var callbackId = -1; + if (callback) { + // If we are waiting for a response from the worker we need to keep + // the runtime alive at least long enough to receive it. + // The corresponding runtimeKeepalivePop is in the `finalResponse` + // handler above. + runtimeKeepalivePush(); + callbackId = info.callbacks.length; + info.callbacks.push({ + func: ( + a1, + a2, + a3 + ) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */, + arg, + }); + info.awaited++; + } + var transferObject = { + funcName: funcName, + callbackId: callbackId, + data: data ? new Uint8Array(HEAPU8.subarray(data, data + size)) : 0, + }; + if (data) { + info.worker.postMessage(transferObject, [ + transferObject.data.buffer, + ]); + } else { + info.worker.postMessage(transferObject); + } + }; + _emscripten_call_worker.sig = 'vippipp'; + + var _emscripten_get_worker_queue_size = (id) => { + var info = Browser.workers[id]; + if (!info) return -1; + return info.awaited; + }; + _emscripten_get_worker_queue_size.sig = 'ii'; + + var getPreloadedImageData = (path, w, h) => { + path = PATH_FS.resolve(path); + + var canvas = /** @type {HTMLCanvasElement} */ ( + Browser.preloadedImages[path] + ); + if (!canvas) return 0; + + var ctx = canvas.getContext('2d'); + var image = ctx.getImageData(0, 0, canvas.width, canvas.height); + var buf = _malloc(canvas.width * canvas.height * 4); + + HEAPU8.set(image.data, buf); + + HEAP32[w >> 2] = canvas.width; + HEAP32[h >> 2] = canvas.height; + return buf; + }; + + var _emscripten_get_preloaded_image_data = (path, w, h) => + getPreloadedImageData(UTF8ToString(path), w, h); + _emscripten_get_preloaded_image_data.sig = 'pppp'; + + var getPreloadedImageData__data = ['$PATH_FS', 'malloc']; + + var _emscripten_get_preloaded_image_data_from_FILE = (file, w, h) => { + var fd = _fileno(file); + var stream = FS.getStream(fd); + if (stream) { + return getPreloadedImageData(stream.path, w, h); + } + + return 0; + }; + _emscripten_get_preloaded_image_data_from_FILE.sig = 'pppp'; + + var wget = { + wgetRequests: {}, + nextWgetRequestHandle: 0, + getNextWgetRequestHandle() { + var handle = wget.nextWgetRequestHandle; + wget.nextWgetRequestHandle++; + return handle; + }, + }; + + /** + * @param {number=} mode Optionally, the mode to create in. Uses mkdir's + * default if not set. + */ + var FS_mkdirTree = (path, mode) => FS.mkdirTree(path, mode); + + var _emscripten_async_wget = (url, file, onload, onerror) => { + runtimeKeepalivePush(); + + var _url = UTF8ToString(url); + var _file = UTF8ToString(file); + _file = PATH_FS.resolve(_file); + function doCallback(callback) { + if (callback) { + runtimeKeepalivePop(); + callUserCallback(() => + withStackSave(() => + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + stringToUTF8OnStack(_file) + ) + ) + ); + } + } + var destinationDirectory = PATH.dirname(_file); + FS_preloadFile( + destinationDirectory, + PATH.basename(_file), + _url, + true, + true, + false, // dontCreateFile + false, // canOwn + () => { + // preFinish + // if a file exists there, we overwrite it + try { + FS_unlink(_file); + } catch (e) {} + // if the destination directory does not yet exist, create it + FS_mkdirTree(destinationDirectory); + } + ) + .then(() => doCallback(onload)) + .catch(() => doCallback(onerror)); + }; + _emscripten_async_wget.sig = 'vpppp'; + + var _emscripten_async_wget_data = async ( + url, + userdata, + onload, + onerror + ) => { + runtimeKeepalivePush(); + /* no need for run dependency, this is async but will not do any prepare etc. step */ + try { + var byteArray = await asyncLoad(UTF8ToString(url)); + runtimeKeepalivePop(); + callUserCallback(() => { + var buffer = _malloc(byteArray.length); + HEAPU8.set(byteArray, buffer); + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + userdata, + buffer, + byteArray.length + ); + _free(buffer); + }); + } catch (e) { + if (onerror) { + runtimeKeepalivePop(); + callUserCallback(() => { + (( + a1 + ) => {}) /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + userdata + ); + }); + } + } + }; + _emscripten_async_wget_data.sig = 'vpppp'; + + var _emscripten_async_wget2 = ( + url, + file, + request, + param, + userdata, + onload, + onerror, + onprogress + ) => { + runtimeKeepalivePush(); + + var _url = UTF8ToString(url); + var _file = UTF8ToString(file); + _file = PATH_FS.resolve(_file); + var _request = UTF8ToString(request); + var _param = UTF8ToString(param); + var index = _file.lastIndexOf('/'); + + var http = new XMLHttpRequest(); + http.open(_request, _url, true); + http.responseType = 'arraybuffer'; + + var handle = wget.getNextWgetRequestHandle(); + + var destinationDirectory = PATH.dirname(_file); + + // LOAD + http.onload = (e) => { + runtimeKeepalivePop(); + if (http.status >= 200 && http.status < 300) { + // if a file exists there, we overwrite it + try { + FS.unlink(_file); + } catch (e) {} + // if the destination directory does not yet exist, create it + FS.mkdirTree(destinationDirectory); + + FS.createDataFile( + _file.slice(0, index), + _file.slice(index + 1), + new Uint8Array(/** @type{ArrayBuffer}*/ (http.response)), + true, + true, + false + ); + if (onload) { + var sp = stackSave(); + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + userdata, + stringToUTF8OnStack(_file) + ); + stackRestore(sp); + } + } else { + if (onerror) + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + userdata, + http.status + ); + } + + delete wget.wgetRequests[handle]; + }; + + // ERROR + http.onerror = (e) => { + runtimeKeepalivePop(); + if (onerror) + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + userdata, + http.status + ); + delete wget.wgetRequests[handle]; + }; + + // PROGRESS + http.onprogress = (e) => { + if ( + e.lengthComputable || + (e.lengthComputable === undefined && e.total != 0) + ) { + var percentComplete = (e.loaded / e.total) * 100; + if (onprogress) + (( + a1, + a2, + a3 + ) => {}) /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + userdata, + percentComplete + ); + } + }; + + // ABORT + http.onabort = (e) => { + runtimeKeepalivePop(); + delete wget.wgetRequests[handle]; + }; + + if (_request == 'POST') { + //Send the proper header information along with the request + http.setRequestHeader( + 'Content-type', + 'application/x-www-form-urlencoded' + ); + http.send(_param); + } else { + http.send(null); + } + + wget.wgetRequests[handle] = http; + + return handle; + }; + _emscripten_async_wget2.sig = 'ipppppppp'; + + var _emscripten_async_wget2_data = ( + url, + request, + param, + userdata, + free, + onload, + onerror, + onprogress + ) => { + var _url = UTF8ToString(url); + var _request = UTF8ToString(request); + var _param = UTF8ToString(param); + + var http = new XMLHttpRequest(); + http.open(_request, _url, true); + http.responseType = 'arraybuffer'; + + var handle = wget.getNextWgetRequestHandle(); + + function onerrorjs() { + if (onerror) { + var sp = stackSave(); + var statusText = 0; + if (http.statusText) { + statusText = stringToUTF8OnStack(http.statusText); + } + (( + a1, + a2, + a3, + a4 + ) => {}) /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + userdata, + http.status, + statusText + ); + stackRestore(sp); + } + } + + // LOAD + http.onload = (e) => { + if ( + (http.status >= 200 && http.status < 300) || + (http.status === 0 && _url.slice(0, 4).toLowerCase() != 'http') + ) { + var byteArray = new Uint8Array( + /** @type{ArrayBuffer} */ (http.response) + ); + var buffer = _malloc(byteArray.length); + HEAPU8.set(byteArray, buffer); + if (onload) + (( + a1, + a2, + a3, + a4 + ) => {}) /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + userdata, + buffer, + byteArray.length + ); + if (free) _free(buffer); + } else { + onerrorjs(); + } + delete wget.wgetRequests[handle]; + }; + + // ERROR + http.onerror = (e) => { + onerrorjs(); + delete wget.wgetRequests[handle]; + }; + + // PROGRESS + http.onprogress = (e) => { + if (onprogress) + (( + a1, + a2, + a3, + a4 + ) => {}) /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + handle, + userdata, + e.loaded, + e.lengthComputable || e.lengthComputable === undefined + ? e.total + : 0 + ); + }; + + // ABORT + http.onabort = (e) => { + delete wget.wgetRequests[handle]; + }; + + if (_request == 'POST') { + //Send the proper header information along with the request + http.setRequestHeader( + 'Content-type', + 'application/x-www-form-urlencoded' + ); + http.send(_param); + } else { + http.send(null); + } + + wget.wgetRequests[handle] = http; + + return handle; + }; + _emscripten_async_wget2_data.sig = 'ippppippp'; + + var _emscripten_async_wget2_abort = (handle) => { + var http = wget.wgetRequests[handle]; + http?.abort(); + }; + _emscripten_async_wget2_abort.sig = 'vi'; + + var ___asctime_r = (tmPtr, buf) => { + var date = { + tm_sec: HEAP32[tmPtr >> 2], + tm_min: HEAP32[(tmPtr + 4) >> 2], + tm_hour: HEAP32[(tmPtr + 8) >> 2], + tm_mday: HEAP32[(tmPtr + 12) >> 2], + tm_mon: HEAP32[(tmPtr + 16) >> 2], + tm_year: HEAP32[(tmPtr + 20) >> 2], + tm_wday: HEAP32[(tmPtr + 24) >> 2], + }; + var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + var months = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ]; + var s = + days[date.tm_wday] + + ' ' + + months[date.tm_mon] + + (date.tm_mday < 10 ? ' ' : ' ') + + date.tm_mday + + (date.tm_hour < 10 ? ' 0' : ' ') + + date.tm_hour + + (date.tm_min < 10 ? ':0' : ':') + + date.tm_min + + (date.tm_sec < 10 ? ':0' : ':') + + date.tm_sec + + ' ' + + (1900 + date.tm_year) + + '\n'; + + // asctime_r is specced to behave in an undefined manner if the algorithm would attempt + // to write out more than 26 bytes (including the null terminator). + // See http://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime.html + // Our undefined behavior is to truncate the write to at most 26 bytes, including null terminator. + stringToUTF8(s, buf, 26); + return buf; + }; + ___asctime_r.sig = 'ppp'; + + var _strptime_l = (buf, format, tm, locale) => _strptime(buf, format, tm); + _strptime_l.sig = 'ppppp'; + + function ___syscall_shutdown(fd, how) { + try { + getSocketFromFD(fd); + return -52; // unsupported feature + } catch (e) { + if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; + return -e.errno; + } + } + ___syscall_shutdown.sig = 'iiiiiii'; - var _alBufferf = (bufferId, param, value) => { - AL.setBufferParam('alBufferf', bufferId, param, null); - }; - _alBufferf.sig = 'viif'; + var __dlsym_catchup_js = (handle, symbolIndex) => { + var lib = LDSO.loadedLibsByHandle[handle]; + var symDict = lib.exports; + var symName = Object.keys(symDict)[symbolIndex]; + var sym = symDict[symName]; + var result = addFunction(sym, sym.sig); + return result; + }; + __dlsym_catchup_js.sig = 'ppi'; - var _alBufferfv = (bufferId, param, pValues) => { - if (!AL.currentCtx) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - AL.setBufferParam('alBufferfv', bufferId, param, null); - }; - _alBufferfv.sig = 'viip'; - - var _alBufferi = (bufferId, param, value) => { - AL.setBufferParam('alBufferi', bufferId, param, null); - }; - _alBufferi.sig = 'viii'; - - var _alBufferiv = (bufferId, param, pValues) => { - if (!AL.currentCtx) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x2015 /* AL_LOOP_POINTS_SOFT */: - AL.paramArray[0] = HEAP32[((pValues)>>2)]; - AL.paramArray[1] = HEAP32[(((pValues)+(4))>>2)]; - AL.setBufferParam('alBufferiv', bufferId, param, AL.paramArray); - break; - default: - AL.setBufferParam('alBufferiv', bufferId, param, null); - break; - } - }; - _alBufferiv.sig = 'viip'; + var FS_readFile = (...args) => FS.readFile(...args); - var _alDeleteBuffers = (count, pBufferIds) => { - if (!AL.currentCtx) { - return; - } - - for (var i = 0; i < count; ++i) { - var bufId = HEAP32[(((pBufferIds)+(i*4))>>2)]; - /// Deleting the zero buffer is a legal NOP, so ignore it - if (bufId === 0) { - continue; - } - - // Make sure the buffer index is valid. - if (!AL.buffers[bufId]) { - AL.currentCtx.err = 40961; - return; - } - - // Make sure the buffer is no longer in use. - if (AL.buffers[bufId].refCount) { - AL.currentCtx.err = 40964; - return; - } - } - - for (var i = 0; i < count; ++i) { - var bufId = HEAP32[(((pBufferIds)+(i*4))>>2)]; - if (bufId === 0) { - continue; - } - - AL.deviceRefCounts[AL.buffers[bufId].deviceId]--; - delete AL.buffers[bufId]; - AL.freeIds.push(bufId); - } - }; - _alDeleteBuffers.sig = 'vip'; - - var _alSourcei = (sourceId, param, value) => { - switch (param) { - case 0x202 /* AL_SOURCE_RELATIVE */: - case 0x1001 /* AL_CONE_INNER_ANGLE */: - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - case 0x1007 /* AL_LOOPING */: - case 0x1009 /* AL_BUFFER */: - case 0x1020 /* AL_REFERENCE_DISTANCE */: - case 0x1021 /* AL_ROLLOFF_FACTOR */: - case 0x1023 /* AL_MAX_DISTANCE */: - case 0x1024 /* AL_SEC_OFFSET */: - case 0x1025 /* AL_SAMPLE_OFFSET */: - case 0x1026 /* AL_BYTE_OFFSET */: - case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: - case 0x2009 /* AL_BYTE_LENGTH_SOFT */: - case 0x200A /* AL_SAMPLE_LENGTH_SOFT */: - case 53248: - AL.setSourceParam('alSourcei', sourceId, param, value); - break; - default: - AL.setSourceParam('alSourcei', sourceId, param, null); - break; - } - }; - _alSourcei.sig = 'viii'; - - var _alDeleteSources = (count, pSourceIds) => { - if (!AL.currentCtx) { - return; - } - - for (var i = 0; i < count; ++i) { - var srcId = HEAP32[(((pSourceIds)+(i*4))>>2)]; - if (!AL.currentCtx.sources[srcId]) { - AL.currentCtx.err = 40961; - return; - } - } - - for (var i = 0; i < count; ++i) { - var srcId = HEAP32[(((pSourceIds)+(i*4))>>2)]; - AL.setSourceState(AL.currentCtx.sources[srcId], 4116); - _alSourcei(srcId, 0x1009 /* AL_BUFFER */, 0); - delete AL.currentCtx.sources[srcId]; - AL.freeIds.push(srcId); - } - }; - _alDeleteSources.sig = 'vip'; + var FS_root = (...args) => FS.root(...args); - var _alDisable = (param) => { - if (!AL.currentCtx) { - return; - } - switch (param) { - case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: - AL.currentCtx.sourceDistanceModel = false; - AL.updateContextGlobal(AL.currentCtx); - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alDisable.sig = 'vi'; - - var _alDistanceModel = (model) => { - AL.setGlobalParam('alDistanceModel', 53248, model); - }; - _alDistanceModel.sig = 'vi'; - - var _alDopplerFactor = (value) => { - AL.setGlobalParam('alDopplerFactor', 49152, value); - }; - _alDopplerFactor.sig = 'vf'; - - var _alDopplerVelocity = (value) => { - warnOnce('alDopplerVelocity() is deprecated, and only kept for compatibility with OpenAL 1.0. Use alSpeedOfSound() instead.'); - if (!AL.currentCtx) { - return; - } - if (value <= 0) { // Negative or zero values are disallowed - AL.currentCtx.err = 40963; - return; - } - }; - _alDopplerVelocity.sig = 'vf'; + var FS_mounts = (...args) => FS.mounts(...args); - var _alEnable = (param) => { - if (!AL.currentCtx) { - return; - } - switch (param) { - case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: - AL.currentCtx.sourceDistanceModel = true; - AL.updateContextGlobal(AL.currentCtx); - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alEnable.sig = 'vi'; + var FS_devices = (...args) => FS.devices(...args); - var _alGenBuffers = (count, pBufferIds) => { - if (!AL.currentCtx) { - return; - } - - for (var i = 0; i < count; ++i) { - var buf = { - deviceId: AL.currentCtx.deviceId, - id: AL.newId(), - refCount: 0, - audioBuf: null, - frequency: 0, - bytesPerSample: 2, - channels: 1, - length: 0, - }; - AL.deviceRefCounts[buf.deviceId]++; - AL.buffers[buf.id] = buf; - HEAP32[(((pBufferIds)+(i*4))>>2)] = buf.id; - } - }; - _alGenBuffers.sig = 'vip'; + var FS_streams = (...args) => FS.streams(...args); - var _alGenSources = (count, pSourceIds) => { - if (!AL.currentCtx) { - return; - } - for (var i = 0; i < count; ++i) { - var gain = AL.currentCtx.audioCtx.createGain(); - gain.connect(AL.currentCtx.gain); - var src = { - context: AL.currentCtx, - id: AL.newId(), - type: 0x1030 /* AL_UNDETERMINED */, - state: 4113, - bufQueue: [AL.buffers[0]], - audioQueue: [], - looping: false, - pitch: 1.0, - dopplerShift: 1.0, - gain, - minGain: 0.0, - maxGain: 1.0, - panner: null, - bufsProcessed: 0, - bufStartTime: Number.NEGATIVE_INFINITY, - bufOffset: 0.0, - relative: false, - refDistance: 1.0, - maxDistance: 3.40282e38 /* FLT_MAX */, - rolloffFactor: 1.0, - position: [0.0, 0.0, 0.0], - velocity: [0.0, 0.0, 0.0], - direction: [0.0, 0.0, 0.0], - coneOuterGain: 0.0, - coneInnerAngle: 360.0, - coneOuterAngle: 360.0, - distanceModel: 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */, - spatialize: 2 /* AL_AUTO_SOFT */, - - get playbackRate() { - return this.pitch * this.dopplerShift; - } - }; - AL.currentCtx.sources[src.id] = src; - HEAP32[(((pSourceIds)+(i*4))>>2)] = src.id; - } - }; - _alGenSources.sig = 'vip'; + var FS_nextInode = (...args) => FS.nextInode(...args); - var _alGetBoolean = (param) => { - var val = AL.getGlobalParam('alGetBoolean', param); - if (val === null) { - return 0; - } - - switch (param) { - case 49152: - case 49155: - case 53248: - return val !== 0 ? 1 : 0; - default: - AL.currentCtx.err = 40962; - return 0; - } - }; - _alGetBoolean.sig = 'ii'; - - var _alGetBooleanv = (param, pValues) => { - var val = AL.getGlobalParam('alGetBooleanv', param); - // Silently ignore null destinations, as per the spec for global state functions - if (val === null || !pValues) { - return; - } - - switch (param) { - case 49152: - case 49155: - case 53248: - HEAP8[pValues] = val; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetBooleanv.sig = 'vip'; + var FS_nameTable = (...args) => FS.nameTable(...args); - var _alGetBuffer3f = (bufferId, param, pValue0, pValue1, pValue2) => { - var val = AL.getBufferParam('alGetBuffer3f', bufferId, param); - if (val === null) { - return; - } - if (!pValue0 || !pValue1 || !pValue2) { - AL.currentCtx.err = 40963; - return; - } - - AL.currentCtx.err = 40962; - }; - _alGetBuffer3f.sig = 'viippp'; - - var _alGetBuffer3i = (bufferId, param, pValue0, pValue1, pValue2) => { - var val = AL.getBufferParam('alGetBuffer3i', bufferId, param); - if (val === null) { - return; - } - if (!pValue0 || !pValue1 || !pValue2) { - AL.currentCtx.err = 40963; - return; - } - - AL.currentCtx.err = 40962; - }; - _alGetBuffer3i.sig = 'viippp'; - - var _alGetBufferf = (bufferId, param, pValue) => { - var val = AL.getBufferParam('alGetBufferf', bufferId, param); - if (val === null) { - return; - } - if (!pValue) { - AL.currentCtx.err = 40963; - return; - } - - AL.currentCtx.err = 40962; - }; - _alGetBufferf.sig = 'viip'; - - var _alGetBufferfv = (bufferId, param, pValues) => { - var val = AL.getBufferParam('alGetBufferfv', bufferId, param); - if (val === null) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - AL.currentCtx.err = 40962; - }; - _alGetBufferfv.sig = 'viip'; - - var _alGetBufferi = (bufferId, param, pValue) => { - var val = AL.getBufferParam('alGetBufferi', bufferId, param); - if (val === null) { - return; - } - if (!pValue) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x2001 /* AL_FREQUENCY */: - case 0x2002 /* AL_BITS */: - case 0x2003 /* AL_CHANNELS */: - case 0x2004 /* AL_SIZE */: - HEAP32[((pValue)>>2)] = val; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetBufferi.sig = 'viip'; + var FS_currentPath = (...args) => FS.currentPath(...args); - var _alGetBufferiv = (bufferId, param, pValues) => { - var val = AL.getBufferParam('alGetBufferiv', bufferId, param); - if (val === null) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x2001 /* AL_FREQUENCY */: - case 0x2002 /* AL_BITS */: - case 0x2003 /* AL_CHANNELS */: - case 0x2004 /* AL_SIZE */: - HEAP32[((pValues)>>2)] = val; - break; - case 0x2015 /* AL_LOOP_POINTS_SOFT */: - HEAP32[((pValues)>>2)] = val[0]; - HEAP32[(((pValues)+(4))>>2)] = val[1]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetBufferiv.sig = 'viip'; + var FS_initialized = (...args) => FS.initialized(...args); - var _alGetDouble = (param) => { - var val = AL.getGlobalParam('alGetDouble', param); - if (val === null) { - return 0.0; - } - - switch (param) { - case 49152: - case 49155: - case 53248: - return val; - default: - AL.currentCtx.err = 40962; - return 0.0; - } - }; - _alGetDouble.sig = 'di'; - - var _alGetDoublev = (param, pValues) => { - var val = AL.getGlobalParam('alGetDoublev', param); - // Silently ignore null destinations, as per the spec for global state functions - if (val === null || !pValues) { - return; - } - - switch (param) { - case 49152: - case 49155: - case 53248: - HEAPF64[((pValues)>>3)] = val; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetDoublev.sig = 'vip'; + var FS_ignorePermissions = (...args) => FS.ignorePermissions(...args); - - var _alGetEnumValue = (pEnumName) => { - if (!AL.currentCtx) { - return 0; - } - - if (!pEnumName) { - AL.currentCtx.err = 40963; - return 0; - } - var name = UTF8ToString(pEnumName); - - switch (name) { - // Spec doesn't clearly state that alGetEnumValue() is required to - // support _only_ extension tokens. - // We should probably follow OpenAL-Soft's example and support all - // of the names we know. - // See http://repo.or.cz/openal-soft.git/blob/HEAD:/Alc/ALc.c - case 'AL_BITS': return 0x2002; - case 'AL_BUFFER': return 0x1009; - case 'AL_BUFFERS_PROCESSED': return 0x1016; - case 'AL_BUFFERS_QUEUED': return 0x1015; - case 'AL_BYTE_OFFSET': return 0x1026; - case 'AL_CHANNELS': return 0x2003; - case 'AL_CONE_INNER_ANGLE': return 0x1001; - case 'AL_CONE_OUTER_ANGLE': return 0x1002; - case 'AL_CONE_OUTER_GAIN': return 0x1022; - case 'AL_DIRECTION': return 0x1005; - case 'AL_DISTANCE_MODEL': return 0xD000; - case 'AL_DOPPLER_FACTOR': return 0xC000; - case 'AL_DOPPLER_VELOCITY': return 0xC001; - case 'AL_EXPONENT_DISTANCE': return 0xD005; - case 'AL_EXPONENT_DISTANCE_CLAMPED': return 0xD006; - case 'AL_EXTENSIONS': return 0xB004; - case 'AL_FORMAT_MONO16': return 0x1101; - case 'AL_FORMAT_MONO8': return 0x1100; - case 'AL_FORMAT_STEREO16': return 0x1103; - case 'AL_FORMAT_STEREO8': return 0x1102; - case 'AL_FREQUENCY': return 0x2001; - case 'AL_GAIN': return 0x100A; - case 'AL_INITIAL': return 0x1011; - case 'AL_INVALID': return -1; - case 'AL_ILLEGAL_ENUM': // fallthrough - case 'AL_INVALID_ENUM': return 0xA002; - case 'AL_INVALID_NAME': return 0xA001; - case 'AL_ILLEGAL_COMMAND': // fallthrough - case 'AL_INVALID_OPERATION': return 0xA004; - case 'AL_INVALID_VALUE': return 0xA003; - case 'AL_INVERSE_DISTANCE': return 0xD001; - case 'AL_INVERSE_DISTANCE_CLAMPED': return 0xD002; - case 'AL_LINEAR_DISTANCE': return 0xD003; - case 'AL_LINEAR_DISTANCE_CLAMPED': return 0xD004; - case 'AL_LOOPING': return 0x1007; - case 'AL_MAX_DISTANCE': return 0x1023; - case 'AL_MAX_GAIN': return 0x100E; - case 'AL_MIN_GAIN': return 0x100D; - case 'AL_NONE': return 0; - case 'AL_NO_ERROR': return 0; - case 'AL_ORIENTATION': return 0x100F; - case 'AL_OUT_OF_MEMORY': return 0xA005; - case 'AL_PAUSED': return 0x1013; - case 'AL_PENDING': return 0x2011; - case 'AL_PITCH': return 0x1003; - case 'AL_PLAYING': return 0x1012; - case 'AL_POSITION': return 0x1004; - case 'AL_PROCESSED': return 0x2012; - case 'AL_REFERENCE_DISTANCE': return 0x1020; - case 'AL_RENDERER': return 0xB003; - case 'AL_ROLLOFF_FACTOR': return 0x1021; - case 'AL_SAMPLE_OFFSET': return 0x1025; - case 'AL_SEC_OFFSET': return 0x1024; - case 'AL_SIZE': return 0x2004; - case 'AL_SOURCE_RELATIVE': return 0x202; - case 'AL_SOURCE_STATE': return 0x1010; - case 'AL_SOURCE_TYPE': return 0x1027; - case 'AL_SPEED_OF_SOUND': return 0xC003; - case 'AL_STATIC': return 0x1028; - case 'AL_STOPPED': return 0x1014; - case 'AL_STREAMING': return 0x1029; - case 'AL_UNDETERMINED': return 0x1030; - case 'AL_UNUSED': return 0x2010; - case 'AL_VELOCITY': return 0x1006; - case 'AL_VENDOR': return 0xB001; - case 'AL_VERSION': return 0xB002; - - /* Extensions */ - case 'AL_AUTO_SOFT': return 0x0002; - case 'AL_SOURCE_DISTANCE_MODEL': return 0x200; - case 'AL_SOURCE_SPATIALIZE_SOFT': return 0x1214; - case 'AL_LOOP_POINTS_SOFT': return 0x2015; - case 'AL_BYTE_LENGTH_SOFT': return 0x2009; - case 'AL_SAMPLE_LENGTH_SOFT': return 0x200A; - case 'AL_SEC_LENGTH_SOFT': return 0x200B; - case 'AL_FORMAT_MONO_FLOAT32': return 0x10010; - case 'AL_FORMAT_STEREO_FLOAT32': return 0x10011; - - default: - AL.currentCtx.err = 40963; - return 0; - } - }; - _alGetEnumValue.sig = 'ip'; + var FS_filesystems = (...args) => FS.filesystems(...args); - var _alGetError = () => { - if (!AL.currentCtx) { - return 40964; - } - // Reset error on get. - var err = AL.currentCtx.err; - AL.currentCtx.err = 0; - return err; - }; - _alGetError.sig = 'i'; - - var _alGetFloat = (param) => { - var val = AL.getGlobalParam('alGetFloat', param); - if (val === null) { - return 0.0; - } - - switch (param) { - case 49152: - case 49155: - case 53248: - return val; - default: - return 0.0; - } - }; - _alGetFloat.sig = 'fi'; - - var _alGetFloatv = (param, pValues) => { - var val = AL.getGlobalParam('alGetFloatv', param); - // Silently ignore null destinations, as per the spec for global state functions - if (val === null || !pValues) { - return; - } - - switch (param) { - case 49152: - case 49155: - case 53248: - HEAPF32[((pValues)>>2)] = val; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetFloatv.sig = 'vip'; + var FS_syncFSRequests = (...args) => FS.syncFSRequests(...args); - var _alGetInteger = (param) => { - var val = AL.getGlobalParam('alGetInteger', param); - if (val === null) { - return 0; - } - - switch (param) { - case 49152: - case 49155: - case 53248: - return val; - default: - AL.currentCtx.err = 40962; - return 0; - } - }; - _alGetInteger.sig = 'ii'; - - var _alGetIntegerv = (param, pValues) => { - var val = AL.getGlobalParam('alGetIntegerv', param); - // Silently ignore null destinations, as per the spec for global state functions - if (val === null || !pValues) { - return; - } - - switch (param) { - case 49152: - case 49155: - case 53248: - HEAP32[((pValues)>>2)] = val; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetIntegerv.sig = 'vip'; + var FS_readFiles = (...args) => FS.readFiles(...args); - var _alGetListener3f = (param, pValue0, pValue1, pValue2) => { - var val = AL.getListenerParam('alGetListener3f', param); - if (val === null) { - return; - } - if (!pValue0 || !pValue1 || !pValue2) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4100: - case 4102: - HEAPF32[((pValue0)>>2)] = val[0]; - HEAPF32[((pValue1)>>2)] = val[1]; - HEAPF32[((pValue2)>>2)] = val[2]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetListener3f.sig = 'vippp'; + var FS_lookupPath = (...args) => FS.lookupPath(...args); - var _alGetListener3i = (param, pValue0, pValue1, pValue2) => { - var val = AL.getListenerParam('alGetListener3i', param); - if (val === null) { - return; - } - if (!pValue0 || !pValue1 || !pValue2) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4100: - case 4102: - HEAP32[((pValue0)>>2)] = val[0]; - HEAP32[((pValue1)>>2)] = val[1]; - HEAP32[((pValue2)>>2)] = val[2]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetListener3i.sig = 'vippp'; + var FS_getPath = (...args) => FS.getPath(...args); - var _alGetListenerf = (param, pValue) => { - var val = AL.getListenerParam('alGetListenerf', param); - if (val === null) { - return; - } - if (!pValue) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4106: - HEAPF32[((pValue)>>2)] = val; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetListenerf.sig = 'vip'; + var FS_hashName = (...args) => FS.hashName(...args); - var _alGetListenerfv = (param, pValues) => { - var val = AL.getListenerParam('alGetListenerfv', param); - if (val === null) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4100: - case 4102: - HEAPF32[((pValues)>>2)] = val[0]; - HEAPF32[(((pValues)+(4))>>2)] = val[1]; - HEAPF32[(((pValues)+(8))>>2)] = val[2]; - break; - case 4111: - HEAPF32[((pValues)>>2)] = val[0]; - HEAPF32[(((pValues)+(4))>>2)] = val[1]; - HEAPF32[(((pValues)+(8))>>2)] = val[2]; - HEAPF32[(((pValues)+(12))>>2)] = val[3]; - HEAPF32[(((pValues)+(16))>>2)] = val[4]; - HEAPF32[(((pValues)+(20))>>2)] = val[5]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetListenerfv.sig = 'vip'; + var FS_hashAddNode = (...args) => FS.hashAddNode(...args); - var _alGetListeneri = (param, pValue) => { - var val = AL.getListenerParam('alGetListeneri', param); - if (val === null) { - return; - } - if (!pValue) { - AL.currentCtx.err = 40963; - return; - } - - AL.currentCtx.err = 40962; - }; - _alGetListeneri.sig = 'vip'; - - var _alGetListeneriv = (param, pValues) => { - var val = AL.getListenerParam('alGetListeneriv', param); - if (val === null) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4100: - case 4102: - HEAP32[((pValues)>>2)] = val[0]; - HEAP32[(((pValues)+(4))>>2)] = val[1]; - HEAP32[(((pValues)+(8))>>2)] = val[2]; - break; - case 4111: - HEAP32[((pValues)>>2)] = val[0]; - HEAP32[(((pValues)+(4))>>2)] = val[1]; - HEAP32[(((pValues)+(8))>>2)] = val[2]; - HEAP32[(((pValues)+(12))>>2)] = val[3]; - HEAP32[(((pValues)+(16))>>2)] = val[4]; - HEAP32[(((pValues)+(20))>>2)] = val[5]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetListeneriv.sig = 'vip'; + var FS_hashRemoveNode = (...args) => FS.hashRemoveNode(...args); - var _alGetSource3f = (sourceId, param, pValue0, pValue1, pValue2) => { - var val = AL.getSourceParam('alGetSource3f', sourceId, param); - if (val === null) { - return; - } - if (!pValue0 || !pValue1 || !pValue2) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4100: - case 4101: - case 4102: - HEAPF32[((pValue0)>>2)] = val[0]; - HEAPF32[((pValue1)>>2)] = val[1]; - HEAPF32[((pValue2)>>2)] = val[2]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetSource3f.sig = 'viippp'; + var FS_lookupNode = (...args) => FS.lookupNode(...args); - var _alGetSource3i = (sourceId, param, pValue0, pValue1, pValue2) => { - var val = AL.getSourceParam('alGetSource3i', sourceId, param); - if (val === null) { - return; - } - if (!pValue0 || !pValue1 || !pValue2) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4100: - case 4101: - case 4102: - HEAP32[((pValue0)>>2)] = val[0]; - HEAP32[((pValue1)>>2)] = val[1]; - HEAP32[((pValue2)>>2)] = val[2]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetSource3i.sig = 'viippp'; + var FS_createNode = (...args) => FS.createNode(...args); - var _alGetSourcef = (sourceId, param, pValue) => { - var val = AL.getSourceParam('alGetSourcef', sourceId, param); - if (val === null) { - return; - } - if (!pValue) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x1001 /* AL_CONE_INNER_ANGLE */: - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - case 0x1003 /* AL_PITCH */: - case 4106: - case 0x100D /* AL_MIN_GAIN */: - case 0x100E /* AL_MAX_GAIN */: - case 0x1020 /* AL_REFERENCE_DISTANCE */: - case 0x1021 /* AL_ROLLOFF_FACTOR */: - case 0x1022 /* AL_CONE_OUTER_GAIN */: - case 0x1023 /* AL_MAX_DISTANCE */: - case 0x1024 /* AL_SEC_OFFSET */: - case 0x1025 /* AL_SAMPLE_OFFSET */: - case 0x1026 /* AL_BYTE_OFFSET */: - case 0x200B /* AL_SEC_LENGTH_SOFT */: - HEAPF32[((pValue)>>2)] = val; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetSourcef.sig = 'viip'; + var FS_destroyNode = (...args) => FS.destroyNode(...args); - var _alGetSourcefv = (sourceId, param, pValues) => { - var val = AL.getSourceParam('alGetSourcefv', sourceId, param); - if (val === null) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x1001 /* AL_CONE_INNER_ANGLE */: - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - case 0x1003 /* AL_PITCH */: - case 4106: - case 0x100D /* AL_MIN_GAIN */: - case 0x100E /* AL_MAX_GAIN */: - case 0x1020 /* AL_REFERENCE_DISTANCE */: - case 0x1021 /* AL_ROLLOFF_FACTOR */: - case 0x1022 /* AL_CONE_OUTER_GAIN */: - case 0x1023 /* AL_MAX_DISTANCE */: - case 0x1024 /* AL_SEC_OFFSET */: - case 0x1025 /* AL_SAMPLE_OFFSET */: - case 0x1026 /* AL_BYTE_OFFSET */: - case 0x200B /* AL_SEC_LENGTH_SOFT */: - HEAPF32[((pValues)>>2)] = val[0]; - break; - case 4100: - case 4101: - case 4102: - HEAPF32[((pValues)>>2)] = val[0]; - HEAPF32[(((pValues)+(4))>>2)] = val[1]; - HEAPF32[(((pValues)+(8))>>2)] = val[2]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetSourcefv.sig = 'viip'; + var FS_isRoot = (...args) => FS.isRoot(...args); - var _alGetSourcei = (sourceId, param, pValue) => { - var val = AL.getSourceParam('alGetSourcei', sourceId, param); - if (val === null) { - return; - } - if (!pValue) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x202 /* AL_SOURCE_RELATIVE */: - case 0x1001 /* AL_CONE_INNER_ANGLE */: - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - case 0x1007 /* AL_LOOPING */: - case 0x1009 /* AL_BUFFER */: - case 0x1010 /* AL_SOURCE_STATE */: - case 0x1015 /* AL_BUFFERS_QUEUED */: - case 0x1016 /* AL_BUFFERS_PROCESSED */: - case 0x1020 /* AL_REFERENCE_DISTANCE */: - case 0x1021 /* AL_ROLLOFF_FACTOR */: - case 0x1023 /* AL_MAX_DISTANCE */: - case 0x1024 /* AL_SEC_OFFSET */: - case 0x1025 /* AL_SAMPLE_OFFSET */: - case 0x1026 /* AL_BYTE_OFFSET */: - case 0x1027 /* AL_SOURCE_TYPE */: - case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: - case 0x2009 /* AL_BYTE_LENGTH_SOFT */: - case 0x200A /* AL_SAMPLE_LENGTH_SOFT */: - case 53248: - HEAP32[((pValue)>>2)] = val; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetSourcei.sig = 'viip'; + var FS_isMountpoint = (...args) => FS.isMountpoint(...args); - var _alGetSourceiv = (sourceId, param, pValues) => { - var val = AL.getSourceParam('alGetSourceiv', sourceId, param); - if (val === null) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x202 /* AL_SOURCE_RELATIVE */: - case 0x1001 /* AL_CONE_INNER_ANGLE */: - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - case 0x1007 /* AL_LOOPING */: - case 0x1009 /* AL_BUFFER */: - case 0x1010 /* AL_SOURCE_STATE */: - case 0x1015 /* AL_BUFFERS_QUEUED */: - case 0x1016 /* AL_BUFFERS_PROCESSED */: - case 0x1020 /* AL_REFERENCE_DISTANCE */: - case 0x1021 /* AL_ROLLOFF_FACTOR */: - case 0x1023 /* AL_MAX_DISTANCE */: - case 0x1024 /* AL_SEC_OFFSET */: - case 0x1025 /* AL_SAMPLE_OFFSET */: - case 0x1026 /* AL_BYTE_OFFSET */: - case 0x1027 /* AL_SOURCE_TYPE */: - case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: - case 0x2009 /* AL_BYTE_LENGTH_SOFT */: - case 0x200A /* AL_SAMPLE_LENGTH_SOFT */: - case 53248: - HEAP32[((pValues)>>2)] = val; - break; - case 4100: - case 4101: - case 4102: - HEAP32[((pValues)>>2)] = val[0]; - HEAP32[(((pValues)+(4))>>2)] = val[1]; - HEAP32[(((pValues)+(8))>>2)] = val[2]; - break; - default: - AL.currentCtx.err = 40962; - return; - } - }; - _alGetSourceiv.sig = 'viip'; - - - - var stringToNewUTF8 = (str) => { - var size = lengthBytesUTF8(str) + 1; - var ret = _malloc(size); - if (ret) stringToUTF8(str, ret, size); - return ret; - }; - - var _alGetString = (param) => { - if (AL.stringCache[param]) { - return AL.stringCache[param]; - } - - var ret; - switch (param) { - case 0: - ret = 'No Error'; - break; - case 40961: - ret = 'Invalid Name'; - break; - case 40962: - ret = 'Invalid Enum'; - break; - case 40963: - ret = 'Invalid Value'; - break; - case 40964: - ret = 'Invalid Operation'; - break; - case 0xA005 /* AL_OUT_OF_MEMORY */: - ret = 'Out of Memory'; - break; - case 0xB001 /* AL_VENDOR */: - ret = 'Emscripten'; - break; - case 0xB002 /* AL_VERSION */: - ret = '1.1'; - break; - case 0xB003 /* AL_RENDERER */: - ret = 'WebAudio'; - break; - case 0xB004 /* AL_EXTENSIONS */: - ret = Object.keys(AL.AL_EXTENSIONS).join(' '); - break; - default: - if (AL.currentCtx) { - AL.currentCtx.err = 40962; - } else { - } - return 0; - } - - ret = stringToNewUTF8(ret); - AL.stringCache[param] = ret; - return ret; - }; - _alGetString.sig = 'pi'; - - var _alIsBuffer = (bufferId) => { - if (!AL.currentCtx) { - return false; - } - if (bufferId > AL.buffers.length) { - return false; - } - - if (!AL.buffers[bufferId]) { - return false; - } - return true; - }; - _alIsBuffer.sig = 'ii'; + var FS_isFile = (...args) => FS.isFile(...args); - var _alIsEnabled = (param) => { - if (!AL.currentCtx) { - return 0; - } - switch (param) { - case 0x200 /* AL_SOURCE_DISTANCE_MODEL */: - return AL.currentCtx.sourceDistanceModel ? 0 : 1; - default: - AL.currentCtx.err = 40962; - return 0; - } - }; - _alIsEnabled.sig = 'ii'; - - - var _alIsExtensionPresent = (pExtName) => { - var name = UTF8ToString(pExtName); - - return AL.AL_EXTENSIONS[name] ? 1 : 0; - }; - _alIsExtensionPresent.sig = 'ip'; - - var _alIsSource = (sourceId) => { - if (!AL.currentCtx) { - return false; - } - - if (!AL.currentCtx.sources[sourceId]) { - return false; - } - return true; - }; - _alIsSource.sig = 'ii'; - - var _alListener3f = (param, value0, value1, value2) => { - switch (param) { - case 4100: - case 4102: - AL.paramArray[0] = value0; - AL.paramArray[1] = value1; - AL.paramArray[2] = value2; - AL.setListenerParam('alListener3f', param, AL.paramArray); - break; - default: - AL.setListenerParam('alListener3f', param, null); - break; - } - }; - _alListener3f.sig = 'vifff'; - - var _alListener3i = (param, value0, value1, value2) => { - switch (param) { - case 4100: - case 4102: - AL.paramArray[0] = value0; - AL.paramArray[1] = value1; - AL.paramArray[2] = value2; - AL.setListenerParam('alListener3i', param, AL.paramArray); - break; - default: - AL.setListenerParam('alListener3i', param, null); - break; - } - }; - _alListener3i.sig = 'viiii'; - - var _alListenerf = (param, value) => { - switch (param) { - case 4106: - AL.setListenerParam('alListenerf', param, value); - break; - default: - AL.setListenerParam('alListenerf', param, null); - break; - } - }; - _alListenerf.sig = 'vif'; + var FS_isDir = (...args) => FS.isDir(...args); - var _alListenerfv = (param, pValues) => { - if (!AL.currentCtx) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4100: - case 4102: - AL.paramArray[0] = HEAPF32[((pValues)>>2)]; - AL.paramArray[1] = HEAPF32[(((pValues)+(4))>>2)]; - AL.paramArray[2] = HEAPF32[(((pValues)+(8))>>2)]; - AL.setListenerParam('alListenerfv', param, AL.paramArray); - break; - case 4111: - AL.paramArray[0] = HEAPF32[((pValues)>>2)]; - AL.paramArray[1] = HEAPF32[(((pValues)+(4))>>2)]; - AL.paramArray[2] = HEAPF32[(((pValues)+(8))>>2)]; - AL.paramArray[3] = HEAPF32[(((pValues)+(12))>>2)]; - AL.paramArray[4] = HEAPF32[(((pValues)+(16))>>2)]; - AL.paramArray[5] = HEAPF32[(((pValues)+(20))>>2)]; - AL.setListenerParam('alListenerfv', param, AL.paramArray); - break; - default: - AL.setListenerParam('alListenerfv', param, null); - break; - } - }; - _alListenerfv.sig = 'vip'; + var FS_isLink = (...args) => FS.isLink(...args); - var _alListeneri = (param, value) => { - AL.setListenerParam('alListeneri', param, null); - }; - _alListeneri.sig = 'vii'; + var FS_isChrdev = (...args) => FS.isChrdev(...args); - var _alListeneriv = (param, pValues) => { - if (!AL.currentCtx) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 4100: - case 4102: - AL.paramArray[0] = HEAP32[((pValues)>>2)]; - AL.paramArray[1] = HEAP32[(((pValues)+(4))>>2)]; - AL.paramArray[2] = HEAP32[(((pValues)+(8))>>2)]; - AL.setListenerParam('alListeneriv', param, AL.paramArray); - break; - case 4111: - AL.paramArray[0] = HEAP32[((pValues)>>2)]; - AL.paramArray[1] = HEAP32[(((pValues)+(4))>>2)]; - AL.paramArray[2] = HEAP32[(((pValues)+(8))>>2)]; - AL.paramArray[3] = HEAP32[(((pValues)+(12))>>2)]; - AL.paramArray[4] = HEAP32[(((pValues)+(16))>>2)]; - AL.paramArray[5] = HEAP32[(((pValues)+(20))>>2)]; - AL.setListenerParam('alListeneriv', param, AL.paramArray); - break; - default: - AL.setListenerParam('alListeneriv', param, null); - break; - } - }; - _alListeneriv.sig = 'vip'; - - var _alSource3f = (sourceId, param, value0, value1, value2) => { - switch (param) { - case 4100: - case 4101: - case 4102: - AL.paramArray[0] = value0; - AL.paramArray[1] = value1; - AL.paramArray[2] = value2; - AL.setSourceParam('alSource3f', sourceId, param, AL.paramArray); - break; - default: - AL.setSourceParam('alSource3f', sourceId, param, null); - break; - } - }; - _alSource3f.sig = 'viifff'; - - var _alSource3i = (sourceId, param, value0, value1, value2) => { - switch (param) { - case 4100: - case 4101: - case 4102: - AL.paramArray[0] = value0; - AL.paramArray[1] = value1; - AL.paramArray[2] = value2; - AL.setSourceParam('alSource3i', sourceId, param, AL.paramArray); - break; - default: - AL.setSourceParam('alSource3i', sourceId, param, null); - break; - } - }; - _alSource3i.sig = 'viiiii'; + var FS_isBlkdev = (...args) => FS.isBlkdev(...args); - var _alSourcePause = (sourceId) => { - if (!AL.currentCtx) { - return; - } - var src = AL.currentCtx.sources[sourceId]; - if (!src) { - AL.currentCtx.err = 40961; - return; - } - AL.setSourceState(src, 4115); - }; - _alSourcePause.sig = 'vi'; + var FS_isFIFO = (...args) => FS.isFIFO(...args); - var _alSourcePausev = (count, pSourceIds) => { - if (!AL.currentCtx) { - return; - } - if (!pSourceIds) { - AL.currentCtx.err = 40963; - } - for (var i = 0; i < count; ++i) { - if (!AL.currentCtx.sources[HEAP32[(((pSourceIds)+(i*4))>>2)]]) { - AL.currentCtx.err = 40961; - return; - } - } - - for (var i = 0; i < count; ++i) { - var srcId = HEAP32[(((pSourceIds)+(i*4))>>2)]; - AL.setSourceState(AL.currentCtx.sources[srcId], 4115); - } - }; - _alSourcePausev.sig = 'vip'; + var FS_isSocket = (...args) => FS.isSocket(...args); - var _alSourcePlay = (sourceId) => { - if (!AL.currentCtx) { - return; - } - var src = AL.currentCtx.sources[sourceId]; - if (!src) { - AL.currentCtx.err = 40961; - return; - } - AL.setSourceState(src, 4114); - }; - _alSourcePlay.sig = 'vi'; + var FS_flagsToPermissionString = (...args) => + FS.flagsToPermissionString(...args); - var _alSourcePlayv = (count, pSourceIds) => { - if (!AL.currentCtx) { - return; - } - if (!pSourceIds) { - AL.currentCtx.err = 40963; - } - for (var i = 0; i < count; ++i) { - if (!AL.currentCtx.sources[HEAP32[(((pSourceIds)+(i*4))>>2)]]) { - AL.currentCtx.err = 40961; - return; - } - } - - for (var i = 0; i < count; ++i) { - var srcId = HEAP32[(((pSourceIds)+(i*4))>>2)]; - AL.setSourceState(AL.currentCtx.sources[srcId], 4114); - } - }; - _alSourcePlayv.sig = 'vip'; + var FS_nodePermissions = (...args) => FS.nodePermissions(...args); - var _alSourceQueueBuffers = (sourceId, count, pBufferIds) => { - if (!AL.currentCtx) { - return; - } - var src = AL.currentCtx.sources[sourceId]; - if (!src) { - AL.currentCtx.err = 40961; - return; - } - if (src.type === 4136) { - AL.currentCtx.err = 40964; - return; - } - - if (count === 0) { - return; - } - - // Find the first non-zero buffer in the queue to determine the proper format - var templateBuf = AL.buffers[0]; - for (var buf of src.bufQueue) { - if (buf.id !== 0) { - templateBuf = buf; - break; - } - } - - for (var i = 0; i < count; ++i) { - var bufId = HEAP32[(((pBufferIds)+(i*4))>>2)]; - var buf = AL.buffers[bufId]; - if (!buf) { - AL.currentCtx.err = 40961; - return; - } - - // Check that the added buffer has the correct format. If the template is the zero buffer, any format is valid. - if (templateBuf.id !== 0 && ( - buf.frequency !== templateBuf.frequency - || buf.bytesPerSample !== templateBuf.bytesPerSample - || buf.channels !== templateBuf.channels) - ) { - AL.currentCtx.err = 40964; - } - } - - // If the only buffer in the queue is the zero buffer, clear the queue before we add anything. - if (src.bufQueue.length === 1 && src.bufQueue[0].id === 0) { - src.bufQueue.length = 0; - } - - src.type = 0x1029 /* AL_STREAMING */; - for (var i = 0; i < count; ++i) { - var bufId = HEAP32[(((pBufferIds)+(i*4))>>2)]; - var buf = AL.buffers[bufId]; - buf.refCount++; - src.bufQueue.push(buf); - } - - // if the source is looping, cancel the schedule so we can reschedule the loop order - if (src.looping) { - AL.cancelPendingSourceAudio(src); - } - - AL.initSourcePanner(src); - AL.scheduleSourceAudio(src); - }; - _alSourceQueueBuffers.sig = 'viip'; - - var _alSourceRewind = (sourceId) => { - if (!AL.currentCtx) { - return; - } - var src = AL.currentCtx.sources[sourceId]; - if (!src) { - AL.currentCtx.err = 40961; - return; - } - // Stop the source first to clear the source queue - AL.setSourceState(src, 4116); - // Now set the state of AL_INITIAL according to the specification - AL.setSourceState(src, 4113); - }; - _alSourceRewind.sig = 'vi'; - - var _alSourceRewindv = (count, pSourceIds) => { - if (!AL.currentCtx) { - return; - } - if (!pSourceIds) { - AL.currentCtx.err = 40963; - } - for (var i = 0; i < count; ++i) { - if (!AL.currentCtx.sources[HEAP32[(((pSourceIds)+(i*4))>>2)]]) { - AL.currentCtx.err = 40961; - return; - } - } - - for (var i = 0; i < count; ++i) { - var srcId = HEAP32[(((pSourceIds)+(i*4))>>2)]; - AL.setSourceState(AL.currentCtx.sources[srcId], 4113); - } - }; - _alSourceRewindv.sig = 'vip'; + var FS_mayLookup = (...args) => FS.mayLookup(...args); - var _alSourceStop = (sourceId) => { - if (!AL.currentCtx) { - return; - } - var src = AL.currentCtx.sources[sourceId]; - if (!src) { - AL.currentCtx.err = 40961; - return; - } - AL.setSourceState(src, 4116); - }; - _alSourceStop.sig = 'vi'; + var FS_mayCreate = (...args) => FS.mayCreate(...args); - var _alSourceStopv = (count, pSourceIds) => { - if (!AL.currentCtx) { - return; - } - if (!pSourceIds) { - AL.currentCtx.err = 40963; - } - for (var i = 0; i < count; ++i) { - if (!AL.currentCtx.sources[HEAP32[(((pSourceIds)+(i*4))>>2)]]) { - AL.currentCtx.err = 40961; - return; - } - } - - for (var i = 0; i < count; ++i) { - var srcId = HEAP32[(((pSourceIds)+(i*4))>>2)]; - AL.setSourceState(AL.currentCtx.sources[srcId], 4116); - } - }; - _alSourceStopv.sig = 'vip'; + var FS_mayDelete = (...args) => FS.mayDelete(...args); - var _alSourceUnqueueBuffers = (sourceId, count, pBufferIds) => { - if (!AL.currentCtx) { - return; - } - var src = AL.currentCtx.sources[sourceId]; - if (!src) { - AL.currentCtx.err = 40961; - return; - } - if (count > (src.bufQueue.length === 1 && src.bufQueue[0].id === 0 ? 0 : src.bufsProcessed)) { - AL.currentCtx.err = 40963; - return; - } - - if (count === 0) { - return; - } - - for (var i = 0; i < count; i++) { - var buf = src.bufQueue.shift(); - buf.refCount--; - // Write the buffers index out to the return list. - HEAP32[(((pBufferIds)+(i*4))>>2)] = buf.id; - src.bufsProcessed--; - } - - /// If the queue is empty, put the zero buffer back in - if (src.bufQueue.length === 0) { - src.bufQueue.push(AL.buffers[0]); - } - - AL.initSourcePanner(src); - AL.scheduleSourceAudio(src); - }; - _alSourceUnqueueBuffers.sig = 'viip'; - - var _alSourcef = (sourceId, param, value) => { - switch (param) { - case 0x1001 /* AL_CONE_INNER_ANGLE */: - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - case 0x1003 /* AL_PITCH */: - case 4106: - case 0x100D /* AL_MIN_GAIN */: - case 0x100E /* AL_MAX_GAIN */: - case 0x1020 /* AL_REFERENCE_DISTANCE */: - case 0x1021 /* AL_ROLLOFF_FACTOR */: - case 0x1022 /* AL_CONE_OUTER_GAIN */: - case 0x1023 /* AL_MAX_DISTANCE */: - case 0x1024 /* AL_SEC_OFFSET */: - case 0x1025 /* AL_SAMPLE_OFFSET */: - case 0x1026 /* AL_BYTE_OFFSET */: - case 0x200B /* AL_SEC_LENGTH_SOFT */: - AL.setSourceParam('alSourcef', sourceId, param, value); - break; - default: - AL.setSourceParam('alSourcef', sourceId, param, null); - break; - } - }; - _alSourcef.sig = 'viif'; + var FS_mayOpen = (...args) => FS.mayOpen(...args); - var _alSourcefv = (sourceId, param, pValues) => { - if (!AL.currentCtx) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x1001 /* AL_CONE_INNER_ANGLE */: - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - case 0x1003 /* AL_PITCH */: - case 4106: - case 0x100D /* AL_MIN_GAIN */: - case 0x100E /* AL_MAX_GAIN */: - case 0x1020 /* AL_REFERENCE_DISTANCE */: - case 0x1021 /* AL_ROLLOFF_FACTOR */: - case 0x1022 /* AL_CONE_OUTER_GAIN */: - case 0x1023 /* AL_MAX_DISTANCE */: - case 0x1024 /* AL_SEC_OFFSET */: - case 0x1025 /* AL_SAMPLE_OFFSET */: - case 0x1026 /* AL_BYTE_OFFSET */: - case 0x200B /* AL_SEC_LENGTH_SOFT */: - var val = HEAPF32[((pValues)>>2)]; - AL.setSourceParam('alSourcefv', sourceId, param, val); - break; - case 4100: - case 4101: - case 4102: - AL.paramArray[0] = HEAPF32[((pValues)>>2)]; - AL.paramArray[1] = HEAPF32[(((pValues)+(4))>>2)]; - AL.paramArray[2] = HEAPF32[(((pValues)+(8))>>2)]; - AL.setSourceParam('alSourcefv', sourceId, param, AL.paramArray); - break; - default: - AL.setSourceParam('alSourcefv', sourceId, param, null); - break; - } - }; - _alSourcefv.sig = 'viip'; + var FS_checkOpExists = (...args) => FS.checkOpExists(...args); + var FS_nextfd = (...args) => FS.nextfd(...args); - var _alSourceiv = (sourceId, param, pValues) => { - if (!AL.currentCtx) { - return; - } - if (!pValues) { - AL.currentCtx.err = 40963; - return; - } - - switch (param) { - case 0x202 /* AL_SOURCE_RELATIVE */: - case 0x1001 /* AL_CONE_INNER_ANGLE */: - case 0x1002 /* AL_CONE_OUTER_ANGLE */: - case 0x1007 /* AL_LOOPING */: - case 0x1009 /* AL_BUFFER */: - case 0x1020 /* AL_REFERENCE_DISTANCE */: - case 0x1021 /* AL_ROLLOFF_FACTOR */: - case 0x1023 /* AL_MAX_DISTANCE */: - case 0x1024 /* AL_SEC_OFFSET */: - case 0x1025 /* AL_SAMPLE_OFFSET */: - case 0x1026 /* AL_BYTE_OFFSET */: - case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */: - case 0x2009 /* AL_BYTE_LENGTH_SOFT */: - case 0x200A /* AL_SAMPLE_LENGTH_SOFT */: - case 53248: - var val = HEAP32[((pValues)>>2)]; - AL.setSourceParam('alSourceiv', sourceId, param, val); - break; - case 4100: - case 4101: - case 4102: - AL.paramArray[0] = HEAP32[((pValues)>>2)]; - AL.paramArray[1] = HEAP32[(((pValues)+(4))>>2)]; - AL.paramArray[2] = HEAP32[(((pValues)+(8))>>2)]; - AL.setSourceParam('alSourceiv', sourceId, param, AL.paramArray); - break; - default: - AL.setSourceParam('alSourceiv', sourceId, param, null); - break; - } - }; - _alSourceiv.sig = 'viip'; - - var _alSpeedOfSound = (value) => { - AL.setGlobalParam('alSpeedOfSound', 49155, value); - }; - _alSpeedOfSound.sig = 'vf'; - - var _alcCaptureCloseDevice = (deviceId) => { - var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureCloseDevice'); - if (!c) return false; - - delete AL.captures[deviceId]; - AL.freeIds.push(deviceId); - - // This clean-up might be unnecessary (paranoid) ? - - // May happen if user hasn't decided to grant or deny input - c.mediaStreamSourceNode?.disconnect(); - c.mergerNode?.disconnect(); - c.splitterNode?.disconnect(); - // May happen if user hasn't decided to grant or deny input - c.scriptProcessorNode?.disconnect(); - if (c.mediaStream) { - // Disabling the microphone of the browser. - // Without this operation, the red dot on the browser tab page will remain. - c.mediaStream.getTracks().forEach((track) => track.stop()); - } - - delete c.buffers; - - c.capturedFrameCount = 0; - c.isCapturing = false; - - return true; - }; - _alcCaptureCloseDevice.sig = 'ip'; - - var autoResumeAudioContext = (ctx) => { - for (var event of ['keydown', 'mousedown', 'touchstart']) { - for (var element of [document, document.getElementById('canvas')]) { - element?.addEventListener(event, () => { - if (ctx.state === 'suspended') ctx.resume(); - }, { 'once': true }); - } - } - }; - - - var _alcCaptureOpenDevice = (pDeviceName, requestedSampleRate, format, bufferFrameCapacity) => { - - var resolvedDeviceName = AL.CAPTURE_DEVICE_NAME; - - // NULL is a valid device name here (resolves to default); - if (pDeviceName !== 0) { - resolvedDeviceName = UTF8ToString(pDeviceName); - if (resolvedDeviceName !== AL.CAPTURE_DEVICE_NAME) { - // ALC_OUT_OF_MEMORY - // From the programmer's guide, ALC_OUT_OF_MEMORY's meaning is - // overloaded here, to mean: - // 'The specified device is invalid, or can not capture audio.' - // This may be misleading to API users, but well... - AL.alcErr = 0xA005 /* ALC_OUT_OF_MEMORY */; - return 0; - } - } - - // Otherwise it's probably okay (though useless) for bufferFrameCapacity to be zero. - if (bufferFrameCapacity < 0) { // ALCsizei is signed int - AL.alcErr = 40964; - return 0; - } - - navigator.getUserMedia = navigator.getUserMedia - || navigator.webkitGetUserMedia - || navigator.mozGetUserMedia - || navigator.msGetUserMedia; - var has_getUserMedia = navigator.getUserMedia - || (navigator.mediaDevices - && navigator.mediaDevices.getUserMedia); - - if (!has_getUserMedia) { - // See previously mentioned rationale for ALC_OUT_OF_MEMORY - AL.alcErr = 0xA005 /* ALC_OUT_OF_MEMORY */; - return 0; - } - - var AudioContext = window.AudioContext || window.webkitAudioContext; - - if (!AL.sharedCaptureAudioCtx) { - try { - AL.sharedCaptureAudioCtx = new AudioContext(); - } catch(e) { - // See previously mentioned rationale for ALC_OUT_OF_MEMORY - AL.alcErr = 0xA005 /* ALC_OUT_OF_MEMORY */; - return 0; - } - } - - autoResumeAudioContext(AL.sharedCaptureAudioCtx); - - var outputChannelCount; - - switch (format) { - case 0x10010: /* AL_FORMAT_MONO_FLOAT32 */ - case 0x1101: /* AL_FORMAT_MONO16 */ - case 0x1100: /* AL_FORMAT_MONO8 */ - outputChannelCount = 1; - break; - case 0x10011: /* AL_FORMAT_STEREO_FLOAT32 */ - case 0x1103: /* AL_FORMAT_STEREO16 */ - case 0x1102: /* AL_FORMAT_STEREO8 */ - outputChannelCount = 2; - break; - default: - AL.alcErr = 40964; - return 0; - } - - function newF32Array(cap) { return new Float32Array(cap);} - function newI16Array(cap) { return new Int16Array(cap); } - function newU8Array(cap) { return new Uint8Array(cap); } - - var requestedSampleType; - var newSampleArray; - - switch (format) { - case 0x10010: /* AL_FORMAT_MONO_FLOAT32 */ - case 0x10011: /* AL_FORMAT_STEREO_FLOAT32 */ - requestedSampleType = 'f32'; - newSampleArray = newF32Array; - break; - case 0x1101: /* AL_FORMAT_MONO16 */ - case 0x1103: /* AL_FORMAT_STEREO16 */ - requestedSampleType = 'i16'; - newSampleArray = newI16Array; - break; - case 0x1100: /* AL_FORMAT_MONO8 */ - case 0x1102: /* AL_FORMAT_STEREO8 */ - requestedSampleType = 'u8'; - newSampleArray = newU8Array; - break; - } - - var buffers = []; - try { - for (var chan=0; chan < outputChannelCount; ++chan) { - buffers[chan] = newSampleArray(bufferFrameCapacity); - } - } catch(e) { - AL.alcErr = 0xA005 /* ALC_OUT_OF_MEMORY */; - return 0; - } - - // What we'll place into the `AL.captures` array in the end, - // declared here for closures to access it - var newCapture = { - audioCtx: AL.sharedCaptureAudioCtx, - deviceName: resolvedDeviceName, - requestedSampleRate, - requestedSampleType, - outputChannelCount, - inputChannelCount: null, // Not known until the getUserMedia() promise resolves - mediaStreamError: null, // Used by other functions to return early and report an error. - mediaStreamSourceNode: null, - mediaStream: null, - // Either one, or none of the below two, is active. - mergerNode: null, - splitterNode: null, - scriptProcessorNode: null, - isCapturing: false, - buffers, - get bufferFrameCapacity() { - return buffers[0].length; - }, - capturePlayhead: 0, // current write position, in sample frames - captureReadhead: 0, - capturedFrameCount: 0 - }; - - // Preparing for getUserMedia() - - var onError = (mediaStreamError) => { - newCapture.mediaStreamError = mediaStreamError; - }; - var onSuccess = (mediaStream) => { - newCapture.mediaStreamSourceNode = newCapture.audioCtx.createMediaStreamSource(mediaStream); - newCapture.mediaStream = mediaStream; - - var inputChannelCount = 1; - switch (newCapture.mediaStreamSourceNode.channelCountMode) { - case 'max': - inputChannelCount = outputChannelCount; - break; - case 'clamped-max': - inputChannelCount = Math.min(outputChannelCount, newCapture.mediaStreamSourceNode.channelCount); - break; - case 'explicit': - inputChannelCount = newCapture.mediaStreamSourceNode.channelCount; - break; - } - - newCapture.inputChannelCount = inputChannelCount; - - // Have to pick a size from 256, 512, 1024, 2048, 4096, 8192, 16384. - // One can also set it to zero, which leaves the decision up to the impl. - // An extension could allow specifying this value. - var processorFrameCount = 512; - - newCapture.scriptProcessorNode = newCapture.audioCtx.createScriptProcessor( - processorFrameCount, inputChannelCount, outputChannelCount - ); - - if (inputChannelCount > outputChannelCount) { - newCapture.mergerNode = newCapture.audioCtx.createChannelMerger(inputChannelCount); - newCapture.mediaStreamSourceNode.connect(newCapture.mergerNode); - newCapture.mergerNode.connect(newCapture.scriptProcessorNode); - } else if (inputChannelCount < outputChannelCount) { - newCapture.splitterNode = newCapture.audioCtx.createChannelSplitter(outputChannelCount); - newCapture.mediaStreamSourceNode.connect(newCapture.splitterNode); - newCapture.splitterNode.connect(newCapture.scriptProcessorNode); - } else { - newCapture.mediaStreamSourceNode.connect(newCapture.scriptProcessorNode); - } - - newCapture.scriptProcessorNode.connect(newCapture.audioCtx.destination); - - newCapture.scriptProcessorNode.onaudioprocess = (audioProcessingEvent) => { - if (!newCapture.isCapturing) { - return; - } - - var c = newCapture; - var srcBuf = audioProcessingEvent.inputBuffer; - - // Actually just copy srcBuf's channel data into - // c.buffers, optimizing for each case. - switch (format) { - case 0x10010: /* AL_FORMAT_MONO_FLOAT32 */ - var channel0 = srcBuf.getChannelData(0); - for (var i = 0 ; i < srcBuf.length; ++i) { - var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity; - c.buffers[0][wi] = channel0[i]; - } - break; - case 0x10011: /* AL_FORMAT_STEREO_FLOAT32 */ - var channel0 = srcBuf.getChannelData(0); - var channel1 = srcBuf.getChannelData(1); - for (var i = 0 ; i < srcBuf.length; ++i) { - var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity; - c.buffers[0][wi] = channel0[i]; - c.buffers[1][wi] = channel1[i]; - } - break; - case 0x1101: /* AL_FORMAT_MONO16 */ - var channel0 = srcBuf.getChannelData(0); - for (var i = 0 ; i < srcBuf.length; ++i) { - var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity; - c.buffers[0][wi] = channel0[i] * 32767; - } - break; - case 0x1103: /* AL_FORMAT_STEREO16 */ - var channel0 = srcBuf.getChannelData(0); - var channel1 = srcBuf.getChannelData(1); - for (var i = 0 ; i < srcBuf.length; ++i) { - var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity; - c.buffers[0][wi] = channel0[i] * 32767; - c.buffers[1][wi] = channel1[i] * 32767; - } - break; - case 0x1100: /* AL_FORMAT_MONO8 */ - var channel0 = srcBuf.getChannelData(0); - for (var i = 0 ; i < srcBuf.length; ++i) { - var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity; - c.buffers[0][wi] = (channel0[i] + 1.0) * 127; - } - break; - case 0x1102: /* AL_FORMAT_STEREO8 */ - var channel0 = srcBuf.getChannelData(0); - var channel1 = srcBuf.getChannelData(1); - for (var i = 0 ; i < srcBuf.length; ++i) { - var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity; - c.buffers[0][wi] = (channel0[i] + 1.0) * 127; - c.buffers[1][wi] = (channel1[i] + 1.0) * 127; - } - break; - } - - c.capturePlayhead += srcBuf.length; - c.capturePlayhead %= c.bufferFrameCapacity; - c.capturedFrameCount += srcBuf.length; - c.capturedFrameCount = Math.min(c.capturedFrameCount, c.bufferFrameCapacity); - }; - }; - - // The latest way to call getUserMedia() - if (navigator.mediaDevices?.getUserMedia) { - navigator.mediaDevices - .getUserMedia({audio: true}) - .then(onSuccess) - .catch(onError); - } else { // The usual (now deprecated) way - navigator.getUserMedia({audio: true}, onSuccess, onError); - } - - var id = AL.newId(); - AL.captures[id] = newCapture; - return id; - }; - _alcCaptureOpenDevice.sig = 'ppiii'; - - var _alcCaptureSamples = (deviceId, pFrames, requestedFrameCount) => { - var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureSamples'); - if (!c) return; - - // ALCsizei is actually 32-bit signed int, so could be negative - // Also, spec says : - // Requesting more sample frames than are currently available is - // an error. - - var dstfreq = c.requestedSampleRate; - var srcfreq = c.audioCtx.sampleRate; - - var fratio = srcfreq / dstfreq; - - if (requestedFrameCount < 0 - || requestedFrameCount > (c.capturedFrameCount / fratio)) - { - AL.alcErr = 40964; - return; - } - - function setF32Sample(i, sample) { - HEAPF32[(((pFrames)+(4*i))>>2)] = sample; - } - function setI16Sample(i, sample) { - HEAP16[(((pFrames)+(2*i))>>1)] = sample; - } - function setU8Sample(i, sample) { - HEAP8[(pFrames)+(i)] = sample; - } - - var setSample; - - switch (c.requestedSampleType) { - case 'f32': setSample = setF32Sample; break; - case 'i16': setSample = setI16Sample; break; - case 'u8' : setSample = setU8Sample ; break; - default: - return; - } - - // If fratio is an integer we don't need linear resampling, just skip samples - if (Math.floor(fratio) == fratio) { - for (var i = 0, frame_i = 0; frame_i < requestedFrameCount; ++frame_i) { - for (var chan = 0; chan < c.buffers.length; ++chan, ++i) { - setSample(i, c.buffers[chan][c.captureReadhead]); - } - c.captureReadhead = (fratio + c.captureReadhead) % c.bufferFrameCapacity; - } - } else { - // Perform linear resampling. - - // There is room for improvement - right now we're fine with linear resampling. - // We don't use OfflineAudioContexts for this: See the discussion at - // https://github.com/jpernst/emscripten/issues/2#issuecomment-312729735 - // if you're curious about why. - for (var i = 0, frame_i = 0; frame_i < requestedFrameCount; ++frame_i) { - var lefti = Math.floor(c.captureReadhead); - var righti = Math.ceil(c.captureReadhead); - var d = c.captureReadhead - lefti; - for (var chan = 0; chan < c.buffers.length; ++chan, ++i) { - var lefts = c.buffers[chan][lefti]; - var rights = c.buffers[chan][righti]; - setSample(i, (1 - d) * lefts + d * rights); - } - c.captureReadhead = (c.captureReadhead + fratio) % c.bufferFrameCapacity; - } - } - - // Spec doesn't say if alcCaptureSamples() must zero the number - // of available captured sample-frames, but not only would it - // be insane not to do, OpenAL-Soft happens to do that as well. - c.capturedFrameCount = 0; - }; - _alcCaptureSamples.sig = 'vppi'; - - var _alcCaptureStart = (deviceId) => { - var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureStart'); - if (!c) return; - - if (c.isCapturing) { - // NOTE: Spec says (emphasis mine): - // The amount of audio samples available after **restarting** a - // stopped capture device is reset to zero. - // So redundant calls to alcCaptureStart() must have no effect. - return; - } - c.isCapturing = true; - c.capturedFrameCount = 0; - c.capturePlayhead = 0; - }; - _alcCaptureStart.sig = 'vp'; - - var _alcCaptureStop = (deviceId) => { - var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureStop'); - if (!c) return; - - c.isCapturing = false; - }; - _alcCaptureStop.sig = 'vp'; - - var _alcCloseDevice = (deviceId) => { - if (!(deviceId in AL.deviceRefCounts) || AL.deviceRefCounts[deviceId] > 0) { - return 0; - } - - delete AL.deviceRefCounts[deviceId]; - AL.freeIds.push(deviceId); - return 1; - }; - _alcCloseDevice.sig = 'ip'; - - - var _alcCreateContext = (deviceId, pAttrList) => { - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 0xA001; /* ALC_INVALID_DEVICE */ - return 0; - } - - var options = null; - var attrs = []; - var hrtf = null; - pAttrList >>= 2; - if (pAttrList) { - var attr = 0; - var val = 0; - while (true) { - attr = HEAP32[pAttrList++]; - attrs.push(attr); - if (attr === 0) { - break; - } - val = HEAP32[pAttrList++]; - attrs.push(val); - - switch (attr) { - case 0x1007 /* ALC_FREQUENCY */: - if (!options) { - options = {}; - } - - options.sampleRate = val; - break; - case 0x1010 /* ALC_MONO_SOURCES */: // fallthrough - case 0x1011 /* ALC_STEREO_SOURCES */: - // Do nothing; these hints are satisfied by default - break - case 0x1992 /* ALC_HRTF_SOFT */: - switch (val) { - case 0: - hrtf = false; - break; - case 1: - hrtf = true; - break; - case 2 /* ALC_DONT_CARE_SOFT */: - break; - default: - AL.alcErr = 40964; - return 0; - } - break; - case 0x1996 /* ALC_HRTF_ID_SOFT */: - if (val !== 0) { - AL.alcErr = 40964; - return 0; - } - break; - default: - AL.alcErr = 0xA004; /* ALC_INVALID_VALUE */ - return 0; - } - } - } - - var AudioContext = window.AudioContext || window.webkitAudioContext; - var ac = null; - try { - // Only try to pass options if there are any, for compat with browsers that don't support this - if (options) { - ac = new AudioContext(options); - } else { - ac = new AudioContext(); - } - } catch (e) { - if (e.name === 'NotSupportedError') { - AL.alcErr = 0xA004; /* ALC_INVALID_VALUE */ - } else { - AL.alcErr = 0xA001; /* ALC_INVALID_DEVICE */ - } - - return 0; - } - - autoResumeAudioContext(ac); - - // Old Web Audio API (e.g. Safari 6.0.5) had an inconsistently named createGainNode function. - if (typeof ac.createGain == 'undefined') { - ac.createGain = ac.createGainNode; - } - - var gain = ac.createGain(); - gain.connect(ac.destination); - var ctx = { - deviceId, - id: AL.newId(), - attrs, - audioCtx: ac, - listener: { - position: [0.0, 0.0, 0.0], - velocity: [0.0, 0.0, 0.0], - direction: [0.0, 0.0, 0.0], - up: [0.0, 0.0, 0.0] - }, - sources: [], - interval: setInterval(() => AL.scheduleContextAudio(ctx), AL.QUEUE_INTERVAL), - gain, - distanceModel: 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */, - speedOfSound: 343.3, - dopplerFactor: 1.0, - sourceDistanceModel: false, - hrtf: hrtf || false, - - _err: 0, - get err() { - return this._err; - }, - set err(val) { - // Errors should not be overwritten by later errors until they are cleared by a query. - if (this._err === 0 || val === 0) { - this._err = val; - } - } - }; - AL.deviceRefCounts[deviceId]++; - AL.contexts[ctx.id] = ctx; - - if (hrtf !== null) { - // Apply hrtf attrib to all contexts for this device - for (var ctxId in AL.contexts) { - var c = AL.contexts[ctxId]; - if (c.deviceId === deviceId) { - c.hrtf = hrtf; - AL.updateContextGlobal(c); - } - } - } - - return ctx.id; - }; - _alcCreateContext.sig = 'ppp'; - - var _alcDestroyContext = (contextId) => { - var ctx = AL.contexts[contextId]; - if (AL.currentCtx === ctx) { - AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; - return; - } - - // Stop playback, etc - if (AL.contexts[contextId].interval) { - clearInterval(AL.contexts[contextId].interval); - } - AL.deviceRefCounts[ctx.deviceId]--; - delete AL.contexts[contextId]; - AL.freeIds.push(contextId); - }; - _alcDestroyContext.sig = 'vp'; - - var _alcGetContextsDevice = (contextId) => { - if (contextId in AL.contexts) { - return AL.contexts[contextId].deviceId; - } - return 0; - }; - _alcGetContextsDevice.sig = 'pp'; + var FS_getStreamChecked = (...args) => FS.getStreamChecked(...args); - var _alcGetCurrentContext = () => { - if (AL.currentCtx !== null) { - return AL.currentCtx.id; - } - return 0; - }; - _alcGetCurrentContext.sig = 'p'; - - - var _alcGetEnumValue = (deviceId, pEnumName) => { - // Spec says : - // Using a NULL handle is legal, but only the - // tokens defined by the AL core are guaranteed. - if (deviceId !== 0 && !(deviceId in AL.deviceRefCounts)) { - // ALC_INVALID_DEVICE is not listed as a possible error state for - // this function, sadly. - return 0; - } else if (!pEnumName) { - AL.alcErr = 40964; - return 0; - } - var name = UTF8ToString(pEnumName); - // See alGetEnumValue(), but basically behave the same as OpenAL-Soft - switch (name) { - case 'ALC_NO_ERROR': return 0; - case 'ALC_INVALID_DEVICE': return 0xA001; - case 'ALC_INVALID_CONTEXT': return 0xA002; - case 'ALC_INVALID_ENUM': return 0xA003; - case 'ALC_INVALID_VALUE': return 0xA004; - case 'ALC_OUT_OF_MEMORY': return 0xA005; - case 'ALC_MAJOR_VERSION': return 0x1000; - case 'ALC_MINOR_VERSION': return 0x1001; - case 'ALC_ATTRIBUTES_SIZE': return 0x1002; - case 'ALC_ALL_ATTRIBUTES': return 0x1003; - case 'ALC_DEFAULT_DEVICE_SPECIFIER': return 0x1004; - case 'ALC_DEVICE_SPECIFIER': return 0x1005; - case 'ALC_EXTENSIONS': return 0x1006; - case 'ALC_FREQUENCY': return 0x1007; - case 'ALC_REFRESH': return 0x1008; - case 'ALC_SYNC': return 0x1009; - case 'ALC_MONO_SOURCES': return 0x1010; - case 'ALC_STEREO_SOURCES': return 0x1011; - case 'ALC_CAPTURE_DEVICE_SPECIFIER': return 0x310; - case 'ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER': return 0x311; - case 'ALC_CAPTURE_SAMPLES': return 0x312; - - /* Extensions */ - case 'ALC_HRTF_SOFT': return 0x1992; - case 'ALC_HRTF_ID_SOFT': return 0x1996; - case 'ALC_DONT_CARE_SOFT': return 0x0002; - case 'ALC_HRTF_STATUS_SOFT': return 0x1993; - case 'ALC_NUM_HRTF_SPECIFIERS_SOFT': return 0x1994; - case 'ALC_HRTF_SPECIFIER_SOFT': return 0x1995; - case 'ALC_HRTF_DISABLED_SOFT': return 0x0000; - case 'ALC_HRTF_ENABLED_SOFT': return 0x0001; - case 'ALC_HRTF_DENIED_SOFT': return 0x0002; - case 'ALC_HRTF_REQUIRED_SOFT': return 0x0003; - case 'ALC_HRTF_HEADPHONES_DETECTED_SOFT': return 0x0004; - case 'ALC_HRTF_UNSUPPORTED_FORMAT_SOFT': return 0x0005; - - default: - AL.alcErr = 40964; - return 0; - } - }; - _alcGetEnumValue.sig = 'ipp'; - - var _alcGetError = (deviceId) => { - var err = AL.alcErr; - AL.alcErr = 0; - return err; - }; - _alcGetError.sig = 'ip'; - - var _alcGetIntegerv = (deviceId, param, size, pValues) => { - if (size === 0 || !pValues) { - // Ignore the query, per the spec - return; - } - - switch (param) { - case 0x1000 /* ALC_MAJOR_VERSION */: - HEAP32[((pValues)>>2)] = 1; - break; - case 0x1001 /* ALC_MINOR_VERSION */: - HEAP32[((pValues)>>2)] = 1; - break; - case 0x1002 /* ALC_ATTRIBUTES_SIZE */: - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - if (!AL.currentCtx) { - AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; - return; - } - - HEAP32[((pValues)>>2)] = AL.currentCtx.attrs.length; - break; - case 0x1003 /* ALC_ALL_ATTRIBUTES */: - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - if (!AL.currentCtx) { - AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; - return; - } - - for (var i = 0; i < AL.currentCtx.attrs.length; i++) { - HEAP32[(((pValues)+(i*4))>>2)] = AL.currentCtx.attrs[i]; - } - break; - case 0x1007 /* ALC_FREQUENCY */: - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - if (!AL.currentCtx) { - AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; - return; - } - - HEAP32[((pValues)>>2)] = AL.currentCtx.audioCtx.sampleRate; - break; - case 0x1010 /* ALC_MONO_SOURCES */: - case 0x1011 /* ALC_STEREO_SOURCES */: - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - if (!AL.currentCtx) { - AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; - return; - } - - HEAP32[((pValues)>>2)] = 0x7FFFFFFF; - break; - case 0x1992 /* ALC_HRTF_SOFT */: - case 0x1993 /* ALC_HRTF_STATUS_SOFT */: - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - - var hrtfStatus = 0 /* ALC_HRTF_DISABLED_SOFT */; - for (var ctxId in AL.contexts) { - var ctx = AL.contexts[ctxId]; - if (ctx.deviceId === deviceId) { - hrtfStatus = ctx.hrtf ? 1 /* ALC_HRTF_ENABLED_SOFT */ : 0 /* ALC_HRTF_DISABLED_SOFT */; - } - } - HEAP32[((pValues)>>2)] = hrtfStatus; - break; - case 0x1994 /* ALC_NUM_HRTF_SPECIFIERS_SOFT */: - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - HEAP32[((pValues)>>2)] = 1; - break; - case 0x20003 /* ALC_MAX_AUXILIARY_SENDS */: - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - if (!AL.currentCtx) { - AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */; - return; - } - - HEAP32[((pValues)>>2)] = 1; - case 0x312 /* ALC_CAPTURE_SAMPLES */: - var c = AL.requireValidCaptureDevice(deviceId, 'alcGetIntegerv'); - if (!c) { - return; - } - var n = c.capturedFrameCount; - var dstfreq = c.requestedSampleRate; - var srcfreq = c.audioCtx.sampleRate; - var nsamples = Math.floor(n * (dstfreq/srcfreq)); - HEAP32[((pValues)>>2)] = nsamples; - break; - default: - AL.alcErr = 40963; - return; - } - }; - _alcGetIntegerv.sig = 'vpiip'; + var FS_getStream = (...args) => FS.getStream(...args); - - var _alcGetString = (deviceId, param) => { - if (AL.alcStringCache[param]) { - return AL.alcStringCache[param]; - } - - var ret; - switch (param) { - case 0: - ret = 'No Error'; - break; - case 40961: - ret = 'Invalid Device'; - break; - case 0xA002 /* ALC_INVALID_CONTEXT */: - ret = 'Invalid Context'; - break; - case 40963: - ret = 'Invalid Enum'; - break; - case 40964: - ret = 'Invalid Value'; - break; - case 0xA005 /* ALC_OUT_OF_MEMORY */: - ret = 'Out of Memory'; - break; - case 0x1004 /* ALC_DEFAULT_DEVICE_SPECIFIER */: - if (globalThis.AudioContext || globalThis.webkitAudioContext) { - ret = AL.DEVICE_NAME; - } else { - return 0; - } - break; - case 0x1005 /* ALC_DEVICE_SPECIFIER */: - if (globalThis.AudioContext || globalThis.webkitAudioContext) { - ret = AL.DEVICE_NAME + '\0'; - } else { - ret = '\0'; - } - break; - case 0x311 /* ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER */: - ret = AL.CAPTURE_DEVICE_NAME; - break; - case 0x310 /* ALC_CAPTURE_DEVICE_SPECIFIER */: - if (deviceId === 0) { - ret = AL.CAPTURE_DEVICE_NAME + '\0'; - } else { - var c = AL.requireValidCaptureDevice(deviceId, 'alcGetString'); - if (!c) { - return 0; - } - ret = c.deviceName; - } - break; - case 0x1006 /* ALC_EXTENSIONS */: - if (!deviceId) { - AL.alcErr = 40961; - return 0; - } - - ret = Object.keys(AL.ALC_EXTENSIONS).join(' ') - break; - default: - AL.alcErr = 40963; - return 0; - } - - ret = stringToNewUTF8(ret); - AL.alcStringCache[param] = ret; - return ret; - }; - _alcGetString.sig = 'ppi'; - - - var _alcIsExtensionPresent = (deviceId, pExtName) => { - var name = UTF8ToString(pExtName); - - return AL.ALC_EXTENSIONS[name] ? 1 : 0; - }; - _alcIsExtensionPresent.sig = 'ipp'; - - var _alcMakeContextCurrent = (contextId) => { - if (contextId === 0) { - AL.currentCtx = null; - } else { - AL.currentCtx = AL.contexts[contextId]; - } - return 1; - }; - _alcMakeContextCurrent.sig = 'ip'; - - - var _alcOpenDevice = (pDeviceName) => { - if (pDeviceName) { - var name = UTF8ToString(pDeviceName); - if (name !== AL.DEVICE_NAME) { - return 0; - } - } - - if (globalThis.AudioContext || globalThis.webkitAudioContext) { - var deviceId = AL.newId(); - AL.deviceRefCounts[deviceId] = 0; - return deviceId; - } - return 0; - }; - _alcOpenDevice.sig = 'pp'; + var FS_createStream = (...args) => FS.createStream(...args); - var _alcProcessContext = (contextId) => {}; - _alcProcessContext.sig = 'vp'; + var FS_closeStream = (...args) => FS.closeStream(...args); - var _alcSuspendContext = (contextId) => {}; - _alcSuspendContext.sig = 'vp'; + var FS_dupStream = (...args) => FS.dupStream(...args); - - var _emscripten_get_now_res = () => { // return resolution of get_now, in nanoseconds - if (ENVIRONMENT_IS_NODE) { - return 1; // nanoseconds - } - // Modern environment where performance.now() is supported: - return 1000; // microseconds (1/1000 of a millisecond) - }; - _emscripten_get_now_res.sig = 'd'; - - var nowIsMonotonic = 1; - - var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; - var _clock_res_get = (clk_id, pres) => { - if (!checkWasiClock(clk_id)) { - return 28; - } - var nsec; - // all wasi clocks but realtime are monotonic - if (clk_id === 0) { - nsec = 1000 * 1000; // educated guess that it's milliseconds - } else if (nowIsMonotonic) { - nsec = _emscripten_get_now_res(); - } else { - return 52; - } - HEAP64[((pres)>>3)] = BigInt(nsec); - return 0; - }; - _clock_res_get.sig = 'iip'; - - - var _emscripten_date_now = () => Date.now(); - _emscripten_date_now.sig = 'd'; - - - - 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 * 1000 * 1000); - HEAP64[((ptime)>>3)] = BigInt(nsec); - return 0; - ; - } - _clock_time_get.sig = 'iijp'; - - var _emscripten_alcDevicePauseSOFT = (deviceId) => { - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - - if (AL.paused) { - return; - } - AL.paused = true; - - for (var ctxId in AL.contexts) { - var ctx = AL.contexts[ctxId]; - if (ctx.deviceId !== deviceId) { - continue; - } - - ctx.audioCtx.suspend(); - clearInterval(ctx.interval); - ctx.interval = null; - } - }; - _emscripten_alcDevicePauseSOFT.sig = 'vi'; + var FS_doSetAttr = (...args) => FS.doSetAttr(...args); - var _emscripten_alcDeviceResumeSOFT = (deviceId) => { - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return; - } - - if (!AL.paused) { - return; - } - AL.paused = false; - - for (var ctxId in AL.contexts) { - var ctx = AL.contexts[ctxId]; - if (ctx.deviceId !== deviceId) { - continue; - } - - ctx.interval = setInterval(() => AL.scheduleContextAudio(ctx), AL.QUEUE_INTERVAL); - ctx.audioCtx.resume(); - } - }; - _emscripten_alcDeviceResumeSOFT.sig = 'vi'; - - - - var _emscripten_alcGetStringiSOFT = (deviceId, param, index) => { - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return 0; - } - - if (AL.alcStringCache[param]) { - return AL.alcStringCache[param]; - } - - var ret; - switch (param) { - case 0x1995 /* ALC_HRTF_SPECIFIER_SOFT */: - if (index === 0) { - ret = 'Web Audio HRTF'; - } else { - AL.alcErr = 40964; - return 0; - } - break; - default: - if (index !== 0) { - AL.alcErr = 40963; - return 0; - } - return _alcGetString(deviceId, param); - } - - ret = stringToNewUTF8(ret); - AL.alcStringCache[param] = ret; - return ret; - }; - _emscripten_alcGetStringiSOFT.sig = 'iiii'; - - var _emscripten_alcResetDeviceSOFT = (deviceId, pAttrList) => { - if (!(deviceId in AL.deviceRefCounts)) { - AL.alcErr = 40961; - return 0; - } - - var hrtf = null; - pAttrList >>= 2; - if (pAttrList) { - var attr = 0; - var val = 0; - while (true) { - attr = HEAP32[pAttrList++]; - if (attr === 0) { - break; - } - val = HEAP32[pAttrList++]; - - switch (attr) { - case 0x1992 /* ALC_HRTF_SOFT */: - if (val === 1) { - hrtf = true; - } else if (val === 0) { - hrtf = false; - } - break; - } - } - } - - if (hrtf !== null) { - // Apply hrtf attrib to all contexts for this device - for (var ctxId in AL.contexts) { - var ctx = AL.contexts[ctxId]; - if (ctx.deviceId === deviceId) { - ctx.hrtf = hrtf; - AL.updateContextGlobal(ctx); - } - } - } - - return 1; - }; - _emscripten_alcResetDeviceSOFT.sig = 'iii'; - - var readEmAsmArgsArray = []; - var readEmAsmArgs = (sigPtr, buf) => { - readEmAsmArgsArray.length = 0; - var ch; - // Most arguments are i32s, so shift the buffer pointer so it is a plain - // index into HEAP32. - while (ch = HEAPU8[sigPtr++]) { - // 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 ? HEAPU32[((buf)>>2)] : - ch == 106 ? HEAP64[((buf)>>3)] : - ch == 105 ? - HEAP32[((buf)>>2)] : - HEAPF64[((buf)>>3)] - ); - buf += wide ? 8 : 4; - } - return readEmAsmArgsArray; - }; - var runEmAsmFunction = (code, sigPtr, argbuf) => { - var args = readEmAsmArgs(sigPtr, argbuf); - return ASM_CONSTS[code](...args); - }; - var _emscripten_asm_const_int = (code, sigPtr, argbuf) => { - return runEmAsmFunction(code, sigPtr, argbuf); - }; - _emscripten_asm_const_int.sig = 'ippp'; - - var _emscripten_console_error = (str) => { - console.error(UTF8ToString(str)); - }; - _emscripten_console_error.sig = 'vp'; - - var _emscripten_console_log = (str) => { - console.log(UTF8ToString(str)); - }; - _emscripten_console_log.sig = 'vp'; - - var _emscripten_console_trace = (str) => { - console.trace(UTF8ToString(str)); - }; - _emscripten_console_trace.sig = 'vp'; - - var _emscripten_console_warn = (str) => { - console.warn(UTF8ToString(str)); - }; - _emscripten_console_warn.sig = 'vp'; - - - var _emscripten_err = (str) => err(UTF8ToString(str)); - _emscripten_err.sig = 'vp'; - - 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(); - _emscripten_get_heap_max.sig = 'p'; - - - 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_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 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:[], - stringCache:{ - }, - 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(0x502 /* GL_INVALID_OPERATION */); - } - HEAP32[(((buffers)+(i*4))>>2)] = id; - } - }, - getSource:(shader, count, string, length) => { - var source = ''; - for (var i = 0; i < count; ++i) { - var len = length ? HEAPU32[(((length)+(i*4))>>2)] : undefined; - source += UTF8ToString(HEAPU32[(((string)+(i*4))>>2)], len); - } - return source; - }, - createContext:(/** @type {HTMLCanvasElement} */ canvas, webGLContextAttributes) => { - - var ctx = - canvas.getContext("webgl", webGLContextAttributes); - - if (!ctx) return 0; - - var handle = GL.registerContext(ctx, webGLContextAttributes); - - return handle; - }, - registerContext:(ctx, webGLContextAttributes) => { - // without pthreads a context is just an integer ID - var handle = GL.getNewId(GL.contexts); - - 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) => { - return 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; - } - 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); - { - GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); - } - - for (var ext of getEmscriptenSupportedExtensions(GLctx)) { - // 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 _emscripten_glActiveTexture = (x0) => GLctx.activeTexture(x0); - _emscripten_glActiveTexture.sig = 'vi'; - - var _emscripten_glAttachShader = (program, shader) => { - GLctx.attachShader(GL.programs[program], GL.shaders[shader]); - }; - _emscripten_glAttachShader.sig = 'vii'; - - var _emscripten_glBeginQueryEXT = (target, id) => { - GLctx.disjointTimerQueryExt['beginQueryEXT'](target, GL.queries[id]); - }; - _emscripten_glBeginQueryEXT.sig = 'vii'; - - - var _emscripten_glBindAttribLocation = (program, index, name) => { - GLctx.bindAttribLocation(GL.programs[program], index, UTF8ToString(name)); - }; - _emscripten_glBindAttribLocation.sig = 'viip'; - - var _emscripten_glBindBuffer = (target, buffer) => { - - GLctx.bindBuffer(target, GL.buffers[buffer]); - }; - _emscripten_glBindBuffer.sig = 'vii'; - - var _emscripten_glBindFramebuffer = (target, framebuffer) => { - - GLctx.bindFramebuffer(target, GL.framebuffers[framebuffer]); - - }; - _emscripten_glBindFramebuffer.sig = 'vii'; - - var _emscripten_glBindRenderbuffer = (target, renderbuffer) => { - GLctx.bindRenderbuffer(target, GL.renderbuffers[renderbuffer]); - }; - _emscripten_glBindRenderbuffer.sig = 'vii'; - - var _emscripten_glBindTexture = (target, texture) => { - GLctx.bindTexture(target, GL.textures[texture]); - }; - _emscripten_glBindTexture.sig = 'vii'; - - - var _emscripten_glBindVertexArray = (vao) => { - GLctx.bindVertexArray(GL.vaos[vao]); - }; - _emscripten_glBindVertexArray.sig = 'vi'; - var _emscripten_glBindVertexArrayOES = _emscripten_glBindVertexArray; - _emscripten_glBindVertexArrayOES.sig = 'vi'; - - var _emscripten_glBlendColor = (x0, x1, x2, x3) => GLctx.blendColor(x0, x1, x2, x3); - _emscripten_glBlendColor.sig = 'vffff'; - - var _emscripten_glBlendEquation = (x0) => GLctx.blendEquation(x0); - _emscripten_glBlendEquation.sig = 'vi'; - - var _emscripten_glBlendEquationSeparate = (x0, x1) => GLctx.blendEquationSeparate(x0, x1); - _emscripten_glBlendEquationSeparate.sig = 'vii'; - - var _emscripten_glBlendFunc = (x0, x1) => GLctx.blendFunc(x0, x1); - _emscripten_glBlendFunc.sig = 'vii'; - - var _emscripten_glBlendFuncSeparate = (x0, x1, x2, x3) => GLctx.blendFuncSeparate(x0, x1, x2, x3); - _emscripten_glBlendFuncSeparate.sig = 'viiii'; - - var _emscripten_glBufferData = (target, size, data, usage) => { - - // 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 ? HEAPU8.subarray(data, data+size) : size, usage); - }; - _emscripten_glBufferData.sig = 'vippi'; - - var _emscripten_glBufferSubData = (target, offset, size, data) => { - GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size)); - }; - _emscripten_glBufferSubData.sig = 'vippp'; - - var _emscripten_glCheckFramebufferStatus = (x0) => GLctx.checkFramebufferStatus(x0); - _emscripten_glCheckFramebufferStatus.sig = 'ii'; - - var _emscripten_glClear = (x0) => GLctx.clear(x0); - _emscripten_glClear.sig = 'vi'; - - var _emscripten_glClearColor = (x0, x1, x2, x3) => GLctx.clearColor(x0, x1, x2, x3); - _emscripten_glClearColor.sig = 'vffff'; - - var _emscripten_glClearDepthf = (x0) => GLctx.clearDepth(x0); - _emscripten_glClearDepthf.sig = 'vf'; - - var _emscripten_glClearStencil = (x0) => GLctx.clearStencil(x0); - _emscripten_glClearStencil.sig = 'vi'; - - var _emscripten_glClipControlEXT = (origin, depth) => { - GLctx.extClipControl['clipControlEXT'](origin, depth); - }; - _emscripten_glClipControlEXT.sig = 'vii'; - - var _emscripten_glColorMask = (red, green, blue, alpha) => { - GLctx.colorMask(!!red, !!green, !!blue, !!alpha); - }; - _emscripten_glColorMask.sig = 'viiii'; - - var _emscripten_glCompileShader = (shader) => { - GLctx.compileShader(GL.shaders[shader]); - }; - _emscripten_glCompileShader.sig = 'vi'; - - var _emscripten_glCompressedTexImage2D = (target, level, internalFormat, width, height, border, imageSize, data) => { - // `data` may be null here, which means "allocate uniniitalized space but - // don't upload" in GLES parlance, but `compressedTexImage2D` requires the - // final data parameter, so we simply pass a heap view starting at zero - // effectively uploading whatever happens to be near address zero. See - // https://github.com/emscripten-core/emscripten/issues/19300. - GLctx.compressedTexImage2D(target, level, internalFormat, width, height, border, HEAPU8.subarray((data), data+imageSize)); - }; - _emscripten_glCompressedTexImage2D.sig = 'viiiiiiip'; - - var _emscripten_glCompressedTexSubImage2D = (target, level, xoffset, yoffset, width, height, format, imageSize, data) => { - GLctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, HEAPU8.subarray((data), data+imageSize)); - }; - _emscripten_glCompressedTexSubImage2D.sig = 'viiiiiiiip'; - - var _emscripten_glCopyTexImage2D = (x0, x1, x2, x3, x4, x5, x6, x7) => GLctx.copyTexImage2D(x0, x1, x2, x3, x4, x5, x6, x7); - _emscripten_glCopyTexImage2D.sig = 'viiiiiiii'; - - var _emscripten_glCopyTexSubImage2D = (x0, x1, x2, x3, x4, x5, x6, x7) => GLctx.copyTexSubImage2D(x0, x1, x2, x3, x4, x5, x6, x7); - _emscripten_glCopyTexSubImage2D.sig = 'viiiiiiii'; - - var _emscripten_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; - }; - _emscripten_glCreateProgram.sig = 'i'; - - var _emscripten_glCreateShader = (shaderType) => { - var id = GL.getNewId(GL.shaders); - GL.shaders[id] = GLctx.createShader(shaderType); - - return id; - }; - _emscripten_glCreateShader.sig = 'ii'; - - var _emscripten_glCullFace = (x0) => GLctx.cullFace(x0); - _emscripten_glCullFace.sig = 'vi'; - - var _emscripten_glDeleteBuffers = (n, buffers) => { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((buffers)+(i*4))>>2)]; - var buffer = GL.buffers[id]; - - // From spec: "glDeleteBuffers silently ignores 0's and names that do not - // correspond to existing buffer objects." - if (!buffer) continue; - - GLctx.deleteBuffer(buffer); - buffer.name = 0; - GL.buffers[id] = null; - - } - }; - _emscripten_glDeleteBuffers.sig = 'vip'; - - var _emscripten_glDeleteFramebuffers = (n, framebuffers) => { - for (var i = 0; i < n; ++i) { - var id = HEAP32[(((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; - } - }; - _emscripten_glDeleteFramebuffers.sig = 'vip'; - - var _emscripten_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(0x501 /* GL_INVALID_VALUE */); - return; - } - GLctx.deleteProgram(program); - program.name = 0; - GL.programs[id] = null; - }; - _emscripten_glDeleteProgram.sig = 'vi'; - - var _emscripten_glDeleteQueriesEXT = (n, ids) => { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((ids)+(i*4))>>2)]; - var query = GL.queries[id]; - if (!query) continue; // GL spec: "unused names in ids are ignored, as is the name zero." - GLctx.disjointTimerQueryExt['deleteQueryEXT'](query); - GL.queries[id] = null; - } - }; - _emscripten_glDeleteQueriesEXT.sig = 'vip'; - - var _emscripten_glDeleteRenderbuffers = (n, renderbuffers) => { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((renderbuffers)+(i*4))>>2)]; - var renderbuffer = GL.renderbuffers[id]; - if (!renderbuffer) continue; // GL spec: "glDeleteRenderbuffers silently ignores 0s and names that do not correspond to existing renderbuffer objects". - GLctx.deleteRenderbuffer(renderbuffer); - renderbuffer.name = 0; - GL.renderbuffers[id] = null; - } - }; - _emscripten_glDeleteRenderbuffers.sig = 'vip'; - - var _emscripten_glDeleteShader = (id) => { - if (!id) return; - var shader = GL.shaders[id]; - if (!shader) { - // glDeleteShader actually signals an error when deleting a nonexisting - // object, unlike some other GL delete functions. - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - GLctx.deleteShader(shader); - GL.shaders[id] = null; - }; - _emscripten_glDeleteShader.sig = 'vi'; - - var _emscripten_glDeleteTextures = (n, textures) => { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((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; - } - }; - _emscripten_glDeleteTextures.sig = 'vip'; - - - var _emscripten_glDeleteVertexArrays = (n, vaos) => { - for (var i = 0; i < n; i++) { - var id = HEAP32[(((vaos)+(i*4))>>2)]; - GLctx.deleteVertexArray(GL.vaos[id]); - GL.vaos[id] = null; - } - }; - _emscripten_glDeleteVertexArrays.sig = 'vip'; - var _emscripten_glDeleteVertexArraysOES = _emscripten_glDeleteVertexArrays; - _emscripten_glDeleteVertexArraysOES.sig = 'vip'; - - var _emscripten_glDepthFunc = (x0) => GLctx.depthFunc(x0); - _emscripten_glDepthFunc.sig = 'vi'; - - var _emscripten_glDepthMask = (flag) => { - GLctx.depthMask(!!flag); - }; - _emscripten_glDepthMask.sig = 'vi'; - - var _emscripten_glDepthRangef = (x0, x1) => GLctx.depthRange(x0, x1); - _emscripten_glDepthRangef.sig = 'vff'; - - var _emscripten_glDetachShader = (program, shader) => { - GLctx.detachShader(GL.programs[program], GL.shaders[shader]); - }; - _emscripten_glDetachShader.sig = 'vii'; - - var _emscripten_glDisable = (x0) => GLctx.disable(x0); - _emscripten_glDisable.sig = 'vi'; - - var _emscripten_glDisableVertexAttribArray = (index) => { - GLctx.disableVertexAttribArray(index); - }; - _emscripten_glDisableVertexAttribArray.sig = 'vi'; - - var _emscripten_glDrawArrays = (mode, first, count) => { - - GLctx.drawArrays(mode, first, count); - - }; - _emscripten_glDrawArrays.sig = 'viii'; - - - var _emscripten_glDrawArraysInstanced = (mode, first, count, primcount) => { - GLctx.drawArraysInstanced(mode, first, count, primcount); - }; - _emscripten_glDrawArraysInstanced.sig = 'viiii'; - var _emscripten_glDrawArraysInstancedANGLE = _emscripten_glDrawArraysInstanced; - - - var tempFixedLengthArray = []; - - var _emscripten_glDrawBuffers = (n, bufs) => { - - var bufArray = tempFixedLengthArray[n]; - for (var i = 0; i < n; i++) { - bufArray[i] = HEAP32[(((bufs)+(i*4))>>2)]; - } - - GLctx.drawBuffers(bufArray); - }; - _emscripten_glDrawBuffers.sig = 'vip'; - var _emscripten_glDrawBuffersWEBGL = _emscripten_glDrawBuffers; - - var _emscripten_glDrawElements = (mode, count, type, indices) => { - - GLctx.drawElements(mode, count, type, indices); - - }; - _emscripten_glDrawElements.sig = 'viiip'; - - - var _emscripten_glDrawElementsInstanced = (mode, count, type, indices, primcount) => { - GLctx.drawElementsInstanced(mode, count, type, indices, primcount); - }; - _emscripten_glDrawElementsInstanced.sig = 'viiipi'; - var _emscripten_glDrawElementsInstancedANGLE = _emscripten_glDrawElementsInstanced; - - var _emscripten_glEnable = (x0) => GLctx.enable(x0); - _emscripten_glEnable.sig = 'vi'; - - var _emscripten_glEnableVertexAttribArray = (index) => { - GLctx.enableVertexAttribArray(index); - }; - _emscripten_glEnableVertexAttribArray.sig = 'vi'; - - var _emscripten_glEndQueryEXT = (target) => { - GLctx.disjointTimerQueryExt['endQueryEXT'](target); - }; - _emscripten_glEndQueryEXT.sig = 'vi'; - - var _emscripten_glFinish = () => GLctx.finish(); - _emscripten_glFinish.sig = 'v'; - - var _emscripten_glFlush = () => GLctx.flush(); - _emscripten_glFlush.sig = 'v'; - - var _emscripten_glFramebufferRenderbuffer = (target, attachment, renderbuffertarget, renderbuffer) => { - GLctx.framebufferRenderbuffer(target, attachment, renderbuffertarget, - GL.renderbuffers[renderbuffer]); - }; - _emscripten_glFramebufferRenderbuffer.sig = 'viiii'; - - var _emscripten_glFramebufferTexture2D = (target, attachment, textarget, texture, level) => { - GLctx.framebufferTexture2D(target, attachment, textarget, - GL.textures[texture], level); - }; - _emscripten_glFramebufferTexture2D.sig = 'viiiii'; - - var _emscripten_glFrontFace = (x0) => GLctx.frontFace(x0); - _emscripten_glFrontFace.sig = 'vi'; - - var _emscripten_glGenBuffers = (n, buffers) => { - GL.genObject(n, buffers, 'createBuffer', GL.buffers - ); - }; - _emscripten_glGenBuffers.sig = 'vip'; - - var _emscripten_glGenFramebuffers = (n, ids) => { - GL.genObject(n, ids, 'createFramebuffer', GL.framebuffers - ); - }; - _emscripten_glGenFramebuffers.sig = 'vip'; - - var _emscripten_glGenQueriesEXT = (n, ids) => { - for (var i = 0; i < n; i++) { - var query = GLctx.disjointTimerQueryExt['createQueryEXT'](); - if (!query) { - GL.recordError(0x502 /* GL_INVALID_OPERATION */); - while (i < n) HEAP32[(((ids)+(i++*4))>>2)] = 0; - return; - } - var id = GL.getNewId(GL.queries); - query.name = id; - GL.queries[id] = query; - HEAP32[(((ids)+(i*4))>>2)] = id; - } - }; - _emscripten_glGenQueriesEXT.sig = 'vip'; - - var _emscripten_glGenRenderbuffers = (n, renderbuffers) => { - GL.genObject(n, renderbuffers, 'createRenderbuffer', GL.renderbuffers - ); - }; - _emscripten_glGenRenderbuffers.sig = 'vip'; - - var _emscripten_glGenTextures = (n, textures) => { - GL.genObject(n, textures, 'createTexture', GL.textures - ); - }; - _emscripten_glGenTextures.sig = 'vip'; - - - var _emscripten_glGenVertexArrays = (n, arrays) => { - GL.genObject(n, arrays, 'createVertexArray', GL.vaos - ); - }; - _emscripten_glGenVertexArrays.sig = 'vip'; - var _emscripten_glGenVertexArraysOES = _emscripten_glGenVertexArrays; - _emscripten_glGenVertexArraysOES.sig = 'vip'; - - var _emscripten_glGenerateMipmap = (x0) => GLctx.generateMipmap(x0); - _emscripten_glGenerateMipmap.sig = 'vi'; - - - var __glGetActiveAttribOrUniform = (funcName, program, index, bufSize, length, size, type, name) => { - program = GL.programs[program]; - var info = GLctx[funcName](program, index); - if (info) { - // If an error occurs, nothing will be written to length, size and type and name. - var numBytesWrittenExclNull = name && stringToUTF8(info.name, name, bufSize); - if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; - if (size) HEAP32[((size)>>2)] = info.size; - if (type) HEAP32[((type)>>2)] = info.type; - } - }; - - var _emscripten_glGetActiveAttrib = (program, index, bufSize, length, size, type, name) => - __glGetActiveAttribOrUniform('getActiveAttrib', program, index, bufSize, length, size, type, name); - _emscripten_glGetActiveAttrib.sig = 'viiipppp'; - - - var _emscripten_glGetActiveUniform = (program, index, bufSize, length, size, type, name) => - __glGetActiveAttribOrUniform('getActiveUniform', program, index, bufSize, length, size, type, name); - _emscripten_glGetActiveUniform.sig = 'viiipppp'; - - var _emscripten_glGetAttachedShaders = (program, maxCount, count, shaders) => { - var result = GLctx.getAttachedShaders(GL.programs[program]); - var len = result.length; - if (len > maxCount) { - len = maxCount; - } - HEAP32[((count)>>2)] = len; - for (var i = 0; i < len; ++i) { - var id = GL.shaders.indexOf(result[i]); - HEAP32[(((shaders)+(i*4))>>2)] = id; - } - }; - _emscripten_glGetAttachedShaders.sig = 'viipp'; - - - var _emscripten_glGetAttribLocation = (program, name) => - GLctx.getAttribLocation(GL.programs[program], UTF8ToString(name)); - _emscripten_glGetAttribLocation.sig = 'iip'; - - var writeI53ToI64 = (ptr, num) => { - HEAPU32[((ptr)>>2)] = num; - var lower = HEAPU32[((ptr)>>2)]; - HEAPU32[(((ptr)+(4))>>2)] = (num - lower)/4294967296; - }; - - var emscriptenWebGLGet = (name_, p, type) => { - // Guard against user passing a null pointer. - // Note that GLES2 spec does not say anything about how passing a null - // pointer should be treated. Testing on desktop core GL 3, the application - // crashes on glGetIntegerv to a null pointer, but better to report an error - // instead of doing anything random. - if (!p) { - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - var ret = undefined; - switch (name_) { // Handle a few trivial GLES values - case 0x8DFA: // GL_SHADER_COMPILER - ret = 1; - break; - case 0x8DF8: // GL_SHADER_BINARY_FORMATS - if (type != 0 && type != 1) { - GL.recordError(0x500); // GL_INVALID_ENUM - } - // Do not write anything to the out pointer, since no binary formats are - // supported. - return; - case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS - ret = 0; - break; - case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS - // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete - // since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be - // queried for length), so implement it ourselves to allow C++ GLES2 - // code get the length. - var formats = GLctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/); - ret = formats ? formats.length : 0; - break; - - } - - if (ret === undefined) { - var result = GLctx.getParameter(name_); - switch (typeof result) { - case "number": - ret = result; - break; - case "boolean": - ret = result ? 1 : 0; - break; - case "string": - GL.recordError(0x500); // GL_INVALID_ENUM - return; - case "object": - if (result === null) { - // null is a valid result for some (e.g., which buffer is bound - - // perhaps nothing is bound), but otherwise can mean an invalid - // name_, which we need to report as an error - switch (name_) { - case 0x8894: // ARRAY_BUFFER_BINDING - case 0x8B8D: // CURRENT_PROGRAM - case 0x8895: // ELEMENT_ARRAY_BUFFER_BINDING - case 0x8CA6: // FRAMEBUFFER_BINDING or DRAW_FRAMEBUFFER_BINDING - case 0x8CA7: // RENDERBUFFER_BINDING - case 0x8069: // TEXTURE_BINDING_2D - case 0x85B5: // WebGL 2 GL_VERTEX_ARRAY_BINDING, or WebGL 1 extension OES_vertex_array_object GL_VERTEX_ARRAY_BINDING_OES - case 0x8514: { // TEXTURE_BINDING_CUBE_MAP - ret = 0; - break; - } - default: { - GL.recordError(0x500); // GL_INVALID_ENUM - return; - } - } - } else if (result instanceof Float32Array || - result instanceof Uint32Array || - result instanceof Int32Array || - result instanceof Array) { - for (var i = 0; i < result.length; ++i) { - switch (type) { - case 0: HEAP32[(((p)+(i*4))>>2)] = result[i]; break; - case 2: HEAPF32[(((p)+(i*4))>>2)] = result[i]; break; - case 4: HEAP8[(p)+(i)] = result[i] ? 1 : 0; break; - } - } - return; - } else { - try { - ret = result.name | 0; - } catch(e) { - GL.recordError(0x500); // GL_INVALID_ENUM - err(`GL_INVALID_ENUM in glGet${type}v: Unknown object returned from WebGL getParameter(${name_})! (error: ${e})`); - return; - } - } - break; - default: - GL.recordError(0x500); // GL_INVALID_ENUM - err(`GL_INVALID_ENUM in glGet${type}v: Native code calling glGet${type}v(${name_}) and it returns ${result} of type ${typeof(result)}!`); - return; - } - } - - switch (type) { - case 1: writeI53ToI64(p, ret); break; - case 0: HEAP32[((p)>>2)] = ret; break; - case 2: HEAPF32[((p)>>2)] = ret; break; - case 4: HEAP8[p] = ret ? 1 : 0; break; - } - }; - - var _emscripten_glGetBooleanv = (name_, p) => emscriptenWebGLGet(name_, p, 4); - _emscripten_glGetBooleanv.sig = 'vip'; - - var _emscripten_glGetBufferParameteriv = (target, value, data) => { - if (!data) { - // GLES2 specification does not specify how to behave if data is a null - // pointer. Since calling this function does not make sense if data == - // null, issue a GL error to notify user about it. - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - HEAP32[((data)>>2)] = GLctx.getBufferParameter(target, value); - }; - _emscripten_glGetBufferParameteriv.sig = 'viip'; - - var _emscripten_glGetError = () => { - var error = GLctx.getError() || GL.lastError; - GL.lastError = 0/*GL_NO_ERROR*/; - return error; - }; - _emscripten_glGetError.sig = 'i'; - - - var _emscripten_glGetFloatv = (name_, p) => emscriptenWebGLGet(name_, p, 2); - _emscripten_glGetFloatv.sig = 'vip'; - - var _emscripten_glGetFramebufferAttachmentParameteriv = (target, attachment, pname, params) => { - var result = GLctx.getFramebufferAttachmentParameter(target, attachment, pname); - if (result instanceof WebGLRenderbuffer || - result instanceof WebGLTexture) { - result = result.name | 0; - } - HEAP32[((params)>>2)] = result; - }; - _emscripten_glGetFramebufferAttachmentParameteriv.sig = 'viiip'; - - - var _emscripten_glGetIntegerv = (name_, p) => emscriptenWebGLGet(name_, p, 0); - _emscripten_glGetIntegerv.sig = 'vip'; - - var _emscripten_glGetProgramInfoLog = (program, maxLength, length, infoLog) => { - var log = GLctx.getProgramInfoLog(GL.programs[program]); - if (log === null) log = '(unknown error)'; - var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; - if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; - }; - _emscripten_glGetProgramInfoLog.sig = 'viipp'; - - var _emscripten_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(0x501 /* GL_INVALID_VALUE */); - return; - } - - if (program >= GL.counter) { - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - - program = GL.programs[program]; - - if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH - var log = GLctx.getProgramInfoLog(program); - if (log === null) log = '(unknown error)'; - HEAP32[((p)>>2)] = log.length + 1; - } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) { - if (!program.maxUniformLength) { - var numActiveUniforms = GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); - for (var i = 0; i < numActiveUniforms; ++i) { - program.maxUniformLength = Math.max(program.maxUniformLength, GLctx.getActiveUniform(program, i).name.length+1); - } - } - HEAP32[((p)>>2)] = program.maxUniformLength; - } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { - if (!program.maxAttributeLength) { - var numActiveAttributes = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); - for (var i = 0; i < numActiveAttributes; ++i) { - program.maxAttributeLength = Math.max(program.maxAttributeLength, GLctx.getActiveAttrib(program, i).name.length+1); - } - } - HEAP32[((p)>>2)] = program.maxAttributeLength; - } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) { - if (!program.maxUniformBlockNameLength) { - var numActiveUniformBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); - for (var i = 0; i < numActiveUniformBlocks; ++i) { - program.maxUniformBlockNameLength = Math.max(program.maxUniformBlockNameLength, GLctx.getActiveUniformBlockName(program, i).length+1); - } - } - HEAP32[((p)>>2)] = program.maxUniformBlockNameLength; - } else { - HEAP32[((p)>>2)] = GLctx.getProgramParameter(program, pname); - } - }; - _emscripten_glGetProgramiv.sig = 'viip'; - - - var _emscripten_glGetQueryObjecti64vEXT = (id, pname, params) => { - if (!params) { - // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); - return; - } - var query = GL.queries[id]; - var param; - { - param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname); - } - var ret; - if (typeof param == 'boolean') { - ret = param ? 1 : 0; - } else { - ret = param; - } - writeI53ToI64(params, ret); - }; - _emscripten_glGetQueryObjecti64vEXT.sig = 'viip'; - - var _emscripten_glGetQueryObjectivEXT = (id, pname, params) => { - if (!params) { - // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); - return; - } - var query = GL.queries[id]; - var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname); - var ret; - if (typeof param == 'boolean') { - ret = param ? 1 : 0; - } else { - ret = param; - } - HEAP32[((params)>>2)] = ret; - }; - _emscripten_glGetQueryObjectivEXT.sig = 'viip'; - - - var _emscripten_glGetQueryObjectui64vEXT = _emscripten_glGetQueryObjecti64vEXT; - - - var _emscripten_glGetQueryObjectuivEXT = _emscripten_glGetQueryObjectivEXT; - - var _emscripten_glGetQueryivEXT = (target, pname, params) => { - if (!params) { - // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); - return; - } - HEAP32[((params)>>2)] = GLctx.disjointTimerQueryExt['getQueryEXT'](target, pname); - }; - _emscripten_glGetQueryivEXT.sig = 'viip'; - - var _emscripten_glGetRenderbufferParameteriv = (target, pname, params) => { - if (!params) { - // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense - // if params == null, issue a GL error to notify user about it. - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - HEAP32[((params)>>2)] = GLctx.getRenderbufferParameter(target, pname); - }; - _emscripten_glGetRenderbufferParameteriv.sig = 'viip'; - - - var _emscripten_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) HEAP32[((length)>>2)] = numBytesWrittenExclNull; - }; - _emscripten_glGetShaderInfoLog.sig = 'viipp'; - - var _emscripten_glGetShaderPrecisionFormat = (shaderType, precisionType, range, precision) => { - var result = GLctx.getShaderPrecisionFormat(shaderType, precisionType); - HEAP32[((range)>>2)] = result.rangeMin; - HEAP32[(((range)+(4))>>2)] = result.rangeMax; - HEAP32[((precision)>>2)] = result.precision; - }; - _emscripten_glGetShaderPrecisionFormat.sig = 'viipp'; - - var _emscripten_glGetShaderSource = (shader, bufSize, length, source) => { - var result = GLctx.getShaderSource(GL.shaders[shader]); - if (!result) return; // If an error occurs, nothing will be written to length or source. - var numBytesWrittenExclNull = (bufSize > 0 && source) ? stringToUTF8(result, source, bufSize) : 0; - if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; - }; - _emscripten_glGetShaderSource.sig = 'viipp'; - - var _emscripten_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(0x501 /* GL_INVALID_VALUE */); - return; - } - if (pname == 0x8B84) { // 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; - HEAP32[((p)>>2)] = logLength; - } else if (pname == 0x8B88) { // 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; - HEAP32[((p)>>2)] = sourceLength; - } else { - HEAP32[((p)>>2)] = GLctx.getShaderParameter(GL.shaders[shader], pname); - } - }; - _emscripten_glGetShaderiv.sig = 'viip'; - - - - var webglGetExtensions = () => { - var exts = getEmscriptenSupportedExtensions(GLctx); - exts = exts.concat(exts.map((e) => "GL_" + e)); - return exts; - }; - - var _emscripten_glGetString = (name_) => { - var ret = GL.stringCache[name_]; - if (!ret) { - switch (name_) { - case 0x1F03 /* GL_EXTENSIONS */: - ret = stringToNewUTF8(webglGetExtensions().join(' ')); - break; - case 0x1F00 /* GL_VENDOR */: - case 0x1F01 /* GL_RENDERER */: - case 0x9245 /* UNMASKED_VENDOR_WEBGL */: - case 0x9246 /* UNMASKED_RENDERER_WEBGL */: - var s = GLctx.getParameter(name_); - if (!s) { - GL.recordError(0x500/*GL_INVALID_ENUM*/); - } - ret = s ? stringToNewUTF8(s) : 0; - break; - - case 0x1F02 /* GL_VERSION */: - var webGLVersion = GLctx.getParameter(0x1F02 /*GL_VERSION*/); - // return GLES version string corresponding to the version of the WebGL context - var glVersion = `OpenGL ES 2.0 (${webGLVersion})`; - ret = stringToNewUTF8(glVersion); - break; - case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */: - var glslVersion = GLctx.getParameter(0x8B8C /*GL_SHADING_LANGUAGE_VERSION*/); - // extract the version number 'N.M' from the string 'WebGL GLSL ES N.M ...' - var ver_re = /^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/; - var ver_num = glslVersion.match(ver_re); - if (ver_num !== null) { - if (ver_num[1].length == 3) ver_num[1] = ver_num[1] + '0'; // ensure minor version has 2 digits - glslVersion = `OpenGL ES GLSL ES ${ver_num[1]} (${glslVersion})`; - } - ret = stringToNewUTF8(glslVersion); - break; - default: - GL.recordError(0x500/*GL_INVALID_ENUM*/); - // fall through - } - GL.stringCache[name_] = ret; - } - return ret; - }; - _emscripten_glGetString.sig = 'pi'; - - var _emscripten_glGetTexParameterfv = (target, pname, params) => { - if (!params) { - // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); - return; - } - HEAPF32[((params)>>2)] = GLctx.getTexParameter(target, pname); - }; - _emscripten_glGetTexParameterfv.sig = 'viip'; - - var _emscripten_glGetTexParameteriv = (target, pname, params) => { - if (!params) { - // GLES2 specification does not specify how to behave if params 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(0x501 /* GL_INVALID_VALUE */); - return; - } - HEAP32[((params)>>2)] = GLctx.getTexParameter(target, pname); - }; - _emscripten_glGetTexParameteriv.sig = 'viip'; - - /** @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, 0x8B86/*GL_ACTIVE_UNIFORMS*/); - 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 _emscripten_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(0x501 /* GL_INVALID_VALUE */); - } - return -1; - }; - _emscripten_glGetUniformLocation.sig = 'iip'; - - 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(0x502/*GL_INVALID_OPERATION*/); - } - }; - - - /** @suppress{checkTypes} */ - var emscriptenWebGLGetUniform = (program, location, params, type) => { - if (!params) { - // GLES2 specification does not specify how to behave if params is a null - // pointer. Since calling this function does not make sense if params == - // null, issue a GL error to notify user about it. - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - program = GL.programs[program]; - webglPrepareUniformLocationsBeforeFirstUse(program); - var data = GLctx.getUniform(program, webglGetUniformLocation(location)); - if (typeof data == 'number' || typeof data == 'boolean') { - switch (type) { - case 0: HEAP32[((params)>>2)] = data; break; - case 2: HEAPF32[((params)>>2)] = data; break; - } - } else { - for (var i = 0; i < data.length; i++) { - switch (type) { - case 0: HEAP32[(((params)+(i*4))>>2)] = data[i]; break; - case 2: HEAPF32[(((params)+(i*4))>>2)] = data[i]; break; - } - } - } - }; - - var _emscripten_glGetUniformfv = (program, location, params) => { - emscriptenWebGLGetUniform(program, location, params, 2); - }; - _emscripten_glGetUniformfv.sig = 'viip'; - - - var _emscripten_glGetUniformiv = (program, location, params) => { - emscriptenWebGLGetUniform(program, location, params, 0); - }; - _emscripten_glGetUniformiv.sig = 'viip'; - - var _emscripten_glGetVertexAttribPointerv = (index, pname, pointer) => { - if (!pointer) { - // GLES2 specification does not specify how to behave if pointer is a null - // pointer. Since calling this function does not make sense if pointer == - // null, issue a GL error to notify user about it. - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - HEAP32[((pointer)>>2)] = GLctx.getVertexAttribOffset(index, pname); - }; - _emscripten_glGetVertexAttribPointerv.sig = 'viip'; - - /** @suppress{checkTypes} */ - var emscriptenWebGLGetVertexAttrib = (index, pname, params, type) => { - if (!params) { - // GLES2 specification does not specify how to behave if params is a null - // pointer. Since calling this function does not make sense if params == - // null, issue a GL error to notify user about it. - GL.recordError(0x501 /* GL_INVALID_VALUE */); - return; - } - var data = GLctx.getVertexAttrib(index, pname); - if (pname == 0x889F/*VERTEX_ATTRIB_ARRAY_BUFFER_BINDING*/) { - HEAP32[((params)>>2)] = data && data["name"]; - } else if (typeof data == 'number' || typeof data == 'boolean') { - switch (type) { - case 0: HEAP32[((params)>>2)] = data; break; - case 2: HEAPF32[((params)>>2)] = data; break; - case 5: HEAP32[((params)>>2)] = Math.fround(data); break; - } - } else { - for (var i = 0; i < data.length; i++) { - switch (type) { - case 0: HEAP32[(((params)+(i*4))>>2)] = data[i]; break; - case 2: HEAPF32[(((params)+(i*4))>>2)] = data[i]; break; - case 5: HEAP32[(((params)+(i*4))>>2)] = Math.fround(data[i]); break; - } - } - } - }; - - var _emscripten_glGetVertexAttribfv = (index, pname, params) => { - // N.B. This function may only be called if the vertex attribute was - // specified using the function glVertexAttrib*f(), otherwise the results - // are undefined. (GLES3 spec 6.1.12) - emscriptenWebGLGetVertexAttrib(index, pname, params, 2); - }; - _emscripten_glGetVertexAttribfv.sig = 'viip'; - - - var _emscripten_glGetVertexAttribiv = (index, pname, params) => { - // N.B. This function may only be called if the vertex attribute was - // specified using the function glVertexAttrib*f(), otherwise the results - // are undefined. (GLES3 spec 6.1.12) - emscriptenWebGLGetVertexAttrib(index, pname, params, 5); - }; - _emscripten_glGetVertexAttribiv.sig = 'viip'; - - var _emscripten_glHint = (x0, x1) => GLctx.hint(x0, x1); - _emscripten_glHint.sig = 'vii'; - - var _emscripten_glIsBuffer = (buffer) => { - var b = GL.buffers[buffer]; - if (!b) return 0; - return GLctx.isBuffer(b); - }; - _emscripten_glIsBuffer.sig = 'ii'; - - var _emscripten_glIsEnabled = (x0) => GLctx.isEnabled(x0); - _emscripten_glIsEnabled.sig = 'ii'; - - var _emscripten_glIsFramebuffer = (framebuffer) => { - var fb = GL.framebuffers[framebuffer]; - if (!fb) return 0; - return GLctx.isFramebuffer(fb); - }; - _emscripten_glIsFramebuffer.sig = 'ii'; - - var _emscripten_glIsProgram = (program) => { - program = GL.programs[program]; - if (!program) return 0; - return GLctx.isProgram(program); - }; - _emscripten_glIsProgram.sig = 'ii'; - - var _emscripten_glIsQueryEXT = (id) => { - var query = GL.queries[id]; - if (!query) return 0; - return GLctx.disjointTimerQueryExt['isQueryEXT'](query); - }; - _emscripten_glIsQueryEXT.sig = 'ii'; - - var _emscripten_glIsRenderbuffer = (renderbuffer) => { - var rb = GL.renderbuffers[renderbuffer]; - if (!rb) return 0; - return GLctx.isRenderbuffer(rb); - }; - _emscripten_glIsRenderbuffer.sig = 'ii'; - - var _emscripten_glIsShader = (shader) => { - var s = GL.shaders[shader]; - if (!s) return 0; - return GLctx.isShader(s); - }; - _emscripten_glIsShader.sig = 'ii'; - - var _emscripten_glIsTexture = (id) => { - var texture = GL.textures[id]; - if (!texture) return 0; - return GLctx.isTexture(texture); - }; - _emscripten_glIsTexture.sig = 'ii'; - - - var _emscripten_glIsVertexArray = (array) => { - - var vao = GL.vaos[array]; - if (!vao) return 0; - return GLctx.isVertexArray(vao); - }; - _emscripten_glIsVertexArray.sig = 'ii'; - var _emscripten_glIsVertexArrayOES = _emscripten_glIsVertexArray; - _emscripten_glIsVertexArrayOES.sig = 'ii'; - - var _emscripten_glLineWidth = (x0) => GLctx.lineWidth(x0); - _emscripten_glLineWidth.sig = 'vf'; - - var _emscripten_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 = {}; - - }; - _emscripten_glLinkProgram.sig = 'vi'; - - var _emscripten_glPixelStorei = (pname, param) => { - if (pname == 3317) { - GL.unpackAlignment = param; - } else if (pname == 3314) { - GL.unpackRowLength = param; - } - GLctx.pixelStorei(pname, param); - }; - _emscripten_glPixelStorei.sig = 'vii'; - - var _emscripten_glPolygonModeWEBGL = (face, mode) => { - GLctx.webglPolygonMode['polygonModeWEBGL'](face, mode); - }; - _emscripten_glPolygonModeWEBGL.sig = 'vii'; - - var _emscripten_glPolygonOffset = (x0, x1) => GLctx.polygonOffset(x0, x1); - _emscripten_glPolygonOffset.sig = 'vff'; - - var _emscripten_glPolygonOffsetClampEXT = (factor, units, clamp) => { - GLctx.extPolygonOffsetClamp['polygonOffsetClampEXT'](factor, units, clamp); - }; - _emscripten_glPolygonOffsetClampEXT.sig = 'vfff'; - - var _emscripten_glQueryCounterEXT = (id, target) => { - GLctx.disjointTimerQueryExt['queryCounterEXT'](GL.queries[id], target); - }; - _emscripten_glQueryCounterEXT.sig = 'vii'; - - 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, - }; - return colorChannels[format - 0x1902]||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 -= 0x1400; - - if (type == 1) return HEAPU8; - - if (type == 4) return HEAP32; - - if (type == 6) return HEAPF32; - - if (type == 5 - || type == 28922 - ) - return HEAPU32; - - return HEAPU16; - }; - - 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 _emscripten_glReadPixels = (x, y, width, height, format, type, pixels) => { - var pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, format); - if (!pixelData) { - GL.recordError(0x500/*GL_INVALID_ENUM*/); - return; - } - GLctx.readPixels(x, y, width, height, format, type, pixelData); - }; - _emscripten_glReadPixels.sig = 'viiiiiip'; - - var _emscripten_glReleaseShaderCompiler = () => { - // NOP (as allowed by GLES 2.0 spec) - }; - _emscripten_glReleaseShaderCompiler.sig = 'v'; - - var _emscripten_glRenderbufferStorage = (x0, x1, x2, x3) => GLctx.renderbufferStorage(x0, x1, x2, x3); - _emscripten_glRenderbufferStorage.sig = 'viiii'; - - var _emscripten_glSampleCoverage = (value, invert) => { - GLctx.sampleCoverage(value, !!invert); - }; - _emscripten_glSampleCoverage.sig = 'vfi'; - - var _emscripten_glScissor = (x0, x1, x2, x3) => GLctx.scissor(x0, x1, x2, x3); - _emscripten_glScissor.sig = 'viiii'; - - var _emscripten_glShaderBinary = (count, shaders, binaryformat, binary, length) => { - GL.recordError(0x500/*GL_INVALID_ENUM*/); - }; - _emscripten_glShaderBinary.sig = 'vipipi'; - - var _emscripten_glShaderSource = (shader, count, string, length) => { - var source = GL.getSource(shader, count, string, length); - - GLctx.shaderSource(GL.shaders[shader], source); - }; - _emscripten_glShaderSource.sig = 'viipp'; - - var _emscripten_glStencilFunc = (x0, x1, x2) => GLctx.stencilFunc(x0, x1, x2); - _emscripten_glStencilFunc.sig = 'viii'; - - var _emscripten_glStencilFuncSeparate = (x0, x1, x2, x3) => GLctx.stencilFuncSeparate(x0, x1, x2, x3); - _emscripten_glStencilFuncSeparate.sig = 'viiii'; - - var _emscripten_glStencilMask = (x0) => GLctx.stencilMask(x0); - _emscripten_glStencilMask.sig = 'vi'; - - var _emscripten_glStencilMaskSeparate = (x0, x1) => GLctx.stencilMaskSeparate(x0, x1); - _emscripten_glStencilMaskSeparate.sig = 'vii'; - - var _emscripten_glStencilOp = (x0, x1, x2) => GLctx.stencilOp(x0, x1, x2); - _emscripten_glStencilOp.sig = 'viii'; - - var _emscripten_glStencilOpSeparate = (x0, x1, x2, x3) => GLctx.stencilOpSeparate(x0, x1, x2, x3); - _emscripten_glStencilOpSeparate.sig = 'viiii'; - - - var _emscripten_glTexImage2D = (target, level, internalFormat, width, height, border, format, type, pixels) => { - var pixelData = pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null; - GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixelData); - }; - _emscripten_glTexImage2D.sig = 'viiiiiiiip'; - - var _emscripten_glTexParameterf = (x0, x1, x2) => GLctx.texParameterf(x0, x1, x2); - _emscripten_glTexParameterf.sig = 'viif'; - - var _emscripten_glTexParameterfv = (target, pname, params) => { - var param = HEAPF32[((params)>>2)]; - GLctx.texParameterf(target, pname, param); - }; - _emscripten_glTexParameterfv.sig = 'viip'; - - var _emscripten_glTexParameteri = (x0, x1, x2) => GLctx.texParameteri(x0, x1, x2); - _emscripten_glTexParameteri.sig = 'viii'; - - var _emscripten_glTexParameteriv = (target, pname, params) => { - var param = HEAP32[((params)>>2)]; - GLctx.texParameteri(target, pname, param); - }; - _emscripten_glTexParameteriv.sig = 'viip'; - - - var _emscripten_glTexSubImage2D = (target, level, xoffset, yoffset, width, height, format, type, pixels) => { - var pixelData = pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, 0) : null; - GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixelData); - }; - _emscripten_glTexSubImage2D.sig = 'viiiiiiiip'; - - - var _emscripten_glUniform1f = (location, v0) => { - GLctx.uniform1f(webglGetUniformLocation(location), v0); - }; - _emscripten_glUniform1f.sig = 'vif'; - - - var miniTempWebGLFloatBuffers = []; - - var _emscripten_glUniform1fv = (location, count, value) => { - - if (count <= 288) { - // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLFloatBuffers[count]; - for (var i = 0; i < count; ++i) { - view[i] = HEAPF32[(((value)+(4*i))>>2)]; - } - } else - { - var view = HEAPF32.subarray((((value)>>2)), ((value+count*4)>>2)); - } - GLctx.uniform1fv(webglGetUniformLocation(location), view); - }; - _emscripten_glUniform1fv.sig = 'viip'; - - - var _emscripten_glUniform1i = (location, v0) => { - GLctx.uniform1i(webglGetUniformLocation(location), v0); - }; - _emscripten_glUniform1i.sig = 'vii'; - - - var miniTempWebGLIntBuffers = []; - - var _emscripten_glUniform1iv = (location, count, value) => { - - if (count <= 288) { - // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLIntBuffers[count]; - for (var i = 0; i < count; ++i) { - view[i] = HEAP32[(((value)+(4*i))>>2)]; - } - } else - { - var view = HEAP32.subarray((((value)>>2)), ((value+count*4)>>2)); - } - GLctx.uniform1iv(webglGetUniformLocation(location), view); - }; - _emscripten_glUniform1iv.sig = 'viip'; - - - var _emscripten_glUniform2f = (location, v0, v1) => { - GLctx.uniform2f(webglGetUniformLocation(location), v0, v1); - }; - _emscripten_glUniform2f.sig = 'viff'; - - - - var _emscripten_glUniform2fv = (location, count, value) => { - - if (count <= 144) { - // avoid allocation when uploading few enough uniforms - count *= 2; - var view = miniTempWebGLFloatBuffers[count]; - for (var i = 0; i < count; i += 2) { - view[i] = HEAPF32[(((value)+(4*i))>>2)]; - view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; - } - } else - { - var view = HEAPF32.subarray((((value)>>2)), ((value+count*8)>>2)); - } - GLctx.uniform2fv(webglGetUniformLocation(location), view); - }; - _emscripten_glUniform2fv.sig = 'viip'; - - - var _emscripten_glUniform2i = (location, v0, v1) => { - GLctx.uniform2i(webglGetUniformLocation(location), v0, v1); - }; - _emscripten_glUniform2i.sig = 'viii'; - - - - var _emscripten_glUniform2iv = (location, count, value) => { - - if (count <= 144) { - // avoid allocation when uploading few enough uniforms - count *= 2; - var view = miniTempWebGLIntBuffers[count]; - for (var i = 0; i < count; i += 2) { - view[i] = HEAP32[(((value)+(4*i))>>2)]; - view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; - } - } else - { - var view = HEAP32.subarray((((value)>>2)), ((value+count*8)>>2)); - } - GLctx.uniform2iv(webglGetUniformLocation(location), view); - }; - _emscripten_glUniform2iv.sig = 'viip'; - - - var _emscripten_glUniform3f = (location, v0, v1, v2) => { - GLctx.uniform3f(webglGetUniformLocation(location), v0, v1, v2); - }; - _emscripten_glUniform3f.sig = 'vifff'; - - - - var _emscripten_glUniform3fv = (location, count, value) => { - - if (count <= 96) { - // avoid allocation when uploading few enough uniforms - count *= 3; - var view = miniTempWebGLFloatBuffers[count]; - for (var i = 0; i < count; i += 3) { - view[i] = HEAPF32[(((value)+(4*i))>>2)]; - view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; - view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; - } - } else - { - var view = HEAPF32.subarray((((value)>>2)), ((value+count*12)>>2)); - } - GLctx.uniform3fv(webglGetUniformLocation(location), view); - }; - _emscripten_glUniform3fv.sig = 'viip'; - - - var _emscripten_glUniform3i = (location, v0, v1, v2) => { - GLctx.uniform3i(webglGetUniformLocation(location), v0, v1, v2); - }; - _emscripten_glUniform3i.sig = 'viiii'; - - - - var _emscripten_glUniform3iv = (location, count, value) => { - - if (count <= 96) { - // avoid allocation when uploading few enough uniforms - count *= 3; - var view = miniTempWebGLIntBuffers[count]; - for (var i = 0; i < count; i += 3) { - view[i] = HEAP32[(((value)+(4*i))>>2)]; - view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; - view[i+2] = HEAP32[(((value)+(4*i+8))>>2)]; - } - } else - { - var view = HEAP32.subarray((((value)>>2)), ((value+count*12)>>2)); - } - GLctx.uniform3iv(webglGetUniformLocation(location), view); - }; - _emscripten_glUniform3iv.sig = 'viip'; - - - var _emscripten_glUniform4f = (location, v0, v1, v2, v3) => { - GLctx.uniform4f(webglGetUniformLocation(location), v0, v1, v2, v3); - }; - _emscripten_glUniform4f.sig = 'viffff'; - - - - var _emscripten_glUniform4fv = (location, count, value) => { - - if (count <= 72) { - // avoid allocation when uploading few enough uniforms - var view = miniTempWebGLFloatBuffers[4*count]; - // hoist the heap out of the loop for size and for pthreads+growth. - var heap = HEAPF32; - value = ((value)>>2); - count *= 4; - for (var i = 0; i < count; i += 4) { - 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]; - } - } else - { - var view = HEAPF32.subarray((((value)>>2)), ((value+count*16)>>2)); - } - GLctx.uniform4fv(webglGetUniformLocation(location), view); - }; - _emscripten_glUniform4fv.sig = 'viip'; - - - var _emscripten_glUniform4i = (location, v0, v1, v2, v3) => { - GLctx.uniform4i(webglGetUniformLocation(location), v0, v1, v2, v3); - }; - _emscripten_glUniform4i.sig = 'viiiii'; - - - - var _emscripten_glUniform4iv = (location, count, value) => { - - if (count <= 72) { - // avoid allocation when uploading few enough uniforms - count *= 4; - var view = miniTempWebGLIntBuffers[count]; - for (var i = 0; i < count; i += 4) { - view[i] = HEAP32[(((value)+(4*i))>>2)]; - view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; - view[i+2] = HEAP32[(((value)+(4*i+8))>>2)]; - view[i+3] = HEAP32[(((value)+(4*i+12))>>2)]; - } - } else - { - var view = HEAP32.subarray((((value)>>2)), ((value+count*16)>>2)); - } - GLctx.uniform4iv(webglGetUniformLocation(location), view); - }; - _emscripten_glUniform4iv.sig = 'viip'; - - - - var _emscripten_glUniformMatrix2fv = (location, count, transpose, value) => { - - if (count <= 72) { - // avoid allocation when uploading few enough uniforms - count *= 4; - var view = miniTempWebGLFloatBuffers[count]; - for (var i = 0; i < count; i += 4) { - view[i] = HEAPF32[(((value)+(4*i))>>2)]; - view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; - view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; - view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)]; - } - } else - { - var view = HEAPF32.subarray((((value)>>2)), ((value+count*16)>>2)); - } - GLctx.uniformMatrix2fv(webglGetUniformLocation(location), !!transpose, view); - }; - _emscripten_glUniformMatrix2fv.sig = 'viiip'; - - - - var _emscripten_glUniformMatrix3fv = (location, count, transpose, value) => { - - if (count <= 32) { - // avoid allocation when uploading few enough uniforms - count *= 9; - var view = miniTempWebGLFloatBuffers[count]; - for (var i = 0; i < count; i += 9) { - view[i] = HEAPF32[(((value)+(4*i))>>2)]; - view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; - view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; - view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)]; - view[i+4] = HEAPF32[(((value)+(4*i+16))>>2)]; - view[i+5] = HEAPF32[(((value)+(4*i+20))>>2)]; - view[i+6] = HEAPF32[(((value)+(4*i+24))>>2)]; - view[i+7] = HEAPF32[(((value)+(4*i+28))>>2)]; - view[i+8] = HEAPF32[(((value)+(4*i+32))>>2)]; - } - } else - { - var view = HEAPF32.subarray((((value)>>2)), ((value+count*36)>>2)); - } - GLctx.uniformMatrix3fv(webglGetUniformLocation(location), !!transpose, view); - }; - _emscripten_glUniformMatrix3fv.sig = 'viiip'; - - - - var _emscripten_glUniformMatrix4fv = (location, count, transpose, value) => { - - 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 = HEAPF32; - 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 = HEAPF32.subarray((((value)>>2)), ((value+count*64)>>2)); - } - GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view); - }; - _emscripten_glUniformMatrix4fv.sig = 'viiip'; - - var _emscripten_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; - }; - _emscripten_glUseProgram.sig = 'vi'; - - var _emscripten_glValidateProgram = (program) => { - GLctx.validateProgram(GL.programs[program]); - }; - _emscripten_glValidateProgram.sig = 'vi'; - - var _emscripten_glVertexAttrib1f = (x0, x1) => GLctx.vertexAttrib1f(x0, x1); - _emscripten_glVertexAttrib1f.sig = 'vif'; - - var _emscripten_glVertexAttrib1fv = (index, v) => { - - GLctx.vertexAttrib1f(index, HEAPF32[v>>2]); - }; - _emscripten_glVertexAttrib1fv.sig = 'vip'; - - var _emscripten_glVertexAttrib2f = (x0, x1, x2) => GLctx.vertexAttrib2f(x0, x1, x2); - _emscripten_glVertexAttrib2f.sig = 'viff'; - - var _emscripten_glVertexAttrib2fv = (index, v) => { - - GLctx.vertexAttrib2f(index, HEAPF32[v>>2], HEAPF32[v+4>>2]); - }; - _emscripten_glVertexAttrib2fv.sig = 'vip'; - - var _emscripten_glVertexAttrib3f = (x0, x1, x2, x3) => GLctx.vertexAttrib3f(x0, x1, x2, x3); - _emscripten_glVertexAttrib3f.sig = 'vifff'; - - var _emscripten_glVertexAttrib3fv = (index, v) => { - - GLctx.vertexAttrib3f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2]); - }; - _emscripten_glVertexAttrib3fv.sig = 'vip'; - - var _emscripten_glVertexAttrib4f = (x0, x1, x2, x3, x4) => GLctx.vertexAttrib4f(x0, x1, x2, x3, x4); - _emscripten_glVertexAttrib4f.sig = 'viffff'; - - var _emscripten_glVertexAttrib4fv = (index, v) => { - - GLctx.vertexAttrib4f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2], HEAPF32[v+12>>2]); - }; - _emscripten_glVertexAttrib4fv.sig = 'vip'; - - - var _emscripten_glVertexAttribDivisor = (index, divisor) => { - GLctx.vertexAttribDivisor(index, divisor); - }; - _emscripten_glVertexAttribDivisor.sig = 'vii'; - var _emscripten_glVertexAttribDivisorANGLE = _emscripten_glVertexAttribDivisor; - - var _emscripten_glVertexAttribPointer = (index, size, type, normalized, stride, ptr) => { - GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); - }; - _emscripten_glVertexAttribPointer.sig = 'viiiiip'; - - var _emscripten_glViewport = (x0, x1, x2, x3) => GLctx.viewport(x0, x1, x2, x3); - _emscripten_glViewport.sig = 'viiii'; - - var _emscripten_out = (str) => out(UTF8ToString(str)); - _emscripten_out.sig = 'vp'; - - class HandleAllocator { - allocated = [undefined]; - freelist = []; - get(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) { - // 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 promiseMap = new HandleAllocator();; - var makePromise = () => { - var promiseInfo = {}; - promiseInfo.promise = new Promise((resolve, reject) => { - promiseInfo.reject = reject; - promiseInfo.resolve = resolve; - }); - promiseInfo.id = promiseMap.allocate(promiseInfo); - return promiseInfo; - }; - var _emscripten_promise_create = () => makePromise().id; - _emscripten_promise_create.sig = 'p'; - - var _emscripten_promise_destroy = (id) => { - promiseMap.free(id); - }; - _emscripten_promise_destroy.sig = 'vp'; - - - var getPromise = (id) => promiseMap.get(id).promise; - - var _emscripten_promise_resolve = (id, result, value) => { - var info = promiseMap.get(id); - switch (result) { - case 0: - info.resolve(value); - return; - case 1: - info.resolve(getPromise(value)); - return; - case 2: - info.resolve(getPromise(value)); - _emscripten_promise_destroy(value); - return; - case 3: - info.reject(value); - return; - } - }; - _emscripten_promise_resolve.sig = 'vpip'; - - - - var growMemory = (size) => { - var oldHeapSize = wasmMemory.buffer.byteLength; - var pages = ((size - oldHeapSize + 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 /*success*/; - } catch(e) { - } - // implicit 0 return to save code size (caller will cast "undefined" into 0 - // anyhow) - }; - var _emscripten_resize_heap = (requestedSize) => { - var oldSize = HEAPU8.length; - // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. - 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. - - // 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) { - 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 + 0.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; - } - } - return false; - }; - _emscripten_resize_heap.sig = 'ip'; - - var maybeCStringToJsString = (cString) => { - // "cString > 2" checks if the input is a number, and isn't of the special - // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). - // In other words, if cString > 2 then it's a pointer to a valid place in - // memory, and points to a C string. - return cString > 2 ? UTF8ToString(cString) : cString; - }; - - /** @type {Object} */ - var specialHTMLTargets = [0, typeof document != 'undefined' ? document : 0, typeof window != 'undefined' ? window : 0]; - var findEventTarget = (target) => { - target = maybeCStringToJsString(target); - var domElement = specialHTMLTargets[target] || (typeof document != 'undefined' ? document.querySelector(target) : null); - return domElement; - }; - var findCanvasEventTarget = findEventTarget; - var _emscripten_set_canvas_element_size = (target, width, height) => { - var canvas = findCanvasEventTarget(target); - if (!canvas) return -4; - canvas.width = width; - canvas.height = height; - return 0; - }; - _emscripten_set_canvas_element_size.sig = 'ipii'; - - - - /** @param {number=} timeout */ - var safeSetTimeout = (func, timeout) => { - runtimeKeepalivePush(); - return setTimeout(() => { - runtimeKeepalivePop(); - callUserCallback(func); - }, timeout); - }; - var _emscripten_sleep = (ms) => Asyncify.handleSleep((wakeUp) => safeSetTimeout(wakeUp, ms)); - _emscripten_sleep.sig = 'vi'; - _emscripten_sleep.isAsync = true; - - - - var _emscripten_wget_data = (url, pbuffer, pnum, perror) => Asyncify.handleAsync(async () => { - /* no need for run dependency, this is async but will not do any prepare etc. step */ - try { - const byteArray = await asyncLoad(UTF8ToString(url)); - // can only allocate the buffer after the wakeUp, not during an asyncing - var buffer = _malloc(byteArray.length); // must be freed by caller! - HEAPU8.set(byteArray, buffer); - HEAPU32[((pbuffer)>>2)] = buffer; - HEAP32[((pnum)>>2)] = byteArray.length; - HEAP32[((perror)>>2)] = 0; - } catch (err) { - HEAP32[((perror)>>2)] = 1; - } - }); - _emscripten_wget_data.sig = 'vpppp'; - _emscripten_wget_data.isAsync = true; - - var ENV = PHPLoader.ENV || { - }; - - var getEnvStrings = () => { - if (!getEnvStrings.strings) { - // Default values. - // Browser language detection #8751 - var lang = ((typeof navigator == 'object' && navigator.language) || '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 _environ_get = (__environ, environ_buf) => { - var bufSize = 0; - var envp = 0; - for (var string of getEnvStrings()) { - var ptr = environ_buf + bufSize; - HEAPU32[(((__environ)+(envp))>>2)] = ptr; - bufSize += stringToUTF8(string, ptr, Infinity) + 1; - envp += 4; - } - return 0; - }; - _environ_get.sig = 'ipp'; - - - var _environ_sizes_get = (penviron_count, penviron_buf_size) => { - var strings = getEnvStrings(); - HEAPU32[((penviron_count)>>2)] = strings.length; - var bufSize = 0; - for (var string of strings) { - bufSize += lengthBytesUTF8(string) + 1; - } - HEAPU32[((penviron_buf_size)>>2)] = bufSize; - return 0; - }; - _environ_sizes_get.sig = 'ipp'; - - - - function _fd_fdstat_get(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; - } - HEAP8[pbuf] = type; - HEAP16[(((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; - } - } - _fd_fdstat_get.sig = 'iip'; - - /** @param {number=} offset */ - var doReadv = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAPU32[((iov)>>2)]; - var len = HEAPU32[(((iov)+(4))>>2)]; - iov += 8; - var curr = FS.read(stream, HEAP8, 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_pread(fd, iov, iovcnt, offset, pnum) { - offset = bigintToI53Checked(offset); - - - try { - - if (isNaN(offset)) return 61; - var stream = SYSCALLS.getStreamFromFD(fd) - var num = doReadv(stream, iov, iovcnt, offset); - HEAPU32[((pnum)>>2)] = num; - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return e.errno; - } - ; - } - _fd_pread.sig = 'iippjp'; - - /** @param {number=} offset */ - var doWritev = (stream, iov, iovcnt, offset) => { - var ret = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAPU32[((iov)>>2)]; - var len = HEAPU32[(((iov)+(4))>>2)]; - iov += 8; - var curr = FS.write(stream, HEAP8, ptr, len, offset); - if (curr < 0) return -1; - ret += curr; - if (curr < len) { - // No more space to write. - break; - } - if (typeof offset != 'undefined') { - offset += curr; - } - } - return ret; - }; - - - function _fd_pwrite(fd, iov, iovcnt, offset, pnum) { - offset = bigintToI53Checked(offset); - - - try { - - if (isNaN(offset)) return 61; - var stream = SYSCALLS.getStreamFromFD(fd) - var num = doWritev(stream, iov, iovcnt, offset); - HEAPU32[((pnum)>>2)] = num; - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return e.errno; - } - ; - } - _fd_pwrite.sig = 'iippjp'; - - - function _fd_read(fd, iov, iovcnt, pnum) { - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - var num = doReadv(stream, iov, iovcnt); - HEAPU32[((pnum)>>2)] = num; - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return e.errno; - } - } - _fd_read.sig = 'iippp'; - - - function _fd_seek(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; - } - ; - } - _fd_seek.sig = 'iijip'; - - var _fd_sync = function (fd) { - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - return Asyncify.handleSleep((wakeUp) => { - var mount = stream.node.mount; - if (!mount.type.syncfs) { - // We write directly to the file system, so there's nothing to do here. - wakeUp(0); - return; - } - mount.type.syncfs(mount, false, (err) => { - wakeUp(err ? 29 : 0); - }); - }); - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return e.errno; - } - }; - _fd_sync.sig = 'ii'; - _fd_sync.isAsync = true; - - - function _fd_write(fd, iov, iovcnt, pnum) { - try { - - var stream = SYSCALLS.getStreamFromFD(fd); - var num = doWritev(stream, iov, iovcnt); - HEAPU32[((pnum)>>2)] = num; - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return e.errno; - } - } - _fd_write.sig = 'iippp'; - - - - - - - - - - var _getaddrinfo = (node, service, hint, out) => { - // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL - // hints are specified or ai_family set to AF_UNSPEC or ai_socktype or ai_protocol set to 0 then we - // really should provide a linked list of suitable addrinfo values. - var addrs = []; - var canon = null; - var addr = 0; - var port = 0; - var flags = 0; - var family = 0; - var type = 0; - var proto = 0; - var ai, last; - - function allocaddrinfo(family, type, proto, canon, addr, port) { - var sa, salen, ai; - var errno; - - salen = family === 10 ? - 28 : - 16; - addr = family === 10 ? - inetNtop6(addr) : - inetNtop4(addr); - sa = _malloc(salen); - errno = writeSockaddr(sa, family, addr, port); - - ai = _malloc(32); - HEAP32[(((ai)+(4))>>2)] = family; - HEAP32[(((ai)+(8))>>2)] = type; - HEAP32[(((ai)+(12))>>2)] = proto; - HEAPU32[(((ai)+(24))>>2)] = canon; - HEAPU32[(((ai)+(20))>>2)] = sa; - if (family === 10) { - HEAP32[(((ai)+(16))>>2)] = 28; - } else { - HEAP32[(((ai)+(16))>>2)] = 16; - } - HEAP32[(((ai)+(28))>>2)] = 0; - - return ai; - } - - if (hint) { - flags = HEAP32[((hint)>>2)]; - family = HEAP32[(((hint)+(4))>>2)]; - type = HEAP32[(((hint)+(8))>>2)]; - proto = HEAP32[(((hint)+(12))>>2)]; - } - if (type && !proto) { - proto = type === 2 ? 17 : 6; - } - if (!type && proto) { - type = proto === 17 ? 2 : 1; - } - - // If type or proto are set to zero in hints we should really be returning multiple addrinfo values, but for - // now default to a TCP STREAM socket so we can at least return a sensible addrinfo given NULL hints. - if (proto === 0) { - proto = 6; - } - if (type === 0) { - type = 1; - } - - if (!node && !service) { - return -2; - } - if (flags & ~(1|2|4| - 1024|8|16|32)) { - return -1; - } - if (hint !== 0 && (HEAP32[((hint)>>2)] & 2) && !node) { - return -1; - } - if (flags & 32) { - // TODO - return -2; - } - if (type !== 0 && type !== 1 && type !== 2) { - return -7; - } - if (family !== 0 && family !== 2 && family !== 10) { - return -6; - } - - if (service) { - service = UTF8ToString(service); - port = parseInt(service, 10); - - if (isNaN(port)) { - if (flags & 1024) { - return -2; - } - // TODO support resolving well-known service names from: - // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt - return -8; - } - } - - if (!node) { - if (family === 0) { - family = 2; - } - if ((flags & 1) === 0) { - if (family === 2) { - addr = _htonl(2130706433); - } else { - addr = [0, 0, 0, _htonl(1)]; - } - } - ai = allocaddrinfo(family, type, proto, null, addr, port); - HEAPU32[((out)>>2)] = ai; - return 0; - } - - // - // try as a numeric address - // - node = UTF8ToString(node); - addr = inetPton4(node); - if (addr !== null) { - // incoming node is a valid ipv4 address - if (family === 0 || family === 2) { - family = 2; - } - else if (family === 10 && (flags & 8)) { - addr = [0, 0, _htonl(0xffff), addr]; - family = 10; - } else { - return -2; - } - } else { - addr = inetPton6(node); - if (addr !== null) { - // incoming node is a valid ipv6 address - if (family === 0 || family === 10) { - family = 10; - } else { - return -2; - } - } - } - if (addr != null) { - ai = allocaddrinfo(family, type, proto, node, addr, port); - HEAPU32[((out)>>2)] = ai; - return 0; - } - if (flags & 4) { - return -2; - } - - // - // try as a hostname - // - // resolve the hostname to a temporary fake address - node = DNS.lookup_name(node); - addr = inetPton4(node); - if (family === 0) { - family = 2; - } else if (family === 10) { - addr = [0, 0, _htonl(0xffff), addr]; - } - ai = allocaddrinfo(family, type, proto, null, addr, port); - HEAPU32[((out)>>2)] = ai; - return 0; - }; - _getaddrinfo.sig = 'ipppp'; - - - - var _getnameinfo = (sa, salen, node, nodelen, serv, servlen, flags) => { - var info = readSockaddr(sa, salen); - if (info.errno) { - return -6; - } - var port = info.port; - var addr = info.addr; - - var overflowed = false; - - if (node && nodelen) { - var lookup; - if ((flags & 1) || !(lookup = DNS.lookup_addr(addr))) { - if (flags & 8) { - return -2; - } - } else { - addr = lookup; - } - var numBytesWrittenExclNull = stringToUTF8(addr, node, nodelen); - - if (numBytesWrittenExclNull+1 >= nodelen) { - overflowed = true; - } - } - - if (serv && servlen) { - port = '' + port; - var numBytesWrittenExclNull = stringToUTF8(port, serv, servlen); - - if (numBytesWrittenExclNull+1 >= servlen) { - overflowed = true; - } - } - - if (overflowed) { - // Note: even when we overflow, getnameinfo() is specced to write out the truncated results. - return -12; - } - - return 0; - }; - _getnameinfo.sig = 'ipipipii'; - - var Protocols = { - list:[], - map:{ - }, - }; - - var stringToAscii = (str, buffer) => { - for (var i = 0; i < str.length; ++i) { - HEAP8[buffer++] = str.charCodeAt(i); - } - // Null-terminate the string - HEAP8[buffer] = 0; - }; - - var _setprotoent = (stayopen) => { - // void setprotoent(int stayopen); - - // Allocate and populate a protoent structure given a name, protocol number and array of aliases - function allocprotoent(name, proto, aliases) { - // write name into buffer - var nameBuf = _malloc(name.length + 1); - stringToAscii(name, nameBuf); - - // write aliases into buffer - var j = 0; - var length = aliases.length; - var aliasListBuf = _malloc((length + 1) * 4); // Use length + 1 so we have space for the terminating NULL ptr. - - for (var i = 0; i < length; i++, j += 4) { - var alias = aliases[i]; - var aliasBuf = _malloc(alias.length + 1); - stringToAscii(alias, aliasBuf); - HEAPU32[(((aliasListBuf)+(j))>>2)] = aliasBuf; - } - HEAPU32[(((aliasListBuf)+(j))>>2)] = 0; // Terminating NULL pointer. - - // generate protoent - var pe = _malloc(12); - HEAPU32[((pe)>>2)] = nameBuf; - HEAPU32[(((pe)+(4))>>2)] = aliasListBuf; - HEAP32[(((pe)+(8))>>2)] = proto; - return pe; - }; - - // Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial - // to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful. - var list = Protocols.list; - var map = Protocols.map; - if (list.length === 0) { - var entry = allocprotoent('tcp', 6, ['TCP']); - list.push(entry); - map['tcp'] = map['6'] = entry; - entry = allocprotoent('udp', 17, ['UDP']); - list.push(entry); - map['udp'] = map['17'] = entry; - } - - _setprotoent.index = 0; - }; - _setprotoent.sig = 'vi'; - - - var _getprotobyname = (name) => { - // struct protoent *getprotobyname(const char *); - name = UTF8ToString(name); - _setprotoent(true); - var result = Protocols.map[name]; - return result; - }; - _getprotobyname.sig = 'pp'; - - - var _getprotobynumber = (number) => { - // struct protoent *getprotobynumber(int proto); - _setprotoent(true); - var result = Protocols.map[number]; - return result; - }; - _getprotobynumber.sig = 'pi'; - - - - - function _js_flock(fd, op) { - _js_wasm_trace('js_flock(%d, %d)', fd, op); - // Emscripten does not expose these constants to JS, so we hardcode them here. - // Based on - // https://github.com/emscripten-core/emscripten/blob/76860cc47cef67f5712a7a03a247bc1baabf7ba4/system/lib/libc/musl/include/sys/file.h#L7-L10 - const emscripten_LOCK_SH = 1; - const emscripten_LOCK_EX = 2; - const emscripten_LOCK_NB = 4; - const emscripten_LOCK_UN = 8; - - const flockToLockOpType = { - [emscripten_LOCK_SH]: 'shared', - [emscripten_LOCK_EX]: 'exclusive', - [emscripten_LOCK_UN]: 'unlocked', - }; - - let vfsPath; - let errno; - - [vfsPath, errno] = locking.get_vfs_path_from_fd(fd); - if (errno !== 0) { - _js_wasm_trace( - 'js_flock(%d, %d) get_vfs_path_from_fd errno %d', - fd, - op, - vfsPath, - errno - ); - return -errno; - } - - if (!locking.is_path_to_shared_fs(vfsPath)) { - _js_wasm_trace( - 'flock(%d, %d) locking is not implemented for non-NodeFS path %s', - fd, - op, - vfsPath - ); - // If not a NodeFS path, we can't lock it. - // Default to succeeding as Emscripten does. - return 0; - } - - errno = locking.check_lock_params(fd, op); - if (errno !== 0) { - _js_wasm_trace( - 'js_flock(%d, %d) check_lock_params errno %d', - fd, - op, - errno - ); - return -errno; - } - - // @TODO: Consider supporting blocking mode of flock() - if (op & (emscripten_LOCK_NB === 0)) { - _js_wasm_trace( - 'js_flock(%d, %d) blocking mode of flock() is not implemented', - fd, - op - ); - // We do not yet support the blocking form of flock(). - // We respond with EINVAL to indicate failure - // because it is a known errno for a failed blocking flock(). - return -ERRNO_CODES.EINVAL; - } - - const maskedOp = - op & - (emscripten_LOCK_SH | emscripten_LOCK_EX | emscripten_LOCK_UN); - - const lockOpType = flockToLockOpType[maskedOp]; - if (lockOpType === undefined) { - _js_wasm_trace( - 'js_flock(%d, %d) invalid flock() operation', - fd, - op - ); - return -ERRNO_CODES.EINVAL; - } - - try { - const nativeFilePath = - locking.get_native_path_from_vfs_path(vfsPath); - const obtainedLock = ( - PHPLoader.fileLockManager.lockWholeFile( - nativeFilePath, - { - type: lockOpType, - pid: PHPLoader.processId, - fd, - } - ) - ); - _js_wasm_trace( - 'js_flock(%d, %d) lockWholeFile %s returned %d', - fd, - op, - vfsPath, - obtainedLock - ); - if (obtainedLock) { - locking.maybeLockedFds.add(fd); - return 0; - } else { - return -ERRNO_CODES.EWOULDBLOCK; - } - } catch (e) { - _js_wasm_trace('js_flock(%d, %d) lockWholeFile error %s', fd, op, e); - return -ERRNO_CODES.EINVAL; - } - } - - - - - function _js_open_process( - command, - argsPtr, - argsLength, - descriptorsPtr, - descriptorsLength, - cwdPtr, - cwdLength, - envPtr, - envLength - ) { - if (!command) { - ___errno_location(ERRNO_CODES.EINVAL); - return -1; - } - - const cmdstr = UTF8ToString(command); - if (!cmdstr.length) { - ___errno_location(ERRNO_CODES.EINVAL); - return -1; - } - - let argsArray = []; - if (argsLength) { - for (var i = 0; i < argsLength; i++) { - const charPointer = argsPtr + i * 4; - argsArray.push(UTF8ToString(HEAPU32[charPointer >> 2])); - } - } - - const cwdstr = cwdPtr ? UTF8ToString(cwdPtr) : FS.cwd(); - let envObject = null; - - if (envLength) { - envObject = {}; - for (var i = 0; i < envLength; i++) { - const envPointer = envPtr + i * 4; - const envEntry = UTF8ToString(HEAPU32[envPointer >> 2]); - const splitAt = envEntry.indexOf('='); - if (splitAt === -1) { - continue; - } - const key = envEntry.substring(0, splitAt); - const value = envEntry.substring(splitAt + 1); - envObject[key] = value; - } - } - - var std = {}; - // Extracts an array of available descriptors that should be dispatched to streams. - // On the C side, the descriptors are expressed as `**int` so we must go read - // each of the `descriptorsLength` `*int` pointers and convert the associated data into - // a JavaScript object { descriptor : { child : fd, parent : fd } }. - for (var i = 0; i < descriptorsLength; i++) { - const descriptorPtr = HEAPU32[(descriptorsPtr + i * 4) >> 2]; - std[HEAPU32[descriptorPtr >> 2]] = { - child: HEAPU32[(descriptorPtr + 4) >> 2], - parent: HEAPU32[(descriptorPtr + 8) >> 2], - }; - // swap parent and child descs until we rebuild PHP 7.4 - if (i === 0) { - HEAPU32[(descriptorPtr + 8) >> 2] = std[HEAPU32[descriptorPtr >> 2]].parent; - HEAPU32[(descriptorPtr + 4) >> 2] = std[HEAPU32[descriptorPtr >> 2]].child; - } - } - - return Asyncify.handleAsync(async () => { - let cp; - try { - const options = {}; - if (cwdstr !== null) { - options.cwd = cwdstr; - } - if (envObject !== null) { - options.env = envObject; - } - cp = PHPWASM.spawnProcess(cmdstr, argsArray, options); - if (cp instanceof Promise) { - cp = await cp; - } - } catch (e) { - if (e.code === 'SPAWN_UNSUPPORTED') { - ___errno_location(ERRNO_CODES.ENOSYS); - return -1; - } - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - ___errno_location(e.code); - return -1; - } - - const ProcInfo = { - pid: cp.pid, - exited: false - }; - PHPWASM.processTable[ProcInfo.pid] = ProcInfo; - - const stdinParentFd = std[0]?.parent, - stdinChildFd = std[0]?.child, - stdoutChildFd = std[1]?.child, - stdoutParentFd = std[1]?.parent, - stderrChildFd = std[2]?.child, - stderrParentFd = std[2]?.parent; - - cp.on('exit', function (code) { - for (const fd of [ - // The child process exited. Let's clean up its output streams: - stdoutChildFd, - stderrChildFd, - stdinChildFd, - - // We won't close these because the PHP already handles that in the parent process: - // stdoutParentFd, - // stderrParentFd, - // stdinParentFd, - ]) { - if (FS.streams[fd] && !FS.isClosed(FS.streams[fd])) { - FS.close(FS.streams[fd]); - } - } - - ProcInfo.exitCode = code; - ProcInfo.exited = true; - }); - - // Pass data from child process's stdout to PHP's end of the stdout pipe. - if (stdoutChildFd) { - const stdoutStream = SYSCALLS.getStreamFromFD( - stdoutChildFd - ); - let stdoutAt = 0; - cp.stdout.on('data', function (data) { - stdoutStream.stream_ops.write( - stdoutStream, - data, - 0, - data.length, - stdoutAt - ); - stdoutAt += data.length; - }); - } - - // Pass data from child process's stderr to PHP's end of the stdout pipe. - if (stderrChildFd) { - const stderrStream = SYSCALLS.getStreamFromFD( - stderrChildFd - ); - let stderrAt = 0; - cp.stderr.on('data', function (data) { - stderrStream.stream_ops.write( - stderrStream, - data, - 0, - data.length, - stderrAt - ); - stderrAt += data.length; - }); - } - - /** - * Wait until the child process has been spawned. - * Unfortunately there is no Node.js API to check whether - * the process has already been spawned. We can only listen - * to the 'spawn' event and if it has already been spawned, - * listen to the 'exit' event. - */ - try { - await new Promise((resolve, reject) => { - /** - * There was no `await` between the `spawnProcess` call - * and the `await` below so the process haven't had a chance - * to run any of the exit-related callbacks yet. - * - * Good. - * - * Let's listen to all the lifecycle events and resolve - * the promise when the process starts or immediately crashes. - */ - let resolved = false; - cp.on('spawn', () => { - if (resolved) return; - resolved = true; - resolve(); - }); - cp.on('error', (e) => { - if (resolved) return; - resolved = true; - reject(e); - }); - cp.on('exit', function (code) { - if (resolved) return; - resolved = true; - if (code === 0) { - resolve(); - } else { - reject( - new Error(`Process exited with code ${code}`) - ); - } - }); - /** - * If the process haven't even started after 5 seconds, something - * is wrong. Perhaps we're missing an event listener, or perhaps - * the `spawnProcess` implementation failed to dispatch the relevant - * event. Either way, let's crash to avoid blocking the proc_open() - * call indefinitely. - */ - setTimeout(() => { - if (resolved) return; - resolved = true; - reject(new Error('Process timed out')); - }, 5000); - }); - } catch (e) { - // Process already started. Even if it exited early, PHP still - // needs to know about the pid and clean up the resources. - console.error(e); - return ProcInfo.pid; - } - - // Now we want to pass data from the STDIN source supplied by PHP - // to the child process. - if (stdinChildFd) { - // We're in a kernel function used instead of fork(). - // - // We are the ones responsible for pumping the data from the stdinChildFd - // into the child process. There is no concurrent task operating on the - // piped data or polling the file descriptors, etc. Nothing will ever - // read from the stdinChildFd if we don't do it here. - // - // Well, let's do it! We'll periodically read from the child end of the - // data pipe and push what we get into the child process. - let stdinStream; - try { - stdinStream = SYSCALLS.getStreamFromFD(stdinChildFd); - } catch (e) { - ___errno_location(ERRNO_CODES.EBADF); - return ProcInfo.pid; - } - if (!stdinStream?.node) { - return ProcInfo.pid; - } - - // Pipe the entire stdinStream to cp.stdin - const CHUNK_SIZE = 1024; - - const iov = _malloc(16); // Space for iovec structure - const pnum = _malloc(4); // Space for number of bytes read - const buffer = _malloc(CHUNK_SIZE); - - // Set up iovec structure pointing to our buffer - HEAPU32[iov >> 2] = buffer; // iov_base - HEAPU32[(iov + 4) >> 2] = CHUNK_SIZE; // iov_len - - function pump() { - try { - while (true) { - if (cp.killed) { - stopPumpingAndCloseStdin(); - return; - } - - const result = js_fd_read( - stdinChildFd, - iov, - 1, - pnum, - false - ); - const bytesRead = HEAPU32[pnum >> 2]; - if (result === 0 && bytesRead > 0) { - const wrote = HEAPU8.subarray( - buffer, - buffer + bytesRead - ); - cp.stdin.write(wrote); - // We've read some data. Let the next iteration decide - // how to break out of the loop. - } else if (result === 0 && bytesRead === 0) { - // result === 0 and bytesRead === 0 means the file descriptor - // is at EOF. Let's close the stdin stream and clean up. - stopPumpingAndCloseStdin(); - break; - } else if (result === ERRNO_CODES.EAGAIN) { - // The file descriptor is not ready for reading. - // Let's break out of the loop. setInterval will invoke - // this function again soon. - break; - } else { - throw new FS.ErrnoError(result); - } - } - } catch (e) { - if ( - typeof FS == 'undefined' || - !(e.name === 'ErrnoError') - ) { - throw e; - } - ___errno_location(e.errno); - stopPumpingAndCloseStdin(); - } - }; - function stopPumpingAndCloseStdin() { - clearInterval(interval); - if (!cp.stdin.closed) { - cp.stdin.end(); - } - _free(buffer); - _free(iov); - _free(pnum); - } - - // pump() can never alter the result of this function. - // Even when it fails, we still return the pid. - // Why? - // Because the process already started. We wouldn't backtrack - // with fork(), we won't backtrack here. Let's give PHP the pid, - // and let it think it's the parent process. It will clean up the - // resources as needed. - - // stdin may be non-blocking – let's check for updates periodically. - // If we exhaust it at any point, pump() will self-terminate. - // - // Note handling any failures, closing the descriptor, etc. will not - // happen synchronously when PHP calls fclose($pipes[0]) or proc_close(). - // It will all happen asynchronously on the next tick. It seems off, - // but there doesn't seem to be a better way: cp.stdin.write() and - // cp.stdin.end() are both async APIs and they both accept onCompleted - // callbacks. - const interval = setInterval(pump, 20); - pump(); - } - - return ProcInfo.pid; - }); - } - - - function _js_process_status(pid, exitCodePtr) { - if (!PHPWASM.processTable[pid]) { - return -1; - } - if (PHPWASM.processTable[pid].exited) { - HEAPU32[exitCodePtr >> 2] = PHPWASM.processTable[pid].exitCode; - return 1; - } - return 0; - } - - - - - function _js_release_file_locks() { - _js_wasm_trace('js_release_file_locks()'); - const pid = PHPLoader.processId; - if (!pid || !PHPLoader.fileLockManager) { - _js_wasm_trace('js_release_file_locks no pid or file lock manager'); - return 0; - } - - try { - PHPLoader.fileLockManager - .releaseLocksForProcess(pid) - _js_wasm_trace('js_release_file_locks succeeded'); - } catch (e) { - _js_wasm_trace('js_release_file_locks error %s', e); - } - } - - - function _js_waitpid(pid, exitCodePtr) { - if (!PHPWASM.processTable[pid]) { - return -1; - } - return Asyncify.handleSleep((wakeUp) => { - const poll = function () { - if (PHPWASM.processTable[pid]?.exited) { - HEAPU32[exitCodePtr >> 2] = - PHPWASM.processTable[pid].exitCode; - wakeUp(pid); - } else { - setTimeout(poll, 50); - } - }; - poll(); - }); - } - - - - function _random_get(buffer, size) { - try { - - randomFill(HEAPU8.subarray(buffer, buffer + size)); - return 0; - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return e.errno; - } - } - _random_get.sig = 'ipp'; - - - var arraySum = (array, index) => { - var sum = 0; - for (var i = 0; i <= index; sum += array[i++]) { - // no-op - } - return sum; - }; - - - var MONTH_DAYS_LEAP = [31,29,31,30,31,30,31,31,30,31,30,31]; - - var MONTH_DAYS_REGULAR = [31,28,31,30,31,30,31,31,30,31,30,31]; - var addDays = (date, days) => { - var newDate = new Date(date.getTime()); - while (days > 0) { - var leap = isLeapYear(newDate.getFullYear()); - var currentMonth = newDate.getMonth(); - var daysInCurrentMonth = (leap ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[currentMonth]; - - if (days > daysInCurrentMonth-newDate.getDate()) { - // we spill over to next month - days -= (daysInCurrentMonth-newDate.getDate()+1); - newDate.setDate(1); - if (currentMonth < 11) { - newDate.setMonth(currentMonth+1) - } else { - newDate.setMonth(0); - newDate.setFullYear(newDate.getFullYear()+1); - } - } else { - // we stay in current month - newDate.setDate(newDate.getDate()+days); - return newDate; - } - } - - return newDate; - }; - - - - - var _strptime = (buf, format, tm) => { - // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html - var pattern = UTF8ToString(format); - - // escape special characters - // TODO: not sure we really need to escape all of these in JS regexps - var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.'; - for (var i=0, ii=SPECIAL_CHARS.length; i EQUIVALENT_MATCHERS[c] || m) - .replace(/%(.)/g, (_, c) => { - let pat = DATE_PATTERNS[c]; - if (pat){ - capture.push(c); - return `(${pat})`; - } else { - return c; - } - }) - .replace( // any number of space or tab characters match zero or more spaces - /\s+/g,'\\s*' - ); - - var matches = new RegExp('^'+pattern_out, "i").exec(UTF8ToString(buf)) - - function initDate() { - function fixup(value, min, max) { - return (typeof value != 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min); - }; - return { - year: fixup(HEAP32[(((tm)+(20))>>2)] + 1900 , 1970, 9999), - month: fixup(HEAP32[(((tm)+(16))>>2)], 0, 11), - day: fixup(HEAP32[(((tm)+(12))>>2)], 1, 31), - hour: fixup(HEAP32[(((tm)+(8))>>2)], 0, 23), - min: fixup(HEAP32[(((tm)+(4))>>2)], 0, 59), - sec: fixup(HEAP32[((tm)>>2)], 0, 59), - gmtoff: 0 - }; - }; - - if (matches) { - var date = initDate(); - var value; - - var getMatch = (symbol) => { - var pos = capture.indexOf(symbol); - // check if symbol appears in regexp - if (pos >= 0) { - // return matched value or null (falsy!) for non-matches - return matches[pos+1]; - } - return; - }; - - // seconds - if ((value=getMatch('S'))) { - date.sec = Number(value); - } - - // minutes - if ((value=getMatch('M'))) { - date.min = Number(value); - } - - // hours - if ((value=getMatch('H'))) { - // 24h clock - date.hour = Number(value); - } else if ((value = getMatch('I'))) { - // AM/PM clock - var hour = Number(value); - if ((value=getMatch('p'))) { - hour += value.toUpperCase()[0] === 'P' ? 12 : 0; - } - date.hour = hour; - } - - // year - if ((value=getMatch('Y'))) { - // parse from four-digit year - date.year = Number(value); - } else if ((value=getMatch('y'))) { - // parse from two-digit year... - var year = Number(value); - if ((value=getMatch('C'))) { - // ...and century - year += Number(value)*100; - } else { - // ...and rule-of-thumb - year += year<69 ? 2000 : 1900; - } - date.year = year; - } - - // month - if ((value=getMatch('m'))) { - // parse from month number - date.month = Number(value)-1; - } else if ((value=getMatch('b'))) { - // parse from month name - date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0; - // TODO: derive month from day in year+year, week number+day of week+year - } - - // day - if ((value=getMatch('d'))) { - // get day of month directly - date.day = Number(value); - } else if ((value=getMatch('j'))) { - // get day of month from day of year ... - var day = Number(value); - var leapYear = isLeapYear(date.year); - for (var month=0; month<12; ++month) { - var daysUntilMonth = arraySum(leapYear ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, month-1); - if (day<=daysUntilMonth+(leapYear ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[month]) { - date.day = day-daysUntilMonth; - } - } - } else if ((value=getMatch('a'))) { - // get day of month from weekday ... - var weekDay = value.substring(0,3).toUpperCase(); - if ((value=getMatch('U'))) { - // ... and week number (Sunday being first day of week) - // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. - // All days in a new year preceding the first Sunday are considered to be in week 0. - var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay]; - var weekNumber = Number(value); - - // January 1st - var janFirst = new Date(date.year, 0, 1); - var endDate; - if (janFirst.getDay() === 0) { - // Jan 1st is a Sunday, and, hence in the 1st CW - endDate = addDays(janFirst, weekDayNumber+7*(weekNumber-1)); - } else { - // Jan 1st is not a Sunday, and, hence still in the 0th CW - endDate = addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1)); - } - date.day = endDate.getDate(); - date.month = endDate.getMonth(); - } else if ((value=getMatch('W'))) { - // ... and week number (Monday being first day of week) - // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. - // All days in a new year preceding the first Monday are considered to be in week 0. - var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay]; - var weekNumber = Number(value); - - // January 1st - var janFirst = new Date(date.year, 0, 1); - var endDate; - if (janFirst.getDay()===1) { - // Jan 1st is a Monday, and, hence in the 1st CW - endDate = addDays(janFirst, weekDayNumber+7*(weekNumber-1)); - } else { - // Jan 1st is not a Monday, and, hence still in the 0th CW - endDate = addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1)); - } - - date.day = endDate.getDate(); - date.month = endDate.getMonth(); - } - } - - // time zone - if ((value = getMatch('z'))) { - // GMT offset as either 'Z' or +-HH:MM or +-HH or +-HHMM - if (value.toLowerCase() === 'z'){ - date.gmtoff = 0; - } else { - var match = value.match(/^((?:\-|\+)\d\d):?(\d\d)?/); - date.gmtoff = match[1] * 3600; - if (match[2]) { - date.gmtoff += date.gmtoff >0 ? match[2] * 60 : -match[2] * 60 - } - } - } - - /* - tm_sec int seconds after the minute 0-61* - tm_min int minutes after the hour 0-59 - tm_hour int hours since midnight 0-23 - tm_mday int day of the month 1-31 - tm_mon int months since January 0-11 - tm_year int years since 1900 - tm_wday int days since Sunday 0-6 - tm_yday int days since January 1 0-365 - tm_isdst int Daylight Saving Time flag - tm_gmtoff long offset from GMT (seconds) - */ - - var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); - HEAP32[((tm)>>2)] = fullDate.getSeconds(); - HEAP32[(((tm)+(4))>>2)] = fullDate.getMinutes(); - HEAP32[(((tm)+(8))>>2)] = fullDate.getHours(); - HEAP32[(((tm)+(12))>>2)] = fullDate.getDate(); - HEAP32[(((tm)+(16))>>2)] = fullDate.getMonth(); - HEAP32[(((tm)+(20))>>2)] = fullDate.getFullYear()-1900; - HEAP32[(((tm)+(24))>>2)] = fullDate.getDay(); - HEAP32[(((tm)+(28))>>2)] = arraySum(isLeapYear(fullDate.getFullYear()) ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1; - HEAP32[(((tm)+(32))>>2)] = 0; - HEAP32[(((tm)+(36))>>2)] = date.gmtoff; - - // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F - // TODO: not sure that intArrayFromString handles all unicode characters correctly - return buf+lengthBytesUTF8(matches[0]); - } - - return 0; - }; - _strptime.sig = 'pppp'; - - - function _wasm_close(socketd) { - return PHPWASM.shutdownSocket(socketd, 2); - } - - - function _wasm_setsockopt( - socketd, - level, - optionName, - optionValuePtr, - optionLen - ) { - const optionValue = HEAPU8[optionValuePtr]; - const SOL_SOCKET = 1; - const SO_KEEPALIVE = 9; - const IPPROTO_TCP = 6; - const TCP_NODELAY = 1; - const isSupported = - (level === SOL_SOCKET && optionName === SO_KEEPALIVE) || - (level === IPPROTO_TCP && optionName === TCP_NODELAY); - if (!isSupported) { - console.warn( - `Unsupported socket option: ${level}, ${optionName}, ${optionValue}` - ); - return -1; - } - const ws = PHPWASM.getAllWebSockets(socketd)[0]; - if (!ws) { - return -1; - } - ws.setSocketOpt(level, optionName, optionValuePtr); - return 0; - } - - - - - - - var runAndAbortIfError = (func) => { - try { - return func(); - } catch (e) { - abort(e); - } - }; - - - - - - - - var Asyncify = { - instrumentWasmImports(imports) { - var importPattern = /^(invoke_i|invoke_ii|invoke_iii|invoke_iiii|invoke_iiiii|invoke_iiiiii|invoke_iiiiiii|invoke_iiiiiiii|invoke_iiiiiiiii|invoke_iiiiiiiiii|invoke_v|invoke_vi|invoke_vii|invoke_viidii|invoke_viii|invoke_viiii|invoke_viiiii|invoke_viiiiii|invoke_viiiiiii|invoke_viiiiiiiii|invoke_i|invoke_ii|invoke_iii|invoke_iiii|invoke_iiiii|invoke_iiiiii|invoke_iiiiiii|invoke_iiiiiiii|invoke_iiiiiiiiii|invoke_iij|invoke_iiji|invoke_iiij|invoke_iijii|invoke_iijiji|invoke_jii|invoke_jiii|invoke_viijii|invoke_vji|js_open_process|_js_open_process|_asyncjs__js_open_process|js_popen_to_file|_js_popen_to_file|_asyncjs__js_popen_to_file|__syscall_fcntl64|___syscall_fcntl64|_asyncjs___syscall_fcntl64|js_release_file_locks|_js_release_file_locks|_async_js_release_file_locks|js_flock|_js_flock|_async_js_flock|js_fd_read|_js_fd_read|fd_close|_fd_close|_asyncjs__fd_close|close|_close|js_module_onMessage|zend_hash_str_find|_js_module_onMessage|_asyncjs__js_module_onMessage|js_waitpid|_js_waitpid|_asyncjs__js_waitpid|wasm_poll_socket|_wasm_poll_socket|_asyncjs__wasm_poll_socket|_wasm_shutdown|_asyncjs__wasm_shutdown|__asyncjs__.*)$/; - - for (let [x, original] of Object.entries(imports)) { - if (typeof original == 'function') { - let isAsyncifyImport = original.isAsync || importPattern.test(x); - } - } - }, - instrumentFunction(original) { - var wrapper = (...args) => { - Asyncify.exportCallStack.push(original); - try { - return original(...args); - } finally { - if (!ABORT) { - var top = Asyncify.exportCallStack.pop(); - Asyncify.maybeStopUnwind(); - } - } - }; - Asyncify.funcWrappers.set(original, wrapper); - wrapper.orig = original; - return wrapper; - }, - instrumentWasmExports(exports) { - var ret = {}; - for (let [x, original] of Object.entries(exports)) { - if (typeof original == 'function') { - var wrapper = Asyncify.instrumentFunction(original); - ret[x] = wrapper; - - } else { - ret[x] = original; - } - } - return ret; - }, - State:{ - Normal:0, - Unwinding:1, - Rewinding:2, - Disabled:3, - }, - state:0, - StackSize:4096, - currData:null, - handleSleepReturnValue:0, - exportCallStack:[], - callstackFuncToId:new Map, - callStackIdToFunc:new Map, - funcWrappers:new Map, - callStackId:0, - asyncPromiseHandlers:null, - sleepCallbacks:[], - getCallStackId(func) { - if (!Asyncify.callstackFuncToId.has(func)) { - var id = Asyncify.callStackId++; - Asyncify.callstackFuncToId.set(func, id); - Asyncify.callStackIdToFunc.set(id, func); - } - return Asyncify.callstackFuncToId.get(func); - }, - 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() { - 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 (callStackIdToFunc[id] == wasm func) - // - // 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) { - HEAPU32[((ptr)>>2)] = stack; - HEAPU32[(((ptr)+(4))>>2)] = stack + stackSize; - }, - setDataRewindFunc(ptr) { - var bottomOfCallStack = Asyncify.exportCallStack[0]; - var rewindId = Asyncify.getCallStackId(bottomOfCallStack); - HEAP32[(((ptr)+(8))>>2)] = rewindId; - }, - getDataRewindFunc(ptr) { - var id = HEAP32[(((ptr)+(8))>>2)]; - var func = Asyncify.callStackIdToFunc.get(id); - return func; - }, - doRewind(ptr) { - var original = Asyncify.getDataRewindFunc(ptr); - var func = Asyncify.funcWrappers.get(original); - // Once we have rewound and the stack we no longer need to artificially - // keep the runtime alive. - runtimeKeepalivePop(); - return func(); - }, - handleSleep(startAsync) { - 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) => { - if (ABORT) return; - Asyncify.handleSleepReturnValue = handleSleepReturnValue; - reachedCallback = true; - if (!reachedAfterCallback) { - // We are happening synchronously, so no need for async. - return; - } - 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) => 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 - return func; - }; - - var writeArrayToMemory = (array, buffer) => { - HEAP8.set(array, buffer); - }; - - - - - - - - - /** - * @param {string|null=} returnType - * @param {Array=} argTypes - * @param {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; - 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) { - // 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. - 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; - }; - - - var FS_createPath = (...args) => FS.createPath(...args); - - - - var FS_unlink = (...args) => FS.unlink(...args); + var FS_chrdev_stream_ops = (...args) => FS.chrdev_stream_ops(...args); - var FS_createLazyFile = (...args) => FS.createLazyFile(...args); - - var FS_createDevice = (...args) => FS.createDevice(...args); - - - - - var writeI53ToI64Clamped = (ptr, num) => { - if (num > 0x7FFFFFFFFFFFFFFF) { - HEAPU32[((ptr)>>2)] = 4294967295; - HEAPU32[(((ptr)+(4))>>2)] = 2147483647; - } else if (num < -0x8000000000000000) { - HEAPU32[((ptr)>>2)] = 0; - HEAPU32[(((ptr)+(4))>>2)] = 2147483648; - } else { - writeI53ToI64(ptr, num); - } - }; - - var writeI53ToI64Signaling = (ptr, num) => { - if (num > 0x7FFFFFFFFFFFFFFF || num < -0x8000000000000000) { - throw `RangeError: ${num}`; - } - writeI53ToI64(ptr, num); - }; - - var writeI53ToU64Clamped = (ptr, num) => { - if (num > 0xFFFFFFFFFFFFFFFF) { - HEAPU32[((ptr)>>2)] = 4294967295; - HEAPU32[(((ptr)+(4))>>2)] = 4294967295; - } else if (num < 0) { - HEAPU32[((ptr)>>2)] = 0; - HEAPU32[(((ptr)+(4))>>2)] = 0; - } else { - writeI53ToI64(ptr, num); - } - }; - - var writeI53ToU64Signaling = (ptr, num) => { - if (num < 0 || num > 0xFFFFFFFFFFFFFFFF) { - throw `RangeError: ${num}`; - } - writeI53ToI64(ptr, num); - }; - - - var readI53FromU64 = (ptr) => { - return HEAPU32[((ptr)>>2)] + HEAPU32[(((ptr)+(4))>>2)] * 4294967296; - }; - - var convertI32PairToI53 = (lo, hi) => { - return (lo >>> 0) + hi * 4294967296; - }; - - var convertI32PairToI53Checked = (lo, hi) => { - return ((hi + 0x200000) >>> 0 < 0x400001 - !!lo) ? (lo >>> 0) + hi * 4294967296 : NaN; - }; - - var convertU32PairToI53 = (lo, hi) => { - return (lo >>> 0) + (hi >>> 0) * 4294967296; - }; + var FS_major = (...args) => FS.major(...args); + var FS_minor = (...args) => FS.minor(...args); + var FS_makedev = (...args) => FS.makedev(...args); + var FS_registerDevice = (...args) => FS.registerDevice(...args); + var FS_getDevice = (...args) => FS.getDevice(...args); + var FS_getMounts = (...args) => FS.getMounts(...args); + var FS_syncfs = (...args) => FS.syncfs(...args); - var getTempRet0 = (val) => __emscripten_tempret_get(); + var FS_mount = (...args) => FS.mount(...args); + var FS_unmount = (...args) => FS.unmount(...args); - var _stackAlloc = stackAlloc; + var FS_lookup = (...args) => FS.lookup(...args); - var _stackSave = stackSave; + var FS_mknod = (...args) => FS.mknod(...args); - var _stackRestore = stackSave; + var FS_statfs = (...args) => FS.statfs(...args); - var _setTempRet0 = setTempRet0; + var FS_statfsStream = (...args) => FS.statfsStream(...args); - var _getTempRet0 = getTempRet0; + var FS_statfsNode = (...args) => FS.statfsNode(...args); + var FS_create = (...args) => FS.create(...args); - var ptrToString = (ptr) => { - // Convert to 32-bit unsigned value - ptr >>>= 0; - return '0x' + ptr.toString(16).padStart(8, '0'); - }; + var FS_mkdir = (...args) => FS.mkdir(...args); + var FS_mkdev = (...args) => FS.mkdev(...args); + var FS_symlink = (...args) => FS.symlink(...args); + var FS_rename = (...args) => FS.rename(...args); + var FS_rmdir = (...args) => FS.rmdir(...args); + var FS_readdir = (...args) => FS.readdir(...args); + var FS_readlink = (...args) => FS.readlink(...args); + var FS_stat = (...args) => FS.stat(...args); - var _emscripten_notify_memory_growth = (memoryIndex) => { - updateMemoryViews(); - }; - _emscripten_notify_memory_growth.sig = 'vp'; + var FS_fstat = (...args) => FS.fstat(...args); + var FS_lstat = (...args) => FS.lstat(...args); + var FS_doChmod = (...args) => FS.doChmod(...args); + var FS_chmod = (...args) => FS.chmod(...args); + var FS_lchmod = (...args) => FS.lchmod(...args); + var FS_fchmod = (...args) => FS.fchmod(...args); + var FS_doChown = (...args) => FS.doChown(...args); + var FS_chown = (...args) => FS.chown(...args); - - - var strError = (errno) => UTF8ToString(_strerror(errno)); + var FS_lchown = (...args) => FS.lchown(...args); + var FS_fchown = (...args) => FS.fchown(...args); + var FS_doTruncate = (...args) => FS.doTruncate(...args); + var FS_truncate = (...args) => FS.truncate(...args); + var FS_ftruncate = (...args) => FS.ftruncate(...args); + var FS_utime = (...args) => FS.utime(...args); + var FS_open = (...args) => FS.open(...args); + var FS_close = (...args) => FS.close(...args); + var FS_isClosed = (...args) => FS.isClosed(...args); + var FS_llseek = (...args) => FS.llseek(...args); + var FS_read = (...args) => FS.read(...args); + var FS_write = (...args) => FS.write(...args); + var FS_mmap = (...args) => FS.mmap(...args); - var _endprotoent = () => { - // void endprotoent(void); - // We're not using a real protocol database so we don't do a real close. - }; - _endprotoent.sig = 'v'; - - - var _getprotoent = (number) => { - // struct protoent *getprotoent(void); - // reads the next entry from the protocols 'database' or return NULL if 'eof' - if (_setprotoent.index === Protocols.list.length) { - return 0; - } - var result = Protocols.list[_setprotoent.index++]; - return result; - }; - _getprotoent.sig = 'p'; - - - - var Sockets = { - BUFFER_SIZE:10240, - MAX_BUFFER_SIZE:10485760, - nextFd:1, - fds:{ - }, - nextport:1, - maxport:65535, - peer:null, - connections:{ - }, - portmap:{ - }, - localAddr:4261412874, - addrPool:[33554442,50331658,67108874,83886090,100663306,117440522,134217738,150994954,167772170,184549386,201326602,218103818,234881034], - }; - - - - - var _emscripten_run_script = (ptr) => { - eval(UTF8ToString(ptr)); - }; - _emscripten_run_script.sig = 'vp'; - - /** @suppress{checkTypes} */ - var _emscripten_run_script_int = (ptr) => { - return eval(UTF8ToString(ptr))|0; - }; - _emscripten_run_script_int.sig = 'ip'; - - - - - var _emscripten_run_script_string = (ptr) => { - var s = eval(UTF8ToString(ptr)); - if (s == null) { - return 0; - } - s += ''; - var me = _emscripten_run_script_string; - me.bufferSize = lengthBytesUTF8(s) + 1; - me.buffer = _realloc(me.buffer ?? 0, me.bufferSize) - stringToUTF8(s, me.buffer, me.bufferSize); - return me.buffer; - }; - _emscripten_run_script_string.sig = 'pp'; - - var _emscripten_random = () => Math.random(); - _emscripten_random.sig = 'f'; - - - var _emscripten_performance_now = () => performance.now(); - _emscripten_performance_now.sig = 'd'; + var FS_msync = (...args) => FS.msync(...args); + var FS_ioctl = (...args) => FS.ioctl(...args); + var FS_writeFile = (...args) => FS.writeFile(...args); + var FS_cwd = (...args) => FS.cwd(...args); - var __emscripten_get_now_is_monotonic = () => nowIsMonotonic; - __emscripten_get_now_is_monotonic.sig = 'i'; + var FS_chdir = (...args) => FS.chdir(...args); - var warnOnce = (text) => { - warnOnce.shown ||= {}; - if (!warnOnce.shown[text]) { - warnOnce.shown[text] = 1; - if (ENVIRONMENT_IS_NODE) text = 'warning: ' + text; - err(text); - } - }; - - - var _emscripten_get_compiler_setting = (name) => abort('You must build with -sRETAIN_COMPILER_SETTINGS for getCompilerSetting or emscripten_get_compiler_setting to work'); - _emscripten_get_compiler_setting.sig = 'pp'; - - var _emscripten_has_asyncify = () => 1; - _emscripten_has_asyncify.sig = 'i'; + var FS_createDefaultDirectories = (...args) => + FS.createDefaultDirectories(...args); - var _emscripten_debugger = () => { debugger }; - _emscripten_debugger.sig = 'v'; + var FS_createDefaultDevices = (...args) => FS.createDefaultDevices(...args); - - var _emscripten_print_double = (x, to, max) => { - var str = x + ''; - if (to) return stringToUTF8(str, to, max); - else return lengthBytesUTF8(str); - }; - _emscripten_print_double.sig = 'idpi'; + var FS_createSpecialDirectories = (...args) => + FS.createSpecialDirectories(...args); + var FS_createStandardStreams = (...args) => + FS.createStandardStreams(...args); + var FS_staticInit = (...args) => FS.staticInit(...args); + var FS_init = (...args) => FS.init(...args); + var FS_quit = (...args) => FS.quit(...args); - var _emscripten_asm_const_double = (code, sigPtr, argbuf) => { - return runEmAsmFunction(code, sigPtr, argbuf); - }; - _emscripten_asm_const_double.sig = 'dppp'; + var FS_findObject = (...args) => FS.findObject(...args); - var _emscripten_asm_const_ptr = (code, sigPtr, argbuf) => { - return runEmAsmFunction(code, sigPtr, argbuf); - }; - _emscripten_asm_const_ptr.sig = 'pppp'; + var FS_analyzePath = (...args) => FS.analyzePath(...args); - var runMainThreadEmAsm = (emAsmAddr, sigPtr, argbuf, sync) => { - var args = readEmAsmArgs(sigPtr, argbuf); - return ASM_CONSTS[emAsmAddr](...args); - }; + var FS_createFile = (...args) => FS.createFile(...args); - var _emscripten_asm_const_int_sync_on_main_thread = (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1); - _emscripten_asm_const_int_sync_on_main_thread.sig = 'ippp'; + var FS_forceLoadFile = (...args) => FS.forceLoadFile(...args); - var _emscripten_asm_const_ptr_sync_on_main_thread = (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 1); - _emscripten_asm_const_ptr_sync_on_main_thread.sig = 'pppp'; + var _setNetworkCallback = (event, userData, callback) => { + function _callback(data) { + callUserCallback(() => { + if (event === 'error') { + withStackSave(() => { + var msg = stringToUTF8OnStack(data[2]); + (( + a1, + a2, + a3, + a4 + ) => {}) /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + data[0], + data[1], + msg, + userData + ); + }); + } else { + (( + a1, + a2 + ) => {}) /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */( + data, + userData + ); + } + }); + } - var _emscripten_asm_const_double_sync_on_main_thread = _emscripten_asm_const_int_sync_on_main_thread; - _emscripten_asm_const_double_sync_on_main_thread.sig = 'dppp'; + // FIXME(sbc): This has no corresponding Pop so will currently keep the + // runtime alive indefinitely. + runtimeKeepalivePush(); + SOCKFS.on(event, callback ? _callback : null); + }; - var _emscripten_asm_const_async_on_main_thread = (emAsmAddr, sigPtr, argbuf) => runMainThreadEmAsm(emAsmAddr, sigPtr, argbuf, 0); - _emscripten_asm_const_async_on_main_thread.sig = 'vppp'; + var _emscripten_set_socket_error_callback = (userData, callback) => + _setNetworkCallback('error', userData, callback); + _emscripten_set_socket_error_callback.sig = 'vpp'; + var _emscripten_set_socket_open_callback = (userData, callback) => + _setNetworkCallback('open', userData, callback); + _emscripten_set_socket_open_callback.sig = 'vpp'; - var __Unwind_Backtrace = (func, arg) => { - var trace = getCallstack(); - var parts = trace.split('\n'); - for (var i = 0; i < parts.length; i++) { - var ret = ((a1, a2) => {} /* a dynamic function call to signature iii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(0, arg); - if (ret !== 0) return; - } - }; - __Unwind_Backtrace.sig = 'ipp'; - - var __Unwind_GetIPInfo = (context, ipBefore) => abort('Unwind_GetIPInfo'); - __Unwind_GetIPInfo.sig = 'ppp'; + var _emscripten_set_socket_listen_callback = (userData, callback) => + _setNetworkCallback('listen', userData, callback); + _emscripten_set_socket_listen_callback.sig = 'vpp'; - var __Unwind_FindEnclosingFunction = (ip) => 0; - __Unwind_FindEnclosingFunction.sig = 'pp'; + var _emscripten_set_socket_connection_callback = (userData, callback) => + _setNetworkCallback('connection', userData, callback); + _emscripten_set_socket_connection_callback.sig = 'vpp'; - - - 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++; - throw exceptionLast; - }; - ___cxa_throw.sig = 'vppp'; - var __Unwind_RaiseException = (ex) => { - err('Warning: _Unwind_RaiseException is not correctly implemented'); - return ___cxa_throw(ex, 0, 0); - }; - __Unwind_RaiseException.sig = 'ip'; + var _emscripten_set_socket_message_callback = (userData, callback) => + _setNetworkCallback('message', userData, callback); + _emscripten_set_socket_message_callback.sig = 'vpp'; - var __Unwind_DeleteException = (ex) => err('TODO: Unwind_DeleteException'); - __Unwind_DeleteException.sig = 'vp'; + var _emscripten_set_socket_close_callback = (userData, callback) => + _setNetworkCallback('close', userData, callback); + _emscripten_set_socket_close_callback.sig = 'vpp'; + var _emscripten_webgl_enable_ANGLE_instanced_arrays = (ctx) => + webgl_enable_ANGLE_instanced_arrays(GL.contexts[ctx].GLctx); + _emscripten_webgl_enable_ANGLE_instanced_arrays.sig = 'ip'; + var _emscripten_webgl_enable_OES_vertex_array_object = (ctx) => + webgl_enable_OES_vertex_array_object(GL.contexts[ctx].GLctx); + _emscripten_webgl_enable_OES_vertex_array_object.sig = 'ip'; + var _emscripten_webgl_enable_WEBGL_draw_buffers = (ctx) => + webgl_enable_WEBGL_draw_buffers(GL.contexts[ctx].GLctx); + _emscripten_webgl_enable_WEBGL_draw_buffers.sig = 'ip'; + var _emscripten_webgl_enable_WEBGL_multi_draw = (ctx) => + webgl_enable_WEBGL_multi_draw(GL.contexts[ctx].GLctx); + _emscripten_webgl_enable_WEBGL_multi_draw.sig = 'ip'; - var getDynCaller = (sig, ptr, promising = false) => { - return (...args) => dynCall(sig, ptr, args, promising); - }; + var _emscripten_webgl_enable_EXT_polygon_offset_clamp = (ctx) => + webgl_enable_EXT_polygon_offset_clamp(GL.contexts[ctx].GLctx); + _emscripten_webgl_enable_EXT_polygon_offset_clamp.sig = 'ip'; + var _emscripten_webgl_enable_EXT_clip_control = (ctx) => + webgl_enable_EXT_clip_control(GL.contexts[ctx].GLctx); + _emscripten_webgl_enable_EXT_clip_control.sig = 'ip'; + var _emscripten_webgl_enable_WEBGL_polygon_mode = (ctx) => + webgl_enable_WEBGL_polygon_mode(GL.contexts[ctx].GLctx); + _emscripten_webgl_enable_WEBGL_polygon_mode.sig = 'ip'; + var _glPixelStorei = _emscripten_glPixelStorei; + _glPixelStorei.sig = 'vii'; + var _glGetString = _emscripten_glGetString; + _glGetString.sig = 'pi'; + var _glGetIntegerv = _emscripten_glGetIntegerv; + _glGetIntegerv.sig = 'vip'; - var _emscripten_exit_with_live_runtime = () => { - runtimeKeepalivePush(); - throw 'unwind'; - }; - _emscripten_exit_with_live_runtime.sig = 'v'; + var _glGetFloatv = _emscripten_glGetFloatv; + _glGetFloatv.sig = 'vip'; + var _glGetBooleanv = _emscripten_glGetBooleanv; + _glGetBooleanv.sig = 'vip'; - - var _emscripten_force_exit = (status) => { - __emscripten_runtime_keepalive_clear(); - _exit(status); - }; - _emscripten_force_exit.sig = 'vi'; + var _glDeleteTextures = _emscripten_glDeleteTextures; + _glDeleteTextures.sig = 'vip'; + var _glCompressedTexImage2D = _emscripten_glCompressedTexImage2D; + _glCompressedTexImage2D.sig = 'viiiiiiip'; - var _emscripten_outn = (str, len) => out(UTF8ToString(str, len)); - _emscripten_outn.sig = 'vpp'; + var _glCompressedTexSubImage2D = _emscripten_glCompressedTexSubImage2D; + _glCompressedTexSubImage2D.sig = 'viiiiiiiip'; + var _glTexImage2D = _emscripten_glTexImage2D; + _glTexImage2D.sig = 'viiiiiiiip'; - var _emscripten_errn = (str, len) => err(UTF8ToString(str, len)); - _emscripten_errn.sig = 'vpp'; + var _glTexSubImage2D = _emscripten_glTexSubImage2D; + _glTexSubImage2D.sig = 'viiiiiiiip'; + var _glReadPixels = _emscripten_glReadPixels; + _glReadPixels.sig = 'viiiiiip'; + var _glBindTexture = _emscripten_glBindTexture; + _glBindTexture.sig = 'vii'; + var _glGetTexParameterfv = _emscripten_glGetTexParameterfv; + _glGetTexParameterfv.sig = 'viip'; + var _glGetTexParameteriv = _emscripten_glGetTexParameteriv; + _glGetTexParameteriv.sig = 'viip'; + var _glTexParameterfv = _emscripten_glTexParameterfv; + _glTexParameterfv.sig = 'viip'; - var _emscripten_throw_number = (number) => { - throw number; - }; - _emscripten_throw_number.sig = 'vd'; + var _glTexParameteriv = _emscripten_glTexParameteriv; + _glTexParameteriv.sig = 'viip'; - var _emscripten_throw_string = (str) => { - throw UTF8ToString(str); - }; - _emscripten_throw_string.sig = 'vp'; + var _glIsTexture = _emscripten_glIsTexture; + _glIsTexture.sig = 'ii'; + var _glGenBuffers = _emscripten_glGenBuffers; + _glGenBuffers.sig = 'vip'; + var _glGenTextures = _emscripten_glGenTextures; + _glGenTextures.sig = 'vip'; + var _glDeleteBuffers = _emscripten_glDeleteBuffers; + _glDeleteBuffers.sig = 'vip'; + var _glGetBufferParameteriv = _emscripten_glGetBufferParameteriv; + _glGetBufferParameteriv.sig = 'viip'; + var _glBufferData = _emscripten_glBufferData; + _glBufferData.sig = 'vippi'; - var _emscripten_runtime_keepalive_push = runtimeKeepalivePush; - _emscripten_runtime_keepalive_push.sig = 'v'; + var _glBufferSubData = _emscripten_glBufferSubData; + _glBufferSubData.sig = 'vippp'; - var _emscripten_runtime_keepalive_pop = runtimeKeepalivePop; - _emscripten_runtime_keepalive_pop.sig = 'v'; + var _glGenQueriesEXT = _emscripten_glGenQueriesEXT; - var _emscripten_runtime_keepalive_check = keepRuntimeAlive; - _emscripten_runtime_keepalive_check.sig = 'i'; + var _glDeleteQueriesEXT = _emscripten_glDeleteQueriesEXT; + var _glIsQueryEXT = _emscripten_glIsQueryEXT; + var _glBeginQueryEXT = _emscripten_glBeginQueryEXT; + var _glEndQueryEXT = _emscripten_glEndQueryEXT; - var asmjsMangle = (x) => { - if (x == '__main_argc_argv') { - x = 'main'; - } - return x.startsWith('dynCall_') ? x : '_' + x; - }; + var _glQueryCounterEXT = _emscripten_glQueryCounterEXT; + var _glGetQueryivEXT = _emscripten_glGetQueryivEXT; + var _glGetQueryObjectivEXT = _emscripten_glGetQueryObjectivEXT; + var _glGetQueryObjectuivEXT = _emscripten_glGetQueryObjectivEXT; + var _glGetQueryObjecti64vEXT = _emscripten_glGetQueryObjecti64vEXT; - - - var __emscripten_fs_load_embedded_files = (ptr) => { - do { - var name_addr = HEAPU32[((ptr)>>2)]; - ptr += 4; - var len = HEAPU32[((ptr)>>2)]; - ptr += 4; - var content = HEAPU32[((ptr)>>2)]; - ptr += 4; - var name = UTF8ToString(name_addr) - FS.createPath('/', PATH.dirname(name), true, true); - // canOwn this data in the filesystem, it is a slice of wasm memory that will never change - FS.createDataFile(name, null, HEAP8.subarray(content, content + len), true, true, true); - } while (HEAPU32[((ptr)>>2)]); - }; - __emscripten_fs_load_embedded_files.sig = 'vp'; + var _glGetQueryObjectui64vEXT = _emscripten_glGetQueryObjecti64vEXT; + var _glIsBuffer = _emscripten_glIsBuffer; + _glIsBuffer.sig = 'ii'; + var _glGenRenderbuffers = _emscripten_glGenRenderbuffers; + _glGenRenderbuffers.sig = 'vip'; + var _glDeleteRenderbuffers = _emscripten_glDeleteRenderbuffers; + _glDeleteRenderbuffers.sig = 'vip'; + var _glBindRenderbuffer = _emscripten_glBindRenderbuffer; + _glBindRenderbuffer.sig = 'vii'; + var _glGetRenderbufferParameteriv = + _emscripten_glGetRenderbufferParameteriv; + _glGetRenderbufferParameteriv.sig = 'viip'; + var _glIsRenderbuffer = _emscripten_glIsRenderbuffer; + _glIsRenderbuffer.sig = 'ii'; + var _glGetUniformfv = _emscripten_glGetUniformfv; + _glGetUniformfv.sig = 'viip'; + var _glGetUniformiv = _emscripten_glGetUniformiv; + _glGetUniformiv.sig = 'viip'; + var _glGetUniformLocation = _emscripten_glGetUniformLocation; + _glGetUniformLocation.sig = 'iip'; + var _glGetVertexAttribfv = _emscripten_glGetVertexAttribfv; + _glGetVertexAttribfv.sig = 'viip'; + var _glGetVertexAttribiv = _emscripten_glGetVertexAttribiv; + _glGetVertexAttribiv.sig = 'viip'; - var onInits = []; + var _glGetVertexAttribPointerv = _emscripten_glGetVertexAttribPointerv; + _glGetVertexAttribPointerv.sig = 'viip'; - var addOnInit = (cb) => onInits.push(cb); + var _glUniform1f = _emscripten_glUniform1f; + _glUniform1f.sig = 'vif'; + var _glUniform2f = _emscripten_glUniform2f; + _glUniform2f.sig = 'viff'; + var _glUniform3f = _emscripten_glUniform3f; + _glUniform3f.sig = 'vifff'; - var onMains = []; + var _glUniform4f = _emscripten_glUniform4f; + _glUniform4f.sig = 'viffff'; - var addOnPreMain = (cb) => onMains.push(cb); + var _glUniform1i = _emscripten_glUniform1i; + _glUniform1i.sig = 'vii'; - var onExits = []; + var _glUniform2i = _emscripten_glUniform2i; + _glUniform2i.sig = 'viii'; - var addOnExit = (cb) => onExits.push(cb); + var _glUniform3i = _emscripten_glUniform3i; + _glUniform3i.sig = 'viiii'; + var _glUniform4i = _emscripten_glUniform4i; + _glUniform4i.sig = 'viiiii'; + var _glUniform1iv = _emscripten_glUniform1iv; + _glUniform1iv.sig = 'viip'; - var STACK_SIZE = 1048576; + var _glUniform2iv = _emscripten_glUniform2iv; + _glUniform2iv.sig = 'viip'; - var STACK_ALIGN = 16; + var _glUniform3iv = _emscripten_glUniform3iv; + _glUniform3iv.sig = 'viip'; - var POINTER_SIZE = 4; + var _glUniform4iv = _emscripten_glUniform4iv; + _glUniform4iv.sig = 'viip'; - var ASSERTIONS = 0; + var _glUniform1fv = _emscripten_glUniform1fv; + _glUniform1fv.sig = 'viip'; + var _glUniform2fv = _emscripten_glUniform2fv; + _glUniform2fv.sig = 'viip'; + var _glUniform3fv = _emscripten_glUniform3fv; + _glUniform3fv.sig = 'viip'; - - - /** - * @param {string=} returnType - * @param {Array=} argTypes - * @param {Object=} opts - */ - var cwrap = (ident, returnType, argTypes, opts) => { - // When the function takes numbers and returns a number, we can just return - // the original function - var numericArgs = !argTypes || argTypes.every((type) => type === 'number' || type === 'boolean'); - var numericRet = returnType !== 'string'; - if (numericRet && numericArgs && !opts) { - return getCFunc(ident); - } - return (...args) => ccall(ident, returnType, argTypes, args, opts); - }; + var _glUniform4fv = _emscripten_glUniform4fv; + _glUniform4fv.sig = 'viip'; + var _glUniformMatrix2fv = _emscripten_glUniformMatrix2fv; + _glUniformMatrix2fv.sig = 'viiip'; + var _glUniformMatrix3fv = _emscripten_glUniformMatrix3fv; + _glUniformMatrix3fv.sig = 'viiip'; + var _glUniformMatrix4fv = _emscripten_glUniformMatrix4fv; + _glUniformMatrix4fv.sig = 'viiip'; + var _glBindBuffer = _emscripten_glBindBuffer; + _glBindBuffer.sig = 'vii'; + var _glVertexAttrib1fv = _emscripten_glVertexAttrib1fv; + _glVertexAttrib1fv.sig = 'vip'; + var _glVertexAttrib2fv = _emscripten_glVertexAttrib2fv; + _glVertexAttrib2fv.sig = 'vip'; + var _glVertexAttrib3fv = _emscripten_glVertexAttrib3fv; + _glVertexAttrib3fv.sig = 'vip'; + var _glVertexAttrib4fv = _emscripten_glVertexAttrib4fv; + _glVertexAttrib4fv.sig = 'vip'; + var _glGetAttribLocation = _emscripten_glGetAttribLocation; + _glGetAttribLocation.sig = 'iip'; + var _glGetActiveAttrib = _emscripten_glGetActiveAttrib; + _glGetActiveAttrib.sig = 'viiipppp'; - - - - - var removeFunction = (index) => { - functionsInTableMap.delete(getWasmTableEntry(index)); - setWasmTableEntry(index, null); - freeTableIndexes.push(index); - }; - - - - var _emscripten_math_cbrt = Math.cbrt; - _emscripten_math_cbrt.sig = 'dd'; - - var _emscripten_math_pow = Math.pow; - _emscripten_math_pow.sig = 'ddd'; - - var _emscripten_math_random = Math.random; - _emscripten_math_random.sig = 'd'; - - var _emscripten_math_sign = Math.sign; - _emscripten_math_sign.sig = 'dd'; - - var _emscripten_math_sqrt = Math.sqrt; - _emscripten_math_sqrt.sig = 'dd'; - - var _emscripten_math_exp = Math.exp; - _emscripten_math_exp.sig = 'dd'; - - var _emscripten_math_expm1 = Math.expm1; - _emscripten_math_expm1.sig = 'dd'; - - var _emscripten_math_fmod = (x, y) => x % y; - _emscripten_math_fmod.sig = 'ddd'; - - var _emscripten_math_log = Math.log; - _emscripten_math_log.sig = 'dd'; - - var _emscripten_math_log1p = Math.log1p; - _emscripten_math_log1p.sig = 'dd'; - - var _emscripten_math_log10 = Math.log10; - _emscripten_math_log10.sig = 'dd'; - - var _emscripten_math_log2 = Math.log2; - _emscripten_math_log2.sig = 'dd'; - - var _emscripten_math_round = Math.round; - _emscripten_math_round.sig = 'dd'; - - var _emscripten_math_acos = Math.acos; - _emscripten_math_acos.sig = 'dd'; - - var _emscripten_math_acosh = Math.acosh; - _emscripten_math_acosh.sig = 'dd'; - - var _emscripten_math_asin = Math.asin; - _emscripten_math_asin.sig = 'dd'; - - var _emscripten_math_asinh = Math.asinh; - _emscripten_math_asinh.sig = 'dd'; - - var _emscripten_math_atan = Math.atan; - _emscripten_math_atan.sig = 'dd'; - - var _emscripten_math_atanh = Math.atanh; - _emscripten_math_atanh.sig = 'dd'; - - var _emscripten_math_atan2 = Math.atan2; - _emscripten_math_atan2.sig = 'ddd'; - - var _emscripten_math_cos = Math.cos; - _emscripten_math_cos.sig = 'dd'; - - var _emscripten_math_cosh = Math.cosh; - _emscripten_math_cosh.sig = 'dd'; - - var _emscripten_math_hypot = (count, varargs) => { - var args = []; - for (var i = 0; i < count; ++i) { - args.push(HEAPF64[(((varargs)+(i * 8))>>3)]); - } - return Math.hypot(...args); - }; - _emscripten_math_hypot.sig = 'dip'; - - var _emscripten_math_sin = Math.sin; - _emscripten_math_sin.sig = 'dd'; - - var _emscripten_math_sinh = Math.sinh; - _emscripten_math_sinh.sig = 'dd'; - - var _emscripten_math_tan = Math.tan; - _emscripten_math_tan.sig = 'dd'; - - var _emscripten_math_tanh = Math.tanh; - _emscripten_math_tanh.sig = 'dd'; - - - - - - - - - - - - var intArrayToString = (array) => { - var ret = []; - for (var i = 0; i < array.length; i++) { - var chr = array[i]; - if (chr > 0xFF) { - chr &= 0xFF; - } - ret.push(String.fromCharCode(chr)); - } - return ret.join(''); - }; - - var AsciiToString = (ptr) => { - var str = ''; - while (1) { - var ch = HEAPU8[ptr++]; - if (!ch) return str; - str += String.fromCharCode(ch); - } - }; - - - var UTF16Decoder = globalThis.TextDecoder ? new TextDecoder('utf-16le') : undefined;; - - - var UTF16ToString = (ptr, maxBytesToRead, ignoreNul) => { - var idx = ((ptr)>>1); - var endIdx = findStringEnd(HEAPU16, idx, maxBytesToRead / 2, ignoreNul); - - // When using conditional TextDecoder, skip it for short strings as the overhead of the native call is not worth it. - if (endIdx - idx > 16 && UTF16Decoder) - return UTF16Decoder.decode(HEAPU16.subarray(idx, endIdx)); - - // 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 = idx; i < endIdx; ++i) { - var codeUnit = HEAPU16[i]; - // 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) => { - // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. - maxBytesToWrite ??= 0x7FFFFFFF; - 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 - HEAP16[((outPtr)>>1)] = codeUnit; - outPtr += 2; - } - // Null-terminate the pointer to the HEAP. - HEAP16[((outPtr)>>1)] = 0; - return outPtr - startPtr; - }; - - var lengthBytesUTF16 = (str) => str.length*2; - - var UTF32ToString = (ptr, maxBytesToRead, ignoreNul) => { - var str = ''; - var startIdx = ((ptr)>>2); - // If maxBytesToRead is not passed explicitly, it will be undefined, and this - // will always evaluate to true. This saves on code size. - for (var i = 0; !(i >= maxBytesToRead / 4); i++) { - var utf32 = HEAPU32[startIdx + i]; - if (!utf32 && !ignoreNul) break; - str += String.fromCodePoint(utf32); - } - return str; - }; - - var stringToUTF32 = (str, outPtr, maxBytesToWrite) => { - // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. - maxBytesToWrite ??= 0x7FFFFFFF; - if (maxBytesToWrite < 4) return 0; - var startPtr = outPtr; - var endPtr = startPtr + maxBytesToWrite - 4; - for (var i = 0; i < str.length; ++i) { - var codePoint = str.codePointAt(i); - // Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16. - // We need to manually skip over the second code unit for correct iteration. - if (codePoint > 0xFFFF) { - i++; - } - HEAP32[((outPtr)>>2)] = codePoint; - outPtr += 4; - if (outPtr + 4 > endPtr) break; - } - // Null-terminate the pointer to the HEAP. - HEAP32[((outPtr)>>2)] = 0; - return outPtr - startPtr; - }; - - var lengthBytesUTF32 = (str) => { - var len = 0; - for (var i = 0; i < str.length; ++i) { - var codePoint = str.codePointAt(i); - // Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16. - // We need to manually skip over the second code unit for correct iteration. - if (codePoint > 0xFFFF) { - i++; - } - len += 4; - } - - return len; - }; - - - - - var JSEvents = { - memcpy(target, src, size) { - HEAP8.set(HEAP8.subarray(src, src + size), target); - }, - removeAllEventListeners() { - while (JSEvents.eventHandlers.length) { - JSEvents._removeHandler(JSEvents.eventHandlers.length - 1); - } - JSEvents.deferredCalls = []; - }, - registerRemoveEventListeners() { - if (!JSEvents.removeEventListenersRegistered) { - addOnExit(JSEvents.removeAllEventListeners); - JSEvents.removeEventListenersRegistered = true; - } - }, - inEventHandler:0, - deferredCalls:[], - deferCall(targetFunction, precedence, argsList) { - function arraysHaveEqualContent(arrA, arrB) { - if (arrA.length != arrB.length) return false; - - for (var i in arrA) { - if (arrA[i] != arrB[i]) return false; - } - return true; - } - // Test if the given call was already queued, and if so, don't add it again. - for (var call of JSEvents.deferredCalls) { - if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { - return; - } - } - JSEvents.deferredCalls.push({ - targetFunction, - precedence, - argsList - }); - - JSEvents.deferredCalls.sort((x,y) => x.precedence < y.precedence); - }, - removeDeferredCalls(targetFunction) { - JSEvents.deferredCalls = JSEvents.deferredCalls.filter((call) => call.targetFunction != targetFunction); - }, - canPerformEventHandlerRequests() { - if (navigator.userActivation) { - // Verify against transient activation status from UserActivation API - // whether it is possible to perform a request here without needing to defer. See - // https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation - // and https://caniuse.com/mdn-api_useractivation - // At the time of writing, Firefox does not support this API: https://bugzil.la/1791079 - return navigator.userActivation.isActive; - } - - return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; - }, - runDeferredCalls() { - if (!JSEvents.canPerformEventHandlerRequests()) { - return; - } - var deferredCalls = JSEvents.deferredCalls; - JSEvents.deferredCalls = []; - for (var call of deferredCalls) { - call.targetFunction(...call.argsList); - } - }, - eventHandlers:[], - removeAllHandlersOnTarget:(target, eventTypeString) => { - for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { - if (JSEvents.eventHandlers[i].target == target && - (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) { - JSEvents._removeHandler(i--); - } - } - }, - _removeHandler(i) { - var h = JSEvents.eventHandlers[i]; - h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); - JSEvents.eventHandlers.splice(i, 1); - }, - registerOrRemoveHandler(eventHandler) { - if (!eventHandler.target) { - return -4; - } - if (eventHandler.callbackfunc) { - eventHandler.eventListenerFunc = function(event) { - // Increment nesting count for the event handler. - ++JSEvents.inEventHandler; - JSEvents.currentEventHandler = eventHandler; - // Process any old deferred calls the user has placed. - JSEvents.runDeferredCalls(); - // Process the actual event, calls back to user C code handler. - eventHandler.handlerFunc(event); - // Process any new deferred calls that were placed right now from this event handler. - JSEvents.runDeferredCalls(); - // Out of event handler - restore nesting count. - --JSEvents.inEventHandler; - }; - - eventHandler.target.addEventListener(eventHandler.eventTypeString, - eventHandler.eventListenerFunc, - eventHandler.useCapture); - JSEvents.eventHandlers.push(eventHandler); - JSEvents.registerRemoveEventListeners(); - } else { - for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { - if (JSEvents.eventHandlers[i].target == eventHandler.target - && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { - JSEvents._removeHandler(i--); - } - } - } - return 0; - }, - getNodeNameForTarget(target) { - if (!target) return ''; - if (target == window) return '#window'; - if (target == screen) return '#screen'; - return target?.nodeName || ''; - }, - fullscreenEnabled() { - return document.fullscreenEnabled - ; - }, - }; - - function getFullscreenElement() { - return document.fullscreenElement || document.mozFullScreenElement || - document.webkitFullscreenElement || document.webkitCurrentFullScreenElement || - document.msFullscreenElement; - } - - - - - var registerKeyEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.keyEvent ||= _malloc(160); - - var keyEventHandlerFunc = (e) => { - - var keyEventData = JSEvents.keyEvent; - HEAPF64[((keyEventData)>>3)] = e.timeStamp; - - var idx = ((keyEventData)>>2); - - HEAP32[idx + 2] = e.location; - HEAP8[keyEventData + 12] = e.ctrlKey; - HEAP8[keyEventData + 13] = e.shiftKey; - HEAP8[keyEventData + 14] = e.altKey; - HEAP8[keyEventData + 15] = e.metaKey; - HEAP8[keyEventData + 16] = e.repeat; - HEAP32[idx + 5] = e.charCode; - HEAP32[idx + 6] = e.keyCode; - HEAP32[idx + 7] = e.which; - stringToUTF8(e.key || '', keyEventData + 32, 32); - stringToUTF8(e.code || '', keyEventData + 64, 32); - stringToUTF8(e.char || '', keyEventData + 96, 32); - stringToUTF8(e.locale || '', keyEventData + 128, 32); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, keyEventData, userData)) e.preventDefault(); - }; - - var eventHandler = { - target: findEventTarget(target), - eventTypeString, - callbackfunc, - handlerFunc: keyEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - - - - var _emscripten_set_keypress_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerKeyEventCallback(target, userData, useCapture, callbackfunc, 1, "keypress", targetThread); - _emscripten_set_keypress_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_keydown_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerKeyEventCallback(target, userData, useCapture, callbackfunc, 2, "keydown", targetThread); - _emscripten_set_keydown_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_keyup_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerKeyEventCallback(target, userData, useCapture, callbackfunc, 3, "keyup", targetThread); - _emscripten_set_keyup_callback_on_thread.sig = 'ippipp'; - - var getBoundingClientRect = (e) => specialHTMLTargets.indexOf(e) < 0 ? e.getBoundingClientRect() : {'left':0,'top':0}; - - var fillMouseEventData = (eventStruct, e, target) => { - HEAPF64[((eventStruct)>>3)] = e.timeStamp; - var idx = ((eventStruct)>>2); - HEAP32[idx + 2] = e.screenX; - HEAP32[idx + 3] = e.screenY; - HEAP32[idx + 4] = e.clientX; - HEAP32[idx + 5] = e.clientY; - HEAP8[eventStruct + 24] = e.ctrlKey; - HEAP8[eventStruct + 25] = e.shiftKey; - HEAP8[eventStruct + 26] = e.altKey; - HEAP8[eventStruct + 27] = e.metaKey; - HEAP16[idx*2 + 14] = e.button; - HEAP16[idx*2 + 15] = e.buttons; - - HEAP32[idx + 8] = e["movementX"]; - - HEAP32[idx + 9] = e["movementY"]; - - // Note: rect contains doubles (truncated to placate SAFE_HEAP, which is the same behaviour when writing to HEAP32 anyway) - var rect = getBoundingClientRect(target); - HEAP32[idx + 10] = e.clientX - (rect.left | 0); - HEAP32[idx + 11] = e.clientY - (rect.top | 0); - }; - - - - - var registerMouseEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.mouseEvent ||= _malloc(64); - target = findEventTarget(target); - - var mouseEventHandlerFunc = (e = event) => { - // TODO: Make this access thread safe, or this could update live while app is reading it. - fillMouseEventData(JSEvents.mouseEvent, e, target); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - allowsDeferredCalls: eventTypeString != 'mousemove' && eventTypeString != 'mouseenter' && eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! - eventTypeString, - callbackfunc, - handlerFunc: mouseEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_click_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 4, "click", targetThread); - _emscripten_set_click_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_mousedown_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 5, "mousedown", targetThread); - _emscripten_set_mousedown_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_mouseup_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 6, "mouseup", targetThread); - _emscripten_set_mouseup_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_dblclick_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 7, "dblclick", targetThread); - _emscripten_set_dblclick_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_mousemove_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 8, "mousemove", targetThread); - _emscripten_set_mousemove_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_mouseenter_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 33, "mouseenter", targetThread); - _emscripten_set_mouseenter_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_mouseleave_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 34, "mouseleave", targetThread); - _emscripten_set_mouseleave_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_mouseover_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 35, "mouseover", targetThread); - _emscripten_set_mouseover_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_mouseout_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerMouseEventCallback(target, userData, useCapture, callbackfunc, 36, "mouseout", targetThread); - _emscripten_set_mouseout_callback_on_thread.sig = 'ippipp'; - - var _emscripten_get_mouse_status = (mouseState) => { - if (!JSEvents.mouseEvent) return -7; - // HTML5 does not really have a polling API for mouse events, so implement one manually by - // returning the data from the most recently received event. This requires that user has registered - // at least some no-op function as an event handler to any of the mouse function. - JSEvents.memcpy(mouseState, JSEvents.mouseEvent, 64); - return 0; - }; - _emscripten_get_mouse_status.sig = 'ip'; - - - - var registerWheelEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.wheelEvent ||= _malloc(96); - - // The DOM Level 3 events spec event 'wheel' - var wheelHandlerFunc = (e = event) => { - var wheelEvent = JSEvents.wheelEvent; - fillMouseEventData(wheelEvent, e, target); - HEAPF64[(((wheelEvent)+(64))>>3)] = e["deltaX"]; - HEAPF64[(((wheelEvent)+(72))>>3)] = e["deltaY"]; - HEAPF64[(((wheelEvent)+(80))>>3)] = e["deltaZ"]; - HEAP32[(((wheelEvent)+(88))>>2)] = e["deltaMode"]; - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, wheelEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - allowsDeferredCalls: true, - eventTypeString, - callbackfunc, - handlerFunc: wheelHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - var _emscripten_set_wheel_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => { - target = findEventTarget(target); - if (!target) return -4; - if (typeof target.onwheel != 'undefined') { - return registerWheelEventCallback(target, userData, useCapture, callbackfunc, 9, "wheel", targetThread); - } else { - return -1; - } - }; - _emscripten_set_wheel_callback_on_thread.sig = 'ippipp'; - - - - var registerUiEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.uiEvent ||= _malloc(36); - - target = findEventTarget(target); - - var uiEventHandlerFunc = (e = event) => { - if (e.target != target) { - // Never take ui events such as scroll via a 'bubbled' route, but always from the direct element that - // was targeted. Otherwise e.g. if app logs a message in response to a page scroll, the Emscripten log - // message box could cause to scroll, generating a new (bubbled) scroll message, causing a new log print, - // causing a new scroll, etc.. - return; - } - var b = document.body; // Take document.body to a variable, Closure compiler does not outline access to it on its own. - if (!b) { - // During a page unload 'body' can be null, with "Cannot read property 'clientWidth' of null" being thrown - return; - } - var uiEvent = JSEvents.uiEvent; - HEAP32[((uiEvent)>>2)] = 0; // always zero for resize and scroll - HEAP32[(((uiEvent)+(4))>>2)] = b.clientWidth; - HEAP32[(((uiEvent)+(8))>>2)] = b.clientHeight; - HEAP32[(((uiEvent)+(12))>>2)] = innerWidth; - HEAP32[(((uiEvent)+(16))>>2)] = innerHeight; - HEAP32[(((uiEvent)+(20))>>2)] = outerWidth; - HEAP32[(((uiEvent)+(24))>>2)] = outerHeight; - HEAP32[(((uiEvent)+(28))>>2)] = pageXOffset | 0; // scroll offsets are float - HEAP32[(((uiEvent)+(32))>>2)] = pageYOffset | 0; - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, uiEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - eventTypeString, - callbackfunc, - handlerFunc: uiEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_resize_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerUiEventCallback(target, userData, useCapture, callbackfunc, 10, "resize", targetThread); - _emscripten_set_resize_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_scroll_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerUiEventCallback(target, userData, useCapture, callbackfunc, 11, "scroll", targetThread); - _emscripten_set_scroll_callback_on_thread.sig = 'ippipp'; - - - - - var registerFocusEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.focusEvent ||= _malloc(256); - - var focusEventHandlerFunc = (e = event) => { - var nodeName = JSEvents.getNodeNameForTarget(e.target); - var id = e.target.id ? e.target.id : ''; - - var focusEvent = JSEvents.focusEvent; - stringToUTF8(nodeName, focusEvent + 0, 128); - stringToUTF8(id, focusEvent + 128, 128); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, focusEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target: findEventTarget(target), - eventTypeString, - callbackfunc, - handlerFunc: focusEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_blur_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerFocusEventCallback(target, userData, useCapture, callbackfunc, 12, "blur", targetThread); - _emscripten_set_blur_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_focus_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerFocusEventCallback(target, userData, useCapture, callbackfunc, 13, "focus", targetThread); - _emscripten_set_focus_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_focusin_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerFocusEventCallback(target, userData, useCapture, callbackfunc, 14, "focusin", targetThread); - _emscripten_set_focusin_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_focusout_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerFocusEventCallback(target, userData, useCapture, callbackfunc, 15, "focusout", targetThread); - _emscripten_set_focusout_callback_on_thread.sig = 'ippipp'; - - var fillDeviceOrientationEventData = (eventStruct, e, target) => { - HEAPF64[((eventStruct)>>3)] = e.alpha; - HEAPF64[(((eventStruct)+(8))>>3)] = e.beta; - HEAPF64[(((eventStruct)+(16))>>3)] = e.gamma; - HEAP8[(eventStruct)+(24)] = e.absolute; - }; - - - - var registerDeviceOrientationEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.deviceOrientationEvent ||= _malloc(32); - - var deviceOrientationEventHandlerFunc = (e = event) => { - fillDeviceOrientationEventData(JSEvents.deviceOrientationEvent, e, target); // TODO: Thread-safety with respect to emscripten_get_deviceorientation_status() - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, JSEvents.deviceOrientationEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target: findEventTarget(target), - eventTypeString, - callbackfunc, - handlerFunc: deviceOrientationEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_deviceorientation_callback_on_thread = (userData, useCapture, callbackfunc, targetThread) => { - return registerDeviceOrientationEventCallback(2, userData, useCapture, callbackfunc, 16, "deviceorientation", targetThread); - }; - _emscripten_set_deviceorientation_callback_on_thread.sig = 'ipipp'; - - var _emscripten_get_deviceorientation_status = (orientationState) => { - if (!JSEvents.deviceOrientationEvent) return -7; - // HTML5 does not really have a polling API for device orientation events, so implement one manually by - // returning the data from the most recently received event. This requires that user has registered - // at least some no-op function as an event handler. - JSEvents.memcpy(orientationState, JSEvents.deviceOrientationEvent, 32); - return 0; - }; - _emscripten_get_deviceorientation_status.sig = 'ip'; - - var fillDeviceMotionEventData = (eventStruct, e, target) => { - var supportedFields = 0; - var a = e['acceleration']; - supportedFields |= a && 1; - var ag = e['accelerationIncludingGravity']; - supportedFields |= ag && 2; - var rr = e['rotationRate']; - supportedFields |= rr && 4; - a = a || {}; - ag = ag || {}; - rr = rr || {}; - HEAP32[(((eventStruct)+(72))>>2)] = supportedFields; - HEAPF64[((eventStruct)>>3)] = a["x"]; - HEAPF64[(((eventStruct)+(8))>>3)] = a["y"]; - HEAPF64[(((eventStruct)+(16))>>3)] = a["z"]; - HEAPF64[(((eventStruct)+(24))>>3)] = ag["x"]; - HEAPF64[(((eventStruct)+(32))>>3)] = ag["y"]; - HEAPF64[(((eventStruct)+(40))>>3)] = ag["z"]; - HEAPF64[(((eventStruct)+(48))>>3)] = rr["alpha"]; - HEAPF64[(((eventStruct)+(56))>>3)] = rr["beta"]; - HEAPF64[(((eventStruct)+(64))>>3)] = rr["gamma"]; - }; - - - - - var registerDeviceMotionEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.deviceMotionEvent ||= _malloc(80); - - var deviceMotionEventHandlerFunc = (e = event) => { - fillDeviceMotionEventData(JSEvents.deviceMotionEvent, e, target); // TODO: Thread-safety with respect to emscripten_get_devicemotion_status() - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, JSEvents.deviceMotionEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target: findEventTarget(target), - eventTypeString, - callbackfunc, - handlerFunc: deviceMotionEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_devicemotion_callback_on_thread = (userData, useCapture, callbackfunc, targetThread) => - registerDeviceMotionEventCallback(2, userData, useCapture, callbackfunc, 17, "devicemotion", targetThread); - _emscripten_set_devicemotion_callback_on_thread.sig = 'ipipp'; - - var _emscripten_get_devicemotion_status = (motionState) => { - if (!JSEvents.deviceMotionEvent) return -7; - // HTML5 does not really have a polling API for device motion events, so implement one manually by - // returning the data from the most recently received event. This requires that user has registered - // at least some no-op function as an event handler. - JSEvents.memcpy(motionState, JSEvents.deviceMotionEvent, 80); - return 0; - }; - _emscripten_get_devicemotion_status.sig = 'ip'; - - var screenOrientation = () => { - if (!window.screen) return undefined; - return screen.orientation || screen['mozOrientation'] || screen['webkitOrientation']; - }; - - var fillOrientationChangeEventData = (eventStruct) => { - // OrientationType enum - var orientationsType1 = ['portrait-primary', 'portrait-secondary', 'landscape-primary', 'landscape-secondary']; - // alternative selection from OrientationLockType enum - var orientationsType2 = ['portrait', 'portrait', 'landscape', 'landscape']; - - var orientationIndex = 0; - var orientationAngle = 0; - var screenOrientObj = screenOrientation(); - if (typeof screenOrientObj === 'object') { - orientationIndex = orientationsType1.indexOf(screenOrientObj.type); - if (orientationIndex < 0) { - orientationIndex = orientationsType2.indexOf(screenOrientObj.type); - } - if (orientationIndex >= 0) { - orientationIndex = 1 << orientationIndex; - } - orientationAngle = screenOrientObj.angle; - } - - HEAP32[((eventStruct)>>2)] = orientationIndex; - HEAP32[(((eventStruct)+(4))>>2)] = orientationAngle; - }; - - - - var registerOrientationChangeEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.orientationChangeEvent ||= _malloc(8); - - var orientationChangeEventHandlerFunc = (e = event) => { - var orientationChangeEvent = JSEvents.orientationChangeEvent; - - fillOrientationChangeEventData(orientationChangeEvent); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, orientationChangeEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - eventTypeString, - callbackfunc, - handlerFunc: orientationChangeEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_orientationchange_callback_on_thread = (userData, useCapture, callbackfunc, targetThread) => { - if (!window.screen || !screen.orientation) return -1; - return registerOrientationChangeEventCallback(screen.orientation, userData, useCapture, callbackfunc, 18, 'change', targetThread); - }; - _emscripten_set_orientationchange_callback_on_thread.sig = 'ipipp'; - - - var _emscripten_get_orientation_status = (orientationChangeEvent) => { - // screenOrientation() resolving standard, window.orientation being the deprecated mobile-only - if (!screenOrientation() && typeof orientation == 'undefined') return -1; - fillOrientationChangeEventData(orientationChangeEvent); - return 0; - }; - _emscripten_get_orientation_status.sig = 'ip'; - - var _emscripten_lock_orientation = (allowedOrientations) => { - var orientations = []; - if (allowedOrientations & 1) orientations.push("portrait-primary"); - if (allowedOrientations & 2) orientations.push("portrait-secondary"); - if (allowedOrientations & 4) orientations.push("landscape-primary"); - if (allowedOrientations & 8) orientations.push("landscape-secondary"); - var succeeded; - if (screen.lockOrientation) { - succeeded = screen.lockOrientation(orientations); - } else if (screen.mozLockOrientation) { - succeeded = screen.mozLockOrientation(orientations); - } else if (screen.webkitLockOrientation) { - succeeded = screen.webkitLockOrientation(orientations); - } else { - return -1; - } - if (succeeded) { - return 0; - } - return -6; - }; - _emscripten_lock_orientation.sig = 'ii'; - - var _emscripten_unlock_orientation = () => { - if (screen.unlockOrientation) { - screen.unlockOrientation(); - } else if (screen.mozUnlockOrientation) { - screen.mozUnlockOrientation(); - } else if (screen.webkitUnlockOrientation) { - screen.webkitUnlockOrientation(); - } else { - return -1; - } - return 0; - }; - _emscripten_unlock_orientation.sig = 'i'; - - - - var fillFullscreenChangeEventData = (eventStruct) => { - var fullscreenElement = getFullscreenElement(); - var isFullscreen = !!fullscreenElement; - // Assigning a boolean to HEAP32 with expected type coercion. - /** @suppress{checkTypes} */ - HEAP8[eventStruct] = isFullscreen; - HEAP8[(eventStruct)+(1)] = JSEvents.fullscreenEnabled(); - // If transitioning to fullscreen, report info about the element that is now fullscreen. - // If transitioning to windowed mode, report info about the element that just was fullscreen. - var reportedElement = isFullscreen ? fullscreenElement : JSEvents.previousFullscreenElement; - var nodeName = JSEvents.getNodeNameForTarget(reportedElement); - var id = reportedElement?.id || ''; - stringToUTF8(nodeName, eventStruct + 2, 128); - stringToUTF8(id, eventStruct + 130, 128); - HEAP32[(((eventStruct)+(260))>>2)] = reportedElement ? reportedElement.clientWidth : 0; - HEAP32[(((eventStruct)+(264))>>2)] = reportedElement ? reportedElement.clientHeight : 0; - HEAP32[(((eventStruct)+(268))>>2)] = screen.width; - HEAP32[(((eventStruct)+(272))>>2)] = screen.height; - if (isFullscreen) { - JSEvents.previousFullscreenElement = fullscreenElement; - } - }; - - - - var registerFullscreenChangeEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.fullscreenChangeEvent ||= _malloc(276); - - var fullscreenChangeEventhandlerFunc = (e = event) => { - var fullscreenChangeEvent = JSEvents.fullscreenChangeEvent; - - fillFullscreenChangeEventData(fullscreenChangeEvent); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, fullscreenChangeEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - eventTypeString, - callbackfunc, - handlerFunc: fullscreenChangeEventhandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - - var _emscripten_set_fullscreenchange_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => { - if (!JSEvents.fullscreenEnabled()) return -1; - target = findEventTarget(target); - if (!target) return -4; - - return registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, 19, "fullscreenchange", targetThread); - }; - _emscripten_set_fullscreenchange_callback_on_thread.sig = 'ippipp'; - - - var _emscripten_get_fullscreen_status = (fullscreenStatus) => { - if (!JSEvents.fullscreenEnabled()) return -1; - fillFullscreenChangeEventData(fullscreenStatus); - return 0; - }; - _emscripten_get_fullscreen_status.sig = 'ip'; - - - var _emscripten_get_canvas_element_size = (target, width, height) => { - var canvas = findCanvasEventTarget(target); - if (!canvas) return -4; - HEAP32[((width)>>2)] = canvas.width; - HEAP32[((height)>>2)] = canvas.height; - }; - _emscripten_get_canvas_element_size.sig = 'ippp'; - - - - var getCanvasElementSize = (target) => { - var sp = stackSave(); - var w = stackAlloc(8); - var h = w + 4; - - var targetInt = stringToUTF8OnStack(target.id); - var ret = _emscripten_get_canvas_element_size(targetInt, w, h); - var size = [HEAP32[((w)>>2)], HEAP32[((h)>>2)]]; - stackRestore(sp); - return size; - }; - - - - - var setCanvasElementSize = (target, width, height) => { - if (!target.controlTransferredOffscreen) { - target.width = width; - target.height = height; - } else { - // This function is being called from high-level JavaScript code instead of asm.js/Wasm, - // and it needs to synchronously proxy over to another thread, so marshal the string onto the heap to do the call. - var sp = stackSave(); - var targetInt = stringToUTF8OnStack(target.id); - _emscripten_set_canvas_element_size(targetInt, width, height); - stackRestore(sp); - } - }; - - var currentFullscreenStrategy = { - }; - var registerRestoreOldStyle = (canvas) => { - var canvasSize = getCanvasElementSize(canvas); - var oldWidth = canvasSize[0]; - var oldHeight = canvasSize[1]; - var oldCssWidth = canvas.style.width; - var oldCssHeight = canvas.style.height; - var oldBackgroundColor = canvas.style.backgroundColor; // Chrome reads color from here. - var oldDocumentBackgroundColor = document.body.style.backgroundColor; // IE11 reads color from here. - // Firefox always has black background color. - var oldPaddingLeft = canvas.style.paddingLeft; // Chrome, FF, Safari - var oldPaddingRight = canvas.style.paddingRight; - var oldPaddingTop = canvas.style.paddingTop; - var oldPaddingBottom = canvas.style.paddingBottom; - var oldMarginLeft = canvas.style.marginLeft; // IE11 - var oldMarginRight = canvas.style.marginRight; - var oldMarginTop = canvas.style.marginTop; - var oldMarginBottom = canvas.style.marginBottom; - var oldDocumentBodyMargin = document.body.style.margin; - var oldDocumentOverflow = document.documentElement.style.overflow; // Chrome, Firefox - var oldDocumentScroll = document.body.scroll; // IE - var oldImageRendering = canvas.style.imageRendering; - - function restoreOldStyle() { - if (!getFullscreenElement()) { - document.removeEventListener('fullscreenchange', restoreOldStyle); - - setCanvasElementSize(canvas, oldWidth, oldHeight); - - canvas.style.width = oldCssWidth; - canvas.style.height = oldCssHeight; - canvas.style.backgroundColor = oldBackgroundColor; // Chrome - // IE11 hack: assigning 'undefined' or an empty string to document.body.style.backgroundColor has no effect, so first assign back the default color - // before setting the undefined value. Setting undefined value is also important, or otherwise we would later treat that as something that the user - // had explicitly set so subsequent fullscreen transitions would not set background color properly. - if (!oldDocumentBackgroundColor) document.body.style.backgroundColor = 'white'; - document.body.style.backgroundColor = oldDocumentBackgroundColor; // IE11 - canvas.style.paddingLeft = oldPaddingLeft; // Chrome, FF, Safari - canvas.style.paddingRight = oldPaddingRight; - canvas.style.paddingTop = oldPaddingTop; - canvas.style.paddingBottom = oldPaddingBottom; - canvas.style.marginLeft = oldMarginLeft; // IE11 - canvas.style.marginRight = oldMarginRight; - canvas.style.marginTop = oldMarginTop; - canvas.style.marginBottom = oldMarginBottom; - document.body.style.margin = oldDocumentBodyMargin; - document.documentElement.style.overflow = oldDocumentOverflow; // Chrome, Firefox - document.body.scroll = oldDocumentScroll; // IE - canvas.style.imageRendering = oldImageRendering; - if (canvas.GLctxObject) canvas.GLctxObject.GLctx.viewport(0, 0, oldWidth, oldHeight); - - if (currentFullscreenStrategy.canvasResizedCallback) { - ((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(37, 0, currentFullscreenStrategy.canvasResizedCallbackUserData); - } - } - } - document.addEventListener('fullscreenchange', restoreOldStyle); - return restoreOldStyle; - }; - - - var setLetterbox = (element, topBottom, leftRight) => { - // Cannot use margin to specify letterboxes in FF or Chrome, since those ignore margins in fullscreen mode. - element.style.paddingLeft = element.style.paddingRight = leftRight + 'px'; - element.style.paddingTop = element.style.paddingBottom = topBottom + 'px'; - }; - - - var JSEvents_resizeCanvasForFullscreen = (target, strategy) => { - var restoreOldStyle = registerRestoreOldStyle(target); - var cssWidth = strategy.softFullscreen ? innerWidth : screen.width; - var cssHeight = strategy.softFullscreen ? innerHeight : screen.height; - var rect = getBoundingClientRect(target); - var windowedCssWidth = rect.width; - var windowedCssHeight = rect.height; - var canvasSize = getCanvasElementSize(target); - var windowedRttWidth = canvasSize[0]; - var windowedRttHeight = canvasSize[1]; - - if (strategy.scaleMode == 3) { - setLetterbox(target, (cssHeight - windowedCssHeight) / 2, (cssWidth - windowedCssWidth) / 2); - cssWidth = windowedCssWidth; - cssHeight = windowedCssHeight; - } else if (strategy.scaleMode == 2) { - if (cssWidth*windowedRttHeight < windowedRttWidth*cssHeight) { - var desiredCssHeight = windowedRttHeight * cssWidth / windowedRttWidth; - setLetterbox(target, (cssHeight - desiredCssHeight) / 2, 0); - cssHeight = desiredCssHeight; - } else { - var desiredCssWidth = windowedRttWidth * cssHeight / windowedRttHeight; - setLetterbox(target, 0, (cssWidth - desiredCssWidth) / 2); - cssWidth = desiredCssWidth; - } - } - - // If we are adding padding, must choose a background color or otherwise Chrome will give the - // padding a default white color. Do it only if user has not customized their own background color. - target.style.backgroundColor ||= 'black'; - // IE11 does the same, but requires the color to be set in the document body. - document.body.style.backgroundColor ||= 'black'; // IE11 - // Firefox always shows black letterboxes independent of style color. - - target.style.width = cssWidth + 'px'; - target.style.height = cssHeight + 'px'; - - if (strategy.filteringMode == 1) { - target.style.imageRendering = 'optimizeSpeed'; - target.style.imageRendering = '-moz-crisp-edges'; - target.style.imageRendering = '-o-crisp-edges'; - target.style.imageRendering = '-webkit-optimize-contrast'; - target.style.imageRendering = 'optimize-contrast'; - target.style.imageRendering = 'crisp-edges'; - target.style.imageRendering = 'pixelated'; - } - - var dpiScale = (strategy.canvasResolutionScaleMode == 2) ? devicePixelRatio : 1; - if (strategy.canvasResolutionScaleMode != 0) { - var newWidth = (cssWidth * dpiScale)|0; - var newHeight = (cssHeight * dpiScale)|0; - setCanvasElementSize(target, newWidth, newHeight); - if (target.GLctxObject) target.GLctxObject.GLctx.viewport(0, 0, newWidth, newHeight); - } - return restoreOldStyle; - }; - var JSEvents_requestFullscreen = (target, strategy) => { - // EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT + EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE is a mode where no extra logic is performed to the DOM elements. - if (strategy.scaleMode != 0 || strategy.canvasResolutionScaleMode != 0) { - JSEvents_resizeCanvasForFullscreen(target, strategy); - } - - if (target.requestFullscreen) { - target.requestFullscreen(); - } else { - return JSEvents.fullscreenEnabled() ? -3 : -1; - } - - currentFullscreenStrategy = strategy; - - if (strategy.canvasResizedCallback) { - ((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(37, 0, strategy.canvasResizedCallbackUserData); - } - - return 0; - }; - - - - var hideEverythingExceptGivenElement = (onlyVisibleElement) => { - var child = onlyVisibleElement; - var parent = child.parentNode; - var hiddenElements = []; - while (child != document.body) { - var children = parent.children; - for (var currChild of children) { - if (currChild != child) { - hiddenElements.push({ node: currChild, displayState: currChild.style.display }); - currChild.style.display = 'none'; - } - } - child = parent; - parent = parent.parentNode; - } - return hiddenElements; - }; - - var restoreHiddenElements = (hiddenElements) => { - for (var elem of hiddenElements) { - elem.node.style.display = elem.displayState; - } - }; - - - - var restoreOldWindowedStyle = null; - - - - - - var softFullscreenResizeWebGLRenderTarget = () => { - var dpr = devicePixelRatio; - var inHiDPIFullscreenMode = currentFullscreenStrategy.canvasResolutionScaleMode == 2; - var inAspectRatioFixedFullscreenMode = currentFullscreenStrategy.scaleMode == 2; - var inPixelPerfectFullscreenMode = currentFullscreenStrategy.canvasResolutionScaleMode != 0; - var inCenteredWithoutScalingFullscreenMode = currentFullscreenStrategy.scaleMode == 3; - var screenWidth = inHiDPIFullscreenMode ? Math.round(innerWidth*dpr) : innerWidth; - var screenHeight = inHiDPIFullscreenMode ? Math.round(innerHeight*dpr) : innerHeight; - var w = screenWidth; - var h = screenHeight; - var canvas = currentFullscreenStrategy.target; - var canvasSize = getCanvasElementSize(canvas); - var x = canvasSize[0]; - var y = canvasSize[1]; - var topMargin; - - if (inAspectRatioFixedFullscreenMode) { - if (w*y < x*h) h = (w * y / x) | 0; - else if (w*y > x*h) w = (h * x / y) | 0; - topMargin = ((screenHeight - h) / 2) | 0; - } - - if (inPixelPerfectFullscreenMode) { - setCanvasElementSize(canvas, w, h); - if (canvas.GLctxObject) canvas.GLctxObject.GLctx.viewport(0, 0, w, h); - } - - // Back to CSS pixels. - if (inHiDPIFullscreenMode) { - topMargin /= dpr; - w /= dpr; - h /= dpr; - // Round to nearest 4 digits of precision. - w = Math.round(w*1e4)/1e4; - h = Math.round(h*1e4)/1e4; - topMargin = Math.round(topMargin*1e4)/1e4; - } - - if (inCenteredWithoutScalingFullscreenMode) { - var t = (innerHeight - jstoi_q(canvas.style.height)) / 2; - var b = (innerWidth - jstoi_q(canvas.style.width)) / 2; - setLetterbox(canvas, t, b); - } else { - canvas.style.width = w + 'px'; - canvas.style.height = h + 'px'; - var b = (innerWidth - w) / 2; - setLetterbox(canvas, topMargin, b); - } - - if (!inCenteredWithoutScalingFullscreenMode && currentFullscreenStrategy.canvasResizedCallback) { - ((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(37, 0, currentFullscreenStrategy.canvasResizedCallbackUserData); - } - }; - - - - var doRequestFullscreen = (target, strategy) => { - if (!JSEvents.fullscreenEnabled()) return -1; - target = findEventTarget(target); - if (!target) return -4; - - if (!target.requestFullscreen - ) { - return -3; - } - - // Queue this function call if we're not currently in an event handler and - // the user saw it appropriate to do so. - if (!JSEvents.canPerformEventHandlerRequests()) { - if (strategy.deferUntilInEventHandler) { - JSEvents.deferCall(JSEvents_requestFullscreen, 1 /* priority over pointer lock */, [target, strategy]); - return 1; - } - return -2; - } - - return JSEvents_requestFullscreen(target, strategy); - }; - - var _emscripten_request_fullscreen = (target, deferUntilInEventHandler) => { - var strategy = { - // These options perform no added logic, but just bare request fullscreen. - scaleMode: 0, - canvasResolutionScaleMode: 0, - filteringMode: 0, - deferUntilInEventHandler, - canvasResizedCallbackTargetThread: 2 - }; - return doRequestFullscreen(target, strategy); - }; - _emscripten_request_fullscreen.sig = 'ipi'; - - var _emscripten_request_fullscreen_strategy = (target, deferUntilInEventHandler, fullscreenStrategy) => { - var strategy = { - scaleMode: HEAP32[((fullscreenStrategy)>>2)], - canvasResolutionScaleMode: HEAP32[(((fullscreenStrategy)+(4))>>2)], - filteringMode: HEAP32[(((fullscreenStrategy)+(8))>>2)], - deferUntilInEventHandler, - canvasResizedCallback: HEAP32[(((fullscreenStrategy)+(12))>>2)], - canvasResizedCallbackUserData: HEAP32[(((fullscreenStrategy)+(16))>>2)] - }; - - return doRequestFullscreen(target, strategy); - }; - _emscripten_request_fullscreen_strategy.sig = 'ipip'; - - - - - - - - - var _emscripten_enter_soft_fullscreen = (target, fullscreenStrategy) => { - target = findEventTarget(target); - if (!target) return -4; - - var strategy = { - scaleMode: HEAP32[((fullscreenStrategy)>>2)], - canvasResolutionScaleMode: HEAP32[(((fullscreenStrategy)+(4))>>2)], - filteringMode: HEAP32[(((fullscreenStrategy)+(8))>>2)], - canvasResizedCallback: HEAP32[(((fullscreenStrategy)+(12))>>2)], - canvasResizedCallbackUserData: HEAP32[(((fullscreenStrategy)+(16))>>2)], - target, - softFullscreen: true - }; - - var restoreOldStyle = JSEvents_resizeCanvasForFullscreen(target, strategy); - - document.documentElement.style.overflow = 'hidden'; // Firefox, Chrome - document.body.scroll = "no"; // IE11 - document.body.style.margin = '0px'; // Override default document margin area on all browsers. - - var hiddenElements = hideEverythingExceptGivenElement(target); - - function restoreWindowedState() { - restoreOldStyle(); - restoreHiddenElements(hiddenElements); - removeEventListener('resize', softFullscreenResizeWebGLRenderTarget); - if (strategy.canvasResizedCallback) { - ((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(37, 0, strategy.canvasResizedCallbackUserData); - } - currentFullscreenStrategy = 0; - } - restoreOldWindowedStyle = restoreWindowedState; - currentFullscreenStrategy = strategy; - addEventListener('resize', softFullscreenResizeWebGLRenderTarget); - - // Inform the caller that the canvas size has changed. - if (strategy.canvasResizedCallback) { - ((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(37, 0, strategy.canvasResizedCallbackUserData); - } - - return 0; - }; - _emscripten_enter_soft_fullscreen.sig = 'ipp'; - - var _emscripten_exit_soft_fullscreen = () => { - restoreOldWindowedStyle?.(); - restoreOldWindowedStyle = null; - - return 0; - }; - _emscripten_exit_soft_fullscreen.sig = 'i'; - - - - var _emscripten_exit_fullscreen = () => { - if (!JSEvents.fullscreenEnabled()) return -1; - // Make sure no queued up calls will fire after this. - JSEvents.removeDeferredCalls(JSEvents_requestFullscreen); - - var d = specialHTMLTargets[1]; - if (d.exitFullscreen) { - d.fullscreenElement && d.exitFullscreen(); - } else { - return -1; - } - - return 0; - }; - _emscripten_exit_fullscreen.sig = 'i'; - - - var fillPointerlockChangeEventData = (eventStruct) => { - var pointerLockElement = document.pointerLockElement; - var isPointerlocked = !!pointerLockElement; - // Assigning a boolean to HEAP32 with expected type coercion. - /** @suppress{checkTypes} */ - HEAP8[eventStruct] = isPointerlocked; - var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement); - var id = pointerLockElement?.id || ''; - stringToUTF8(nodeName, eventStruct + 1, 128); - stringToUTF8(id, eventStruct + 129, 128); - }; - - - - var registerPointerlockChangeEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.pointerlockChangeEvent ||= _malloc(257); - - var pointerlockChangeEventHandlerFunc = (e = event) => { - var pointerlockChangeEvent = JSEvents.pointerlockChangeEvent; - fillPointerlockChangeEventData(pointerlockChangeEvent); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, pointerlockChangeEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - eventTypeString, - callbackfunc, - handlerFunc: pointerlockChangeEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - var _emscripten_set_pointerlockchange_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => { - if (!document.body?.requestPointerLock) { - return -1; - } - - target = findEventTarget(target); - if (!target) return -4; - return registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, "pointerlockchange", targetThread); - }; - _emscripten_set_pointerlockchange_callback_on_thread.sig = 'ippipp'; - - var registerPointerlockErrorEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - - var pointerlockErrorEventHandlerFunc = (e = event) => { - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, 0, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - eventTypeString, - callbackfunc, - handlerFunc: pointerlockErrorEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - var _emscripten_set_pointerlockerror_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => { - if (!document.body?.requestPointerLock) { - return -1; - } - - target = findEventTarget(target); - - if (!target) return -4; - return registerPointerlockErrorEventCallback(target, userData, useCapture, callbackfunc, 38, "pointerlockerror", targetThread); - }; - _emscripten_set_pointerlockerror_callback_on_thread.sig = 'ippipp'; - - var _emscripten_get_pointerlock_status = (pointerlockStatus) => { - if (pointerlockStatus) fillPointerlockChangeEventData(pointerlockStatus); - if (!document.body?.requestPointerLock) { - return -1; - } - return 0; - }; - _emscripten_get_pointerlock_status.sig = 'ip'; - - var requestPointerLock = (target) => { - if (target.requestPointerLock) { - target.requestPointerLock(); - } else { - // document.body is known to accept pointer lock, so use that to differentiate if the user passed a bad element, - // or if the whole browser just doesn't support the feature. - if (document.body.requestPointerLock) { - return -3; - } - return -1; - } - return 0; - }; - - - - var _emscripten_request_pointerlock = (target, deferUntilInEventHandler) => { - target = findEventTarget(target); - if (!target) return -4; - if (!target.requestPointerLock) { - return -1; - } - - // Queue this function call if we're not currently in an event handler and - // the user saw it appropriate to do so. - if (!JSEvents.canPerformEventHandlerRequests()) { - if (deferUntilInEventHandler) { - JSEvents.deferCall(requestPointerLock, 2 /* priority below fullscreen */, [target]); - return 1; - } - return -2; - } - - return requestPointerLock(target); - }; - _emscripten_request_pointerlock.sig = 'ipi'; - - - var _emscripten_exit_pointerlock = () => { - // Make sure no queued up calls will fire after this. - JSEvents.removeDeferredCalls(requestPointerLock); - if (!document.exitPointerLock) return -1; - document.exitPointerLock(); - return 0; - }; - _emscripten_exit_pointerlock.sig = 'i'; - - var _emscripten_vibrate = (msecs) => { - if (!navigator.vibrate) return -1; - navigator.vibrate(msecs); - return 0; - }; - _emscripten_vibrate.sig = 'ii'; - - var _emscripten_vibrate_pattern = (msecsArray, numEntries) => { - if (!navigator.vibrate) return -1; - - var vibrateList = []; - for (var i = 0; i < numEntries; ++i) { - var msecs = HEAP32[(((msecsArray)+(i*4))>>2)]; - vibrateList.push(msecs); - } - navigator.vibrate(vibrateList); - return 0; - }; - _emscripten_vibrate_pattern.sig = 'ipi'; - - var fillVisibilityChangeEventData = (eventStruct) => { - var visibilityStates = [ "hidden", "visible", "prerender", "unloaded" ]; - var visibilityState = visibilityStates.indexOf(document.visibilityState); - - // Assigning a boolean to HEAP32 with expected type coercion. - /** @suppress{checkTypes} */ - HEAP8[eventStruct] = document.hidden; - HEAP32[(((eventStruct)+(4))>>2)] = visibilityState; - }; - - - - var registerVisibilityChangeEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.visibilityChangeEvent ||= _malloc(8); - - var visibilityChangeEventHandlerFunc = (e = event) => { - var visibilityChangeEvent = JSEvents.visibilityChangeEvent; - - fillVisibilityChangeEventData(visibilityChangeEvent); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, visibilityChangeEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - eventTypeString, - callbackfunc, - handlerFunc: visibilityChangeEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - var _emscripten_set_visibilitychange_callback_on_thread = (userData, useCapture, callbackfunc, targetThread) => { - if (!specialHTMLTargets[1]) { - return -4; - } - return registerVisibilityChangeEventCallback(specialHTMLTargets[1], userData, useCapture, callbackfunc, 21, "visibilitychange", targetThread); - }; - _emscripten_set_visibilitychange_callback_on_thread.sig = 'ipipp'; - - var _emscripten_get_visibility_status = (visibilityStatus) => { - if (typeof document.visibilityState == 'undefined' && typeof document.hidden == 'undefined') { - return -1; - } - fillVisibilityChangeEventData(visibilityStatus); - return 0; - }; - _emscripten_get_visibility_status.sig = 'ip'; - - - - - var registerTouchEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.touchEvent ||= _malloc(1552); - - target = findEventTarget(target); - - var touchEventHandlerFunc = (e) => { - var t, touches = {}, et = e.touches; - // To ease marshalling different kinds of touches that browser reports (all touches are listed in e.touches, - // only changed touches in e.changedTouches, and touches on target at a.targetTouches), mark a boolean in - // each Touch object so that we can later loop only once over all touches we see to marshall over to Wasm. - - for (let t of et) { - // Browser might recycle the generated Touch objects between each frame (Firefox on Android), so reset any - // changed/target states we may have set from previous frame. - t.isChanged = t.onTarget = 0; - touches[t.identifier] = t; - } - // Mark which touches are part of the changedTouches list. - for (let t of e.changedTouches) { - t.isChanged = 1; - touches[t.identifier] = t; - } - // Mark which touches are part of the targetTouches list. - for (let t of e.targetTouches) { - touches[t.identifier].onTarget = 1; - } - - var touchEvent = JSEvents.touchEvent; - HEAPF64[((touchEvent)>>3)] = e.timeStamp; - HEAP8[touchEvent + 12] = e.ctrlKey; - HEAP8[touchEvent + 13] = e.shiftKey; - HEAP8[touchEvent + 14] = e.altKey; - HEAP8[touchEvent + 15] = e.metaKey; - var idx = touchEvent + 16; - var targetRect = getBoundingClientRect(target); - var numTouches = 0; - for (let t of Object.values(touches)) { - var idx32 = ((idx)>>2); // Pre-shift the ptr to index to HEAP32 to save code size - HEAP32[idx32 + 0] = t.identifier; - HEAP32[idx32 + 1] = t.screenX; - HEAP32[idx32 + 2] = t.screenY; - HEAP32[idx32 + 3] = t.clientX; - HEAP32[idx32 + 4] = t.clientY; - HEAP32[idx32 + 5] = t.pageX; - HEAP32[idx32 + 6] = t.pageY; - HEAP8[idx + 28] = t.isChanged; - HEAP8[idx + 29] = t.onTarget; - HEAP32[idx32 + 8] = t.clientX - (targetRect.left | 0); - HEAP32[idx32 + 9] = t.clientY - (targetRect.top | 0); - - idx += 48; - - if (++numTouches > 31) { - break; - } - } - HEAP32[(((touchEvent)+(8))>>2)] = numTouches; - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, touchEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target, - allowsDeferredCalls: eventTypeString == 'touchstart' || eventTypeString == 'touchend', - eventTypeString, - callbackfunc, - handlerFunc: touchEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_touchstart_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerTouchEventCallback(target, userData, useCapture, callbackfunc, 22, "touchstart", targetThread); - _emscripten_set_touchstart_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_touchend_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerTouchEventCallback(target, userData, useCapture, callbackfunc, 23, "touchend", targetThread); - _emscripten_set_touchend_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_touchmove_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerTouchEventCallback(target, userData, useCapture, callbackfunc, 24, "touchmove", targetThread); - _emscripten_set_touchmove_callback_on_thread.sig = 'ippipp'; - - var _emscripten_set_touchcancel_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => - registerTouchEventCallback(target, userData, useCapture, callbackfunc, 25, "touchcancel", targetThread); - _emscripten_set_touchcancel_callback_on_thread.sig = 'ippipp'; - - var fillGamepadEventData = (eventStruct, e) => { - HEAPF64[((eventStruct)>>3)] = e.timestamp; - for (var i = 0; i < e.axes.length; ++i) { - HEAPF64[(((eventStruct+i*8)+(16))>>3)] = e.axes[i]; - } - for (var i = 0; i < e.buttons.length; ++i) { - if (typeof e.buttons[i] == 'object') { - HEAPF64[(((eventStruct+i*8)+(528))>>3)] = e.buttons[i].value; - } else { - HEAPF64[(((eventStruct+i*8)+(528))>>3)] = e.buttons[i]; - } - } - for (var i = 0; i < e.buttons.length; ++i) { - if (typeof e.buttons[i] == 'object') { - HEAP8[(eventStruct+i)+(1040)] = e.buttons[i].pressed; - } else { - // Assigning a boolean to HEAP32, that's ok, but Closure would like to warn about it: - /** @suppress {checkTypes} */ - HEAP8[(eventStruct+i)+(1040)] = e.buttons[i] == 1; - } - } - HEAP8[(eventStruct)+(1104)] = e.connected; - HEAP32[(((eventStruct)+(1108))>>2)] = e.index; - HEAP32[(((eventStruct)+(8))>>2)] = e.axes.length; - HEAP32[(((eventStruct)+(12))>>2)] = e.buttons.length; - stringToUTF8(e.id, eventStruct + 1112, 64); - stringToUTF8(e.mapping, eventStruct + 1176, 64); - }; - - - - - var registerGamepadEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.gamepadEvent ||= _malloc(1240); - - var gamepadEventHandlerFunc = (e = event) => { - var gamepadEvent = JSEvents.gamepadEvent; - fillGamepadEventData(gamepadEvent, e["gamepad"]); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, gamepadEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target: findEventTarget(target), - allowsDeferredCalls: true, - eventTypeString, - callbackfunc, - handlerFunc: gamepadEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - /** @suppress {checkTypes} */ - var _emscripten_sample_gamepad_data = () => { - try { - if (navigator.getGamepads) return (JSEvents.lastGamepadState = navigator.getGamepads()) - ? 0 : -1; - } catch(e) { - navigator.getGamepads = null; // Disable getGamepads() so that it won't be attempted to be used again. - } - return -1; - }; - _emscripten_sample_gamepad_data.sig = 'i'; - var _emscripten_set_gamepadconnected_callback_on_thread = (userData, useCapture, callbackfunc, targetThread) => { - if (_emscripten_sample_gamepad_data()) return -1; - return registerGamepadEventCallback(2, userData, useCapture, callbackfunc, 26, "gamepadconnected", targetThread); - }; - _emscripten_set_gamepadconnected_callback_on_thread.sig = 'ipipp'; - - - var _emscripten_set_gamepaddisconnected_callback_on_thread = (userData, useCapture, callbackfunc, targetThread) => { - if (_emscripten_sample_gamepad_data()) return -1; - return registerGamepadEventCallback(2, userData, useCapture, callbackfunc, 27, "gamepaddisconnected", targetThread); - }; - _emscripten_set_gamepaddisconnected_callback_on_thread.sig = 'ipipp'; - - - var _emscripten_get_num_gamepads = () => { - // N.B. Do not call emscripten_get_num_gamepads() unless having first called emscripten_sample_gamepad_data(), and that has returned EMSCRIPTEN_RESULT_SUCCESS. - // Otherwise the following line will throw an exception. - return JSEvents.lastGamepadState.length; - }; - _emscripten_get_num_gamepads.sig = 'i'; - - - var _emscripten_get_gamepad_status = (index, gamepadState) => { - // INVALID_PARAM is returned on a Gamepad index that never was there. - if (index < 0 || index >= JSEvents.lastGamepadState.length) return -5; - - // NO_DATA is returned on a Gamepad index that was removed. - // For previously disconnected gamepads there should be an empty slot (null/undefined/false) at the index. - // This is because gamepads must keep their original position in the array. - // For example, removing the first of two gamepads produces [null/undefined/false, gamepad]. - if (!JSEvents.lastGamepadState[index]) return -7; - - fillGamepadEventData(gamepadState, JSEvents.lastGamepadState[index]); - return 0; - }; - _emscripten_get_gamepad_status.sig = 'iip'; - - - - var registerBeforeUnloadEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) => { - var beforeUnloadEventHandlerFunc = (e = event) => { - // Note: This is always called on the main browser thread, since it needs synchronously return a value! - var confirmationMessage = ((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, 0, userData); - - if (confirmationMessage) { - confirmationMessage = UTF8ToString(confirmationMessage); - } - if (confirmationMessage) { - e.preventDefault(); - e.returnValue = confirmationMessage; - return confirmationMessage; - } - }; - - var eventHandler = { - target: findEventTarget(target), - eventTypeString, - callbackfunc, - handlerFunc: beforeUnloadEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - var _emscripten_set_beforeunload_callback_on_thread = (userData, callbackfunc, targetThread) => { - if (typeof onbeforeunload == 'undefined') return -1; - // beforeunload callback can only be registered on the main browser thread, because the page will go away immediately after returning from the handler, - // and there is no time to start proxying it anywhere. - if (targetThread !== 1) return -5; - return registerBeforeUnloadEventCallback(2, userData, true, callbackfunc, 28, "beforeunload"); - }; - _emscripten_set_beforeunload_callback_on_thread.sig = 'ippp'; - - var fillBatteryEventData = (eventStruct, battery) => { - HEAPF64[((eventStruct)>>3)] = battery.chargingTime; - HEAPF64[(((eventStruct)+(8))>>3)] = battery.dischargingTime; - HEAPF64[(((eventStruct)+(16))>>3)] = battery.level; - HEAP8[(eventStruct)+(24)] = battery.charging; - }; - - var hasBatteryAPI = () => typeof navigator != 'undefined' && navigator.getBattery; - - - - var registerBatteryEventCallback = (battery, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - JSEvents.batteryEvent ||= _malloc(32); - - var batteryEventHandlerFunc = (e = event) => { - var batteryEvent = JSEvents.batteryEvent; - fillBatteryEventData(batteryEvent, battery); - - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, batteryEvent, userData)) e.preventDefault(); - }; - - var eventHandler = { - target: battery, - eventTypeString, - callbackfunc, - handlerFunc: batteryEventHandlerFunc, - useCapture - }; - return JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - var _emscripten_set_batterychargingchange_callback_on_thread = (userData, callbackfunc, targetThread) => { - if (!hasBatteryAPI()) return -1; - navigator.getBattery().then((b) => { - registerBatteryEventCallback(b, userData, true, callbackfunc, 29, "chargingchange", targetThread); - }); - }; - _emscripten_set_batterychargingchange_callback_on_thread.sig = 'ippp'; - - - var _emscripten_set_batterylevelchange_callback_on_thread = (userData, callbackfunc, targetThread) => { - if (!hasBatteryAPI()) return -1; - navigator.getBattery().then((b) => { - registerBatteryEventCallback(b, userData, true, callbackfunc, 30, "levelchange", targetThread); - }); - }; - _emscripten_set_batterylevelchange_callback_on_thread.sig = 'ippp'; - - var batteryManager; - - - - var _emscripten_get_battery_status = (batteryState) => { - if (!hasBatteryAPI()) return -1; - if (!batteryManager) { - navigator.getBattery().then((b) => { - batteryManager = b; - }); - return -7; - } - fillBatteryEventData(batteryState, batteryManager); - return 0; - }; - _emscripten_get_battery_status.sig = 'ip'; - - - - - - var _emscripten_set_element_css_size = (target, width, height) => { - target = findEventTarget(target); - if (!target) return -4; - - target.style.width = width + "px"; - target.style.height = height + "px"; - - return 0; - }; - _emscripten_set_element_css_size.sig = 'ipdd'; - - - var _emscripten_get_element_css_size = (target, width, height) => { - target = findEventTarget(target); - if (!target) return -4; - - var rect = getBoundingClientRect(target); - HEAPF64[((width)>>3)] = rect.width; - HEAPF64[((height)>>3)] = rect.height; - - return 0; - }; - _emscripten_get_element_css_size.sig = 'ippp'; - - var _emscripten_html5_remove_all_event_listeners = () => JSEvents.removeAllEventListeners(); - _emscripten_html5_remove_all_event_listeners.sig = 'v'; - - var _emscripten_request_animation_frame = (cb, userData) => - requestAnimationFrame((timeStamp) => ((a1, a2) => {} /* a dynamic function call to signature idi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(timeStamp, userData)); - _emscripten_request_animation_frame.sig = 'ipp'; - - var _emscripten_cancel_animation_frame = (id) => cancelAnimationFrame(id); - _emscripten_cancel_animation_frame.sig = 'vi'; - - var _emscripten_request_animation_frame_loop = (cb, userData) => { - function tick(timeStamp) { - if (((a1, a2) => {} /* a dynamic function call to signature idi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(timeStamp, userData)) { - requestAnimationFrame(tick); - } - } - return requestAnimationFrame(tick); - }; - _emscripten_request_animation_frame_loop.sig = 'vpp'; - - var _emscripten_get_device_pixel_ratio = () => { - return (typeof devicePixelRatio == 'number' && devicePixelRatio) || 1.0; - }; - _emscripten_get_device_pixel_ratio.sig = 'd'; - - - - - - var _emscripten_get_callstack = (flags, str, maxbytes) => { - var callstack = getCallstack(flags); - // User can query the required amount of bytes to hold the callstack. - if (!str || maxbytes <= 0) { - return lengthBytesUTF8(callstack)+1; - } - // Output callstack string as C string to HEAP. - var bytesWrittenExcludingNull = stringToUTF8(callstack, str, maxbytes); - - // Return number of bytes written, including null. - return bytesWrittenExcludingNull+1; - }; - _emscripten_get_callstack.sig = 'iipi'; - - /** @returns {number} */ - var convertFrameToPC = (frame) => { - var match; - - if (match = /\bwasm-function\[\d+\]:(0x[0-9a-f]+)/.exec(frame)) { - // Wasm engines give the binary offset directly, so we use that as return address - return +match[1]; - } else if (match = /:(\d+):\d+(?:\)|$)/.exec(frame)) { - // If we are in js, we can use the js line number as the "return address". - // This should work for wasm2js. We tag the high bit to distinguish this - // from wasm addresses. - return 0x80000000 | +match[1]; - } - // return 0 if we can't find any - return 0; - }; - - - var _emscripten_return_address = (level) => { - var callstack = jsStackTrace().split('\n'); - if (callstack[0] == 'Error') { - callstack.shift(); - } - // skip this function and the caller to get caller's return address - var caller = callstack[level + 3]; - return convertFrameToPC(caller); - }; - _emscripten_return_address.sig = 'pi'; - - var UNWIND_CACHE = { - }; - - - - - var saveInUnwindCache = (callstack) => { - for (var line of callstack) { - var pc = convertFrameToPC(line); - if (pc) { - UNWIND_CACHE[pc] = line; - } - } - }; - - var _emscripten_stack_snapshot = () => { - var callstack = jsStackTrace().split('\n'); - if (callstack[0] == 'Error') { - callstack.shift(); - } - saveInUnwindCache(callstack); - - // Caches the stack snapshot so that emscripten_stack_unwind_buffer() can - // unwind from this spot. - UNWIND_CACHE.last_addr = convertFrameToPC(callstack[3]); - UNWIND_CACHE.last_stack = callstack; - return UNWIND_CACHE.last_addr; - }; - _emscripten_stack_snapshot.sig = 'p'; - - - - - - var _emscripten_stack_unwind_buffer = (addr, buffer, count) => { - var stack; - if (UNWIND_CACHE.last_addr == addr) { - stack = UNWIND_CACHE.last_stack; - } else { - stack = jsStackTrace().split('\n'); - if (stack[0] == 'Error') { - stack.shift(); - } - saveInUnwindCache(stack); - } - - var offset = 3; - while (stack[offset] && convertFrameToPC(stack[offset]) != addr) { - ++offset; - } - - for (var i = 0; i < count && stack[i+offset]; ++i) { - HEAP32[(((buffer)+(i*4))>>2)] = convertFrameToPC(stack[i + offset]); - } - return i; - }; - _emscripten_stack_unwind_buffer.sig = 'ippi'; - - - - - var _emscripten_pc_get_function = (pc) => { - var frame = UNWIND_CACHE[pc]; - if (!frame) return 0; - - var name; - var match; - // First try to match foo.wasm.sym files explcitly. e.g. - // - // at test_return_address.wasm.main (wasm://wasm/test_return_address.wasm-0012cc2a:wasm-function[26]:0x9f3 - // - // Then match JS symbols which don't include that module name: - // - // at invokeEntryPoint (.../test_return_address.js:1500:42) - // - // Finally match firefox format: - // - // Object._main@http://server.com:4324:12' - if (match = /^\s+at .*\.wasm\.(.*) \(.*\)$/.exec(frame)) { - name = match[1]; - } else if (match = /^\s+at (.*) \(.*\)$/.exec(frame)) { - name = match[1]; - } else if (match = /^(.+?)@/.exec(frame)) { - name = match[1]; - } else { - return 0; - } - - _free(_emscripten_pc_get_function.ret ?? 0); - _emscripten_pc_get_function.ret = stringToNewUTF8(name); - return _emscripten_pc_get_function.ret; - }; - _emscripten_pc_get_function.sig = 'pp'; - - var convertPCtoSourceLocation = (pc) => { - if (UNWIND_CACHE.last_get_source_pc == pc) return UNWIND_CACHE.last_source; - - var match; - var source; - - if (!source) { - var frame = UNWIND_CACHE[pc]; - if (!frame) return null; - // Example: at callMain (a.out.js:6335:22) - if (match = /\((.*):(\d+):(\d+)\)$/.exec(frame)) { - source = {file: match[1], line: match[2], column: match[3]}; - // Example: main@a.out.js:1337:42 - } else if (match = /@(.*):(\d+):(\d+)/.exec(frame)) { - source = {file: match[1], line: match[2], column: match[3]}; - } - } - UNWIND_CACHE.last_get_source_pc = pc; - UNWIND_CACHE.last_source = source; - return source; - }; - - - - var _emscripten_pc_get_file = (pc) => { - var result = convertPCtoSourceLocation(pc); - if (!result) return 0; - - _free(_emscripten_pc_get_file.ret ?? 0); - _emscripten_pc_get_file.ret = stringToNewUTF8(result.file); - return _emscripten_pc_get_file.ret; - }; - _emscripten_pc_get_file.sig = 'pp'; - - var _emscripten_pc_get_line = (pc) => { - var result = convertPCtoSourceLocation(pc); - return result ? result.line : 0; - }; - _emscripten_pc_get_line.sig = 'ip'; - - var _emscripten_pc_get_column = (pc) => { - var result = convertPCtoSourceLocation(pc); - return result ? result.column || 0 : 0; - }; - _emscripten_pc_get_column.sig = 'ip'; - - - - var _sched_yield = () => 0; - _sched_yield.sig = 'i'; - - - - - - - - - - - - - - - - var wasiRightsToMuslOFlags = (rights) => { - if ((rights & 2) && (rights & 64)) { - return 2; - } - if (rights & 2) { - return 0; - } - if (rights & 64) { - return 1; - } - throw new FS.ErrnoError(28); - }; - - var wasiOFlagsToMuslOFlags = (oflags) => { - var musl_oflags = 0; - if (oflags & 1) { - musl_oflags |= 64; - } - if (oflags & 8) { - musl_oflags |= 512; - } - if (oflags & 2) { - musl_oflags |= 65536; - } - if (oflags & 4) { - musl_oflags |= 128; - } - return musl_oflags; - }; - - - - - - - var _emscripten_unwind_to_js_event_loop = () => { - throw 'unwind'; - }; - _emscripten_unwind_to_js_event_loop.sig = 'v'; - - - var setImmediateWrapped = (func) => { - setImmediateWrapped.mapping ||= []; - var id = setImmediateWrapped.mapping.length; - setImmediateWrapped.mapping[id] = setImmediate(() => { - setImmediateWrapped.mapping[id] = undefined; - func(); - }); - return id; - }; - - - - var safeRequestAnimationFrame = (func) => { - runtimeKeepalivePush(); - return MainLoop.requestAnimationFrame(() => { - runtimeKeepalivePop(); - callUserCallback(func); - }); - }; - - var clearImmediateWrapped = (id) => { - clearImmediate(setImmediateWrapped.mapping[id]); - setImmediateWrapped.mapping[id] = undefined; - }; - - - - var emClearImmediate; - var emSetImmediate; - - var emClearImmediate_deps = ["$emSetImmediate"]; - - - - - - var _emscripten_set_immediate = (cb, userData) => { - runtimeKeepalivePush(); - return emSetImmediate(() => { - runtimeKeepalivePop(); - callUserCallback(() => ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(userData)); - }); - }; - _emscripten_set_immediate.sig = 'ipp'; - - - var _emscripten_clear_immediate = (id) => { - runtimeKeepalivePop(); - emClearImmediate(id); - }; - _emscripten_clear_immediate.sig = 'vi'; - - - - - var _emscripten_set_immediate_loop = (cb, userData) => { - function tick() { - callUserCallback(() => { - if (((a1) => {} /* a dynamic function call to signature ii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(userData)) { - emSetImmediate(tick); - } else { - runtimeKeepalivePop(); - } - }); - } - runtimeKeepalivePush(); - emSetImmediate(tick); - }; - _emscripten_set_immediate_loop.sig = 'vpp'; - - var _emscripten_set_timeout = (cb, msecs, userData) => - safeSetTimeout(() => ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(userData), msecs); - _emscripten_set_timeout.sig = 'ipdp'; - - var _emscripten_clear_timeout = clearTimeout; - _emscripten_clear_timeout.sig = 'vi'; - - - - - var _emscripten_set_timeout_loop = (cb, msecs, userData) => { - function tick() { - var t = _emscripten_get_now(); - var n = t + msecs; - runtimeKeepalivePop(); - callUserCallback(() => { - if (((a1, a2) => {} /* a dynamic function call to signature idi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(t, userData)) { - runtimeKeepalivePush(); - // Save a little bit of code space: modern browsers should treat - // negative setTimeout as timeout of 0 - // (https://stackoverflow.com/questions/8430966/is-calling-settimeout-with-a-negative-delay-ok) - var remaining = n - _emscripten_get_now(); - // Recent revsions of node, however, give TimeoutNegativeWarning - remaining = Math.max(0, remaining); - setTimeout(tick, remaining); - } - }); - } - runtimeKeepalivePush(); - return setTimeout(tick, 0); - }; - _emscripten_set_timeout_loop.sig = 'vpdp'; - - - var _emscripten_set_interval = (cb, msecs, userData) => { - runtimeKeepalivePush(); - return setInterval(() => { - callUserCallback(() => ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(userData)); - }, msecs); - }; - _emscripten_set_interval.sig = 'ipdp'; - - var _emscripten_clear_interval = (id) => { - runtimeKeepalivePop(); - clearInterval(id); - }; - _emscripten_clear_interval.sig = 'vi'; - - - var _emscripten_async_call = (func, arg, millis) => { - var wrapper = () => ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - - if (millis >= 0 - // node does not support requestAnimationFrame - || ENVIRONMENT_IS_NODE - ) { - safeSetTimeout(wrapper, millis); - } else { - safeRequestAnimationFrame(wrapper); - } - }; - _emscripten_async_call.sig = 'vppi'; - - var registerPostMainLoop = (f) => { - // Does nothing unless $MainLoop is included/used. - typeof MainLoop != 'undefined' && MainLoop.postMainLoop.push(f); - }; - - var registerPreMainLoop = (f) => { - // Does nothing unless $MainLoop is included/used. - typeof MainLoop != 'undefined' && MainLoop.preMainLoop.push(f); - }; - - - var _emscripten_get_main_loop_timing = (mode, value) => { - if (mode) HEAP32[((mode)>>2)] = MainLoop.timingMode; - if (value) HEAP32[((value)>>2)] = MainLoop.timingValue; - }; - _emscripten_get_main_loop_timing.sig = 'vpp'; - - - var _emscripten_set_main_loop = (func, fps, simulateInfiniteLoop) => { - var iterFunc = (() => {} /* a dynamic function call to signature v, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */); - setMainLoop(iterFunc, fps, simulateInfiniteLoop); - }; - _emscripten_set_main_loop.sig = 'vpii'; - - - var _emscripten_set_main_loop_arg = (func, arg, fps, simulateInfiniteLoop) => { - var iterFunc = () => ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - setMainLoop(iterFunc, fps, simulateInfiniteLoop, arg); - }; - _emscripten_set_main_loop_arg.sig = 'vppii'; - - var _emscripten_cancel_main_loop = () => { - MainLoop.pause(); - MainLoop.func = null; - }; - _emscripten_cancel_main_loop.sig = 'v'; - - var _emscripten_pause_main_loop = () => MainLoop.pause(); - _emscripten_pause_main_loop.sig = 'v'; - - var _emscripten_resume_main_loop = () => MainLoop.resume(); - _emscripten_resume_main_loop.sig = 'v'; - - - var __emscripten_push_main_loop_blocker = (func, arg, name) => { - MainLoop.queue.push({ func: () => { - ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - }, name: UTF8ToString(name), counted: true }); - MainLoop.updateStatus(); - }; - __emscripten_push_main_loop_blocker.sig = 'vppp'; - - - var __emscripten_push_uncounted_main_loop_blocker = (func, arg, name) => { - MainLoop.queue.push({ func: () => { - ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - }, name: UTF8ToString(name), counted: false }); - MainLoop.updateStatus(); - }; - __emscripten_push_uncounted_main_loop_blocker.sig = 'vppp'; - - var _emscripten_set_main_loop_expected_blockers = (num) => { - MainLoop.expectedBlockers = num; - MainLoop.remainingBlockers = num; - MainLoop.updateStatus(); - }; - _emscripten_set_main_loop_expected_blockers.sig = 'vi'; - - - - - var idsToPromises = (idBuf, size) => { - var promises = []; - for (var i = 0; i < size; i++) { - var id = HEAP32[(((idBuf)+(i*4))>>2)]; - promises[i] = getPromise(id); - } - return promises; - }; - - - - - - - - - - - var makePromiseCallback = (callback, userData) => { - return (value) => { - runtimeKeepalivePop();; - var stack = stackSave(); - // Allocate space for the result value and initialize it to NULL. - var resultPtr = stackAlloc(POINTER_SIZE); - HEAPU32[((resultPtr)>>2)] = 0; - try { - var result = - ((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(resultPtr, userData, value); - var resultVal = HEAPU32[((resultPtr)>>2)]; - } catch (e) { - // If the thrown value is potentially a valid pointer, use it as the - // rejection reason. Otherwise use a null pointer as the reason. If we - // allow arbitrary objects to be thrown here, we will get a TypeError in - // MEMORY64 mode when they are later converted to void* rejection - // values. - if (typeof e != 'number') { - throw 0; - } - throw e; - } finally { - // Thrown errors will reject the promise, but at least we will restore - // the stack first. - stackRestore(stack); - } - switch (result) { - case 0: - return resultVal; - case 1: - return getPromise(resultVal); - case 2: - var ret = getPromise(resultVal); - _emscripten_promise_destroy(resultVal); - return ret; - case 3: - throw resultVal; - } - }; - }; - - - - - var _emscripten_promise_then = (id, onFulfilled, onRejected, userData) => { - runtimeKeepalivePush();; - var promise = getPromise(id); - var newId = promiseMap.allocate({ - promise: promise.then(makePromiseCallback(onFulfilled, userData), - makePromiseCallback(onRejected, userData)) - }); - return newId; - }; - _emscripten_promise_then.sig = 'ppppp'; - - - var _emscripten_promise_all = (idBuf, resultBuf, size) => { - var promises = idsToPromises(idBuf, size); - var id = promiseMap.allocate({ - promise: Promise.all(promises).then((results) => { - if (resultBuf) { - for (var i = 0; i < size; i++) { - var result = results[i]; - HEAPU32[(((resultBuf)+(i*4))>>2)] = result; - } - } - return resultBuf; - }) - }); - return id; - }; - _emscripten_promise_all.sig = 'pppp'; - - var setPromiseResult = (ptr, fulfill, value) => { - var result = fulfill ? 0 : 3 - HEAP32[((ptr)>>2)] = result; - HEAPU32[(((ptr)+(4))>>2)] = value; - }; - - - - var _emscripten_promise_all_settled = (idBuf, resultBuf, size) => { - var promises = idsToPromises(idBuf, size); - var id = promiseMap.allocate({ - promise: Promise.allSettled(promises).then((results) => { - if (resultBuf) { - var offset = resultBuf; - for (var i = 0; i < size; i++, offset += 8) { - if (results[i].status === 'fulfilled') { - setPromiseResult(offset, true, results[i].value); - } else { - setPromiseResult(offset, false, results[i].reason); - } - } - } - return resultBuf; - }) - }); - return id; - }; - _emscripten_promise_all_settled.sig = 'pppp'; - - - var _emscripten_promise_any = (idBuf, errorBuf, size) => { - var promises = idsToPromises(idBuf, size); - var id = promiseMap.allocate({ - promise: Promise.any(promises).catch((err) => { - if (errorBuf) { - for (var i = 0; i < size; i++) { - HEAPU32[(((errorBuf)+(i*4))>>2)] = err.errors[i]; - } - } - throw errorBuf; - }) - }); - return id; - }; - _emscripten_promise_any.sig = 'pppp'; - - - var _emscripten_promise_race = (idBuf, size) => { - var promises = idsToPromises(idBuf, size); - var id = promiseMap.allocate({ - promise: Promise.race(promises) - }); - return id; - }; - _emscripten_promise_race.sig = 'ppp'; - - - var _emscripten_promise_await = (returnValuePtr, id) => { - return Asyncify.handleAsync(() => getPromise(id).then( - value => setPromiseResult(returnValuePtr, true, value), - error => setPromiseResult(returnValuePtr, false, error) - )); - }; - _emscripten_promise_await.sig = 'vpp'; - _emscripten_promise_await.isAsync = true; - - - var ___cxa_find_matching_catch_3 = (arg0) => findMatchingCatch([arg0]); - ___cxa_find_matching_catch_3.sig = 'pp'; - - var ___cxa_find_matching_catch_4 = (arg0,arg1) => findMatchingCatch([arg0,arg1]); - ___cxa_find_matching_catch_4.sig = 'ppp'; - - - - var exceptionCaught = []; - - - - - - var ___cxa_rethrow = () => { - var info = exceptionCaught.pop(); - if (!info) { - abort('no exception to throw'); - } - var ptr = info.excPtr; - if (!info.get_rethrown()) { - // Only pop if the corresponding push was through rethrow_primary_exception - exceptionCaught.push(info); - info.set_rethrown(true); - info.set_caught(false); - uncaughtExceptionCount++; - } - exceptionLast = ptr; - throw exceptionLast; - }; - ___cxa_rethrow.sig = 'v'; - - var _llvm_eh_typeid_for = (type) => type; - _llvm_eh_typeid_for.sig = 'vp'; - - - - - var ___cxa_begin_catch = (ptr) => { - var info = new ExceptionInfo(ptr); - if (!info.get_caught()) { - info.set_caught(true); - uncaughtExceptionCount--; - } - info.set_rethrown(false); - exceptionCaught.push(info); - ___cxa_increment_exception_refcount(ptr); - return ___cxa_get_exception_ptr(ptr); - }; - ___cxa_begin_catch.sig = 'pp'; - - - - - var ___cxa_end_catch = () => { - // Clear state flag. - _setThrew(0, 0); - // Call destructor if one is registered then clear it. - var info = exceptionCaught.pop(); - - ___cxa_decrement_exception_refcount(info.excPtr); - exceptionLast = 0; // XXX in decRef? - }; - ___cxa_end_catch.sig = 'v'; - - var ___cxa_uncaught_exceptions = () => uncaughtExceptionCount; - ___cxa_uncaught_exceptions.sig = 'i'; - - var ___cxa_call_unexpected = (exception) => abort('Unexpected exception thrown, this is not properly supported - aborting'); - ___cxa_call_unexpected.sig = 'vp'; - - - var ___cxa_current_primary_exception = () => { - if (!exceptionCaught.length) { - return 0; - } - var info = exceptionCaught[exceptionCaught.length - 1]; - ___cxa_increment_exception_refcount(info.excPtr); - return info.excPtr; - }; - ___cxa_current_primary_exception.sig = 'p'; - - function ___cxa_current_exception_type() { - if (!exceptionCaught.length) { - return 0; - } - var info = exceptionCaught[exceptionCaught.length - 1]; - return info.get_type(); - } - ___cxa_current_exception_type.sig = 'p'; - - - - var ___cxa_rethrow_primary_exception = (ptr) => { - if (!ptr) return; - var info = new ExceptionInfo(ptr); - exceptionCaught.push(info); - info.set_rethrown(true); - ___cxa_rethrow(); - }; - ___cxa_rethrow_primary_exception.sig = 'vp'; - - - - - - - - - - var Browser = { - useWebGL:false, - isFullscreen:false, - pointerLock:false, - moduleContextCreatedCallbacks:[], - workers:[], - preloadedImages:{ - }, - preloadedAudios:{ - }, - getCanvas:() => Module['canvas'], - init() { - if (Browser.initted) return; - Browser.initted = true; - - // Support for plugins that can process preloaded files. You can add more of these to - // your app by creating and appending to preloadPlugins. - // - // Each plugin is asked if it can handle a file based on the file's name. If it can, - // it is given the file's raw data. When it is done, it calls a callback with the file's - // (possibly modified) data. For example, a plugin might decompress a file, or it - // might create some side data structure for use later (like an Image element, etc.). - - var imagePlugin = {}; - imagePlugin['canHandle'] = function imagePlugin_canHandle(name) { - return !Module['noImageDecoding'] && /\.(jpg|jpeg|png|bmp|webp)$/i.test(name); - }; - imagePlugin['handle'] = async function imagePlugin_handle(byteArray, name) { - var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); - if (b.size !== byteArray.length) { // Safari bug #118630 - // Safari's Blob can only take an ArrayBuffer - b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) }); - } - var url = URL.createObjectURL(b); - return new Promise((resolve, reject) => { - var img = new Image(); - img.onload = () => { - var canvas = /** @type {!HTMLCanvasElement} */ (document.createElement('canvas')); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - Browser.preloadedImages[name] = canvas; - URL.revokeObjectURL(url); - resolve(byteArray); - }; - img.onerror = (event) => { - err(`Image ${url} could not be decoded`); - reject(); - }; - img.src = url; - }); - }; - preloadPlugins.push(imagePlugin); - - var audioPlugin = {}; - audioPlugin['canHandle'] = function audioPlugin_canHandle(name) { - return !Module['noAudioDecoding'] && name.slice(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 }; - }; - audioPlugin['handle'] = async function audioPlugin_handle(byteArray, name) { - return new Promise((resolve, reject) => { - var done = false; - function finish(audio) { - if (done) return; - done = true; - Browser.preloadedAudios[name] = audio; - resolve(byteArray); - } - var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); - var url = URL.createObjectURL(b); // XXX we never revoke this! - var audio = new Audio(); - audio.addEventListener('canplaythrough', () => finish(audio), false); // use addEventListener due to chromium bug 124926 - audio.onerror = function audio_onerror(event) { - if (done) return; - err(`warning: browser could not fully decode audio ${name}, trying slower base64 approach`); - function encode64(data) { - var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; - var PAD = '='; - var ret = ''; - var leftchar = 0; - var leftbits = 0; - for (var i = 0; i < data.length; i++) { - leftchar = (leftchar << 8) | data[i]; - leftbits += 8; - while (leftbits >= 6) { - var curr = (leftchar >> (leftbits-6)) & 0x3f; - leftbits -= 6; - ret += BASE[curr]; - } - } - if (leftbits == 2) { - ret += BASE[(leftchar&3) << 4]; - ret += PAD + PAD; - } else if (leftbits == 4) { - ret += BASE[(leftchar&0xf) << 2]; - ret += PAD; - } - return ret; - } - audio.src = 'data:audio/x-' + name.slice(-3) + ';base64,' + encode64(byteArray); - finish(audio); // we don't wait for confirmation this worked - but it's worth trying - }; - audio.src = url; - // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror - safeSetTimeout(() => { - finish(audio); // try to use it even though it is not necessarily ready to play - }, 10000); - }); - }; - preloadPlugins.push(audioPlugin); - - // Canvas event setup - - function pointerLockChange() { - var canvas = Browser.getCanvas(); - Browser.pointerLock = document.pointerLockElement === canvas; - } - var canvas = Browser.getCanvas(); - if (canvas) { - // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module - // Module['forcedAspectRatio'] = 4 / 3; - - document.addEventListener('pointerlockchange', pointerLockChange, false); - - if (Module['elementPointerLock']) { - canvas.addEventListener("click", (ev) => { - if (!Browser.pointerLock && Browser.getCanvas().requestPointerLock) { - Browser.getCanvas().requestPointerLock(); - ev.preventDefault(); - } - }, false); - } - } - }, - createContext(/** @type {HTMLCanvasElement} */ canvas, useWebGL, setInModule, webGLContextAttributes) { - if (useWebGL && Module['ctx'] && canvas == Browser.getCanvas()) return Module['ctx']; // no need to recreate GL context if it's already been created for this canvas. - - var ctx; - var contextHandle; - if (useWebGL) { - // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults. - var contextAttributes = { - antialias: false, - alpha: false, - majorVersion: 1, - }; - - if (webGLContextAttributes) { - for (var attribute in webGLContextAttributes) { - contextAttributes[attribute] = webGLContextAttributes[attribute]; - } - } - - // This check of existence of GL is here to satisfy Closure compiler, which yells if variable GL is referenced below but GL object is not - // actually compiled in because application is not doing any GL operations. TODO: Ideally if GL is not being used, this function - // Browser.createContext() should not even be emitted. - if (typeof GL != 'undefined') { - contextHandle = GL.createContext(canvas, contextAttributes); - if (contextHandle) { - ctx = GL.getContext(contextHandle).GLctx; - } - } - } else { - ctx = canvas.getContext('2d'); - } - - if (!ctx) return null; - - if (setInModule) { - Module['ctx'] = ctx; - if (useWebGL) GL.makeContextCurrent(contextHandle); - Browser.useWebGL = useWebGL; - Browser.moduleContextCreatedCallbacks.forEach((callback) => callback()); - Browser.init(); - } - return ctx; - }, - fullscreenHandlersInstalled:false, - lockPointer:undefined, - resizeCanvas:undefined, - requestFullscreen(lockPointer, resizeCanvas) { - Browser.lockPointer = lockPointer; - Browser.resizeCanvas = resizeCanvas; - if (typeof Browser.lockPointer == 'undefined') Browser.lockPointer = true; - if (typeof Browser.resizeCanvas == 'undefined') Browser.resizeCanvas = false; - - var canvas = Browser.getCanvas(); - function fullscreenChange() { - Browser.isFullscreen = false; - var canvasContainer = canvas.parentNode; - if (getFullscreenElement() === canvasContainer) { - canvas.exitFullscreen = Browser.exitFullscreen; - if (Browser.lockPointer) canvas.requestPointerLock(); - Browser.isFullscreen = true; - if (Browser.resizeCanvas) { - Browser.setFullscreenCanvasSize(); - } else { - Browser.updateCanvasDimensions(canvas); - } - } else { - // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen - canvasContainer.parentNode.insertBefore(canvas, canvasContainer); - canvasContainer.parentNode.removeChild(canvasContainer); - - if (Browser.resizeCanvas) { - Browser.setWindowedCanvasSize(); - } else { - Browser.updateCanvasDimensions(canvas); - } - } - Module['onFullScreen']?.(Browser.isFullscreen); - Module['onFullscreen']?.(Browser.isFullscreen); - } - - if (!Browser.fullscreenHandlersInstalled) { - Browser.fullscreenHandlersInstalled = true; - document.addEventListener('fullscreenchange', fullscreenChange, false); - document.addEventListener('mozfullscreenchange', fullscreenChange, false); - document.addEventListener('webkitfullscreenchange', fullscreenChange, false); - document.addEventListener('MSFullscreenChange', fullscreenChange, false); - } - - // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root - var canvasContainer = document.createElement("div"); - canvas.parentNode.insertBefore(canvasContainer, canvas); - canvasContainer.appendChild(canvas); - - // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size) - canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] || - canvasContainer['mozRequestFullScreen'] || - canvasContainer['msRequestFullscreen'] || - (canvasContainer['webkitRequestFullscreen'] ? () => canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) : null) || - (canvasContainer['webkitRequestFullScreen'] ? () => canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) : null); - - canvasContainer.requestFullscreen(); - }, - exitFullscreen() { - // This is workaround for chrome. Trying to exit from fullscreen - // not in fullscreen state will cause "TypeError: Document not active" - // in chrome. See https://github.com/emscripten-core/emscripten/pull/8236 - if (!Browser.isFullscreen) { - return false; - } - - var CFS = document['exitFullscreen'] || - document['cancelFullScreen'] || - document['mozCancelFullScreen'] || - document['msExitFullscreen'] || - document['webkitCancelFullScreen'] || - (() => {}); - CFS.apply(document, []); - return true; - }, - safeSetTimeout(func, timeout) { - // Legacy function, this is used by the SDL2 port so we need to keep it - // around at least until that is updated. - // See https://github.com/libsdl-org/SDL/pull/6304 - return safeSetTimeout(func, timeout); - }, - getMimetype(name) { - return { - 'jpg': 'image/jpeg', - 'jpeg': 'image/jpeg', - 'png': 'image/png', - 'bmp': 'image/bmp', - 'ogg': 'audio/ogg', - 'wav': 'audio/wav', - 'mp3': 'audio/mpeg' - }[name.slice(name.lastIndexOf('.')+1)]; - }, - getUserMedia(func) { - window.getUserMedia ||= navigator['getUserMedia'] || - navigator['mozGetUserMedia']; - window.getUserMedia(func); - }, - getMovementX(event) { - return event['movementX'] || - event['mozMovementX'] || - event['webkitMovementX'] || - 0; - }, - getMovementY(event) { - return event['movementY'] || - event['mozMovementY'] || - event['webkitMovementY'] || - 0; - }, - getMouseWheelDelta(event) { - var delta = 0; - switch (event.type) { - case 'DOMMouseScroll': - // 3 lines make up a step - delta = event.detail / 3; - break; - case 'mousewheel': - // 120 units make up a step - delta = event.wheelDelta / 120; - break; - case 'wheel': - delta = event.deltaY - switch (event.deltaMode) { - case 0: - // DOM_DELTA_PIXEL: 100 pixels make up a step - delta /= 100; - break; - case 1: - // DOM_DELTA_LINE: 3 lines make up a step - delta /= 3; - break; - case 2: - // DOM_DELTA_PAGE: A page makes up 80 steps - delta *= 80; - break; - default: - abort('unrecognized mouse wheel delta mode: ' + event.deltaMode); - } - break; - default: - abort('unrecognized mouse wheel event: ' + event.type); - } - return delta; - }, - mouseX:0, - mouseY:0, - mouseMovementX:0, - mouseMovementY:0, - touches:{ - }, - lastTouches:{ - }, - calculateMouseCoords(pageX, pageY) { - // Calculate the movement based on the changes - // in the coordinates. - var canvas = Browser.getCanvas(); - var rect = canvas.getBoundingClientRect(); - - // Neither .scrollX or .pageXOffset are defined in a spec, but - // we prefer .scrollX because it is currently in a spec draft. - // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/) - var scrollX = ((typeof window.scrollX != 'undefined') ? window.scrollX : window.pageXOffset); - var scrollY = ((typeof window.scrollY != 'undefined') ? window.scrollY : window.pageYOffset); - var adjustedX = pageX - (scrollX + rect.left); - var adjustedY = pageY - (scrollY + rect.top); - - // the canvas might be CSS-scaled compared to its backbuffer; - // SDL-using content will want mouse coordinates in terms - // of backbuffer units. - adjustedX = adjustedX * (canvas.width / rect.width); - adjustedY = adjustedY * (canvas.height / rect.height); - - return { x: adjustedX, y: adjustedY }; - }, - setMouseCoords(pageX, pageY) { - const {x, y} = Browser.calculateMouseCoords(pageX, pageY); - Browser.mouseMovementX = x - Browser.mouseX; - Browser.mouseMovementY = y - Browser.mouseY; - Browser.mouseX = x; - Browser.mouseY = y; - }, - calculateMouseEvent(event) { // event should be mousemove, mousedown or mouseup - if (Browser.pointerLock) { - // When the pointer is locked, calculate the coordinates - // based on the movement of the mouse. - // Workaround for Firefox bug 764498 - if (event.type != 'mousemove' && - ('mozMovementX' in event)) { - Browser.mouseMovementX = Browser.mouseMovementY = 0; - } else { - Browser.mouseMovementX = Browser.getMovementX(event); - Browser.mouseMovementY = Browser.getMovementY(event); - } - - // add the mouse delta to the current absolute mouse position - Browser.mouseX += Browser.mouseMovementX; - Browser.mouseY += Browser.mouseMovementY; - } else { - if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') { - var touch = event.touch; - if (touch === undefined) { - return; // the "touch" property is only defined in SDL - - } - var coords = Browser.calculateMouseCoords(touch.pageX, touch.pageY); - - if (event.type === 'touchstart') { - Browser.lastTouches[touch.identifier] = coords; - Browser.touches[touch.identifier] = coords; - } else if (event.type === 'touchend' || event.type === 'touchmove') { - var last = Browser.touches[touch.identifier]; - last ||= coords; - Browser.lastTouches[touch.identifier] = last; - Browser.touches[touch.identifier] = coords; - } - return; - } - - Browser.setMouseCoords(event.pageX, event.pageY); - } - }, - resizeListeners:[], - updateResizeListeners() { - var canvas = Browser.getCanvas(); - Browser.resizeListeners.forEach((listener) => listener(canvas.width, canvas.height)); - }, - setCanvasSize(width, height, noUpdates) { - var canvas = Browser.getCanvas(); - Browser.updateCanvasDimensions(canvas, width, height); - if (!noUpdates) Browser.updateResizeListeners(); - }, - windowedWidth:0, - windowedHeight:0, - setFullscreenCanvasSize() { - // check if SDL is available - if (typeof SDL != "undefined") { - var flags = HEAPU32[((SDL.screen)>>2)]; - flags = flags | 0x00800000; // set SDL_FULLSCREEN flag - HEAP32[((SDL.screen)>>2)] = flags; - } - Browser.updateCanvasDimensions(Browser.getCanvas()); - Browser.updateResizeListeners(); - }, - setWindowedCanvasSize() { - // check if SDL is available - if (typeof SDL != "undefined") { - var flags = HEAPU32[((SDL.screen)>>2)]; - flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag - HEAP32[((SDL.screen)>>2)] = flags; - } - Browser.updateCanvasDimensions(Browser.getCanvas()); - Browser.updateResizeListeners(); - }, - updateCanvasDimensions(canvas, wNative, hNative) { - if (wNative && hNative) { - canvas.widthNative = wNative; - canvas.heightNative = hNative; - } else { - wNative = canvas.widthNative; - hNative = canvas.heightNative; - } - var w = wNative; - var h = hNative; - if (Module['forcedAspectRatio'] > 0) { - if (w/h < Module['forcedAspectRatio']) { - w = Math.round(h * Module['forcedAspectRatio']); - } else { - h = Math.round(w / Module['forcedAspectRatio']); - } - } - if ((getFullscreenElement() === canvas.parentNode) && (typeof screen != 'undefined')) { - var factor = Math.min(screen.width / w, screen.height / h); - w = Math.round(w * factor); - h = Math.round(h * factor); - } - if (Browser.resizeCanvas) { - if (canvas.width != w) canvas.width = w; - if (canvas.height != h) canvas.height = h; - if (typeof canvas.style != 'undefined') { - canvas.style.removeProperty( "width"); - canvas.style.removeProperty("height"); - } - } else { - if (canvas.width != wNative) canvas.width = wNative; - if (canvas.height != hNative) canvas.height = hNative; - if (typeof canvas.style != 'undefined') { - if (w != wNative || h != hNative) { - canvas.style.setProperty( "width", w + "px", "important"); - canvas.style.setProperty("height", h + "px", "important"); - } else { - canvas.style.removeProperty( "width"); - canvas.style.removeProperty("height"); - } - } - } - }, - }; - - var requestFullscreen = Browser.requestFullscreen; - - var setCanvasSize = Browser.setCanvasSize; - - var getUserMedia = Browser.getUserMedia; - - var createContext = Browser.createContext; - - - - - - var _emscripten_run_preload_plugins = (file, onload, onerror) => { - runtimeKeepalivePush(); - - var _file = UTF8ToString(file); - var data = FS.analyzePath(_file); - if (!data.exists) return -1; - FS.createPreloadedFile( - PATH.dirname(_file), - PATH.basename(_file), - // TODO: This copy is not needed if the contents are already a Uint8Array, - // which they often are (and always are in WasmFS). - new Uint8Array(data.object.contents), true, true, - () => { - runtimeKeepalivePop(); - if (onload) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(file); - }, - () => { - runtimeKeepalivePop(); - if (onerror) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(file); - }, - true // don'tCreateFile - it's already there - ); - return 0; - }; - _emscripten_run_preload_plugins.sig = 'ippp'; - - var Browser_asyncPrepareDataCounter = 0; - - - - - - - var _emscripten_run_preload_plugins_data = (data, size, suffix, arg, onload, onerror) => { - runtimeKeepalivePush(); - - var _suffix = UTF8ToString(suffix); - var name = 'prepare_data_' + (Browser_asyncPrepareDataCounter++) + '.' + _suffix; - var cname = stringToNewUTF8(name); - FS.createPreloadedFile( - '/', - name, - HEAPU8.subarray((data), data + size), - true, true, - () => { - runtimeKeepalivePop(); - if (onload) ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg, cname); - }, - () => { - runtimeKeepalivePop(); - if (onerror) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - }, - true // don'tCreateFile - it's already there - ); - }; - _emscripten_run_preload_plugins_data.sig = 'vpipppp'; - - - - var _emscripten_async_run_script = (script, millis) => { - // TODO: cache these to avoid generating garbage - safeSetTimeout(() => _emscripten_run_script(script), millis); - }; - _emscripten_async_run_script.sig = 'vpi'; - - - - - - var _emscripten_async_load_script = async (url, onload, onerror) => { - url = UTF8ToString(url); - runtimeKeepalivePush(); - - var loadDone = () => { - runtimeKeepalivePop(); - if (onload) { - var onloadCallback = () => callUserCallback((() => {} /* a dynamic function call to signature v, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)); - if (runDependencies > 0) { - dependenciesFulfilled = onloadCallback; - } else { - onloadCallback(); - } - } - } - - var loadError = () => { - runtimeKeepalivePop(); - if (onerror) { - callUserCallback((() => {} /* a dynamic function call to signature v, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)); - } - }; - - if (ENVIRONMENT_IS_NODE) { - try { - var data = await readAsync(url, false); - eval(data); - loadDone(); - } catch (e) { - err(e); - loadError(); - } - return; - } - - var script = document.createElement('script'); - script.onload = loadDone; - script.onerror = loadError; - script.src = url; - document.body.appendChild(script); - }; - _emscripten_async_load_script.sig = 'vppp'; - - var _emscripten_get_window_title = () => { - var buflen = 256; - - if (!_emscripten_get_window_title.buffer) { - _emscripten_get_window_title.buffer = _malloc(buflen); - } - - stringToUTF8(document.title, _emscripten_get_window_title.buffer, buflen); - - return _emscripten_get_window_title.buffer; - }; - _emscripten_get_window_title.sig = 'p'; - - - var _emscripten_set_window_title = (title) => document.title = UTF8ToString(title); - _emscripten_set_window_title.sig = 'vp'; - - var _emscripten_get_screen_size = (width, height) => { - HEAP32[((width)>>2)] = screen.width; - HEAP32[((height)>>2)] = screen.height; - }; - _emscripten_get_screen_size.sig = 'vpp'; - - var _emscripten_hide_mouse = () => { - var styleSheet = document.styleSheets[0]; - var rules = styleSheet.cssRules; - for (var i = 0; i < rules.length; i++) { - if (rules[i].cssText.startsWith('canvas')) { - styleSheet.deleteRule(i); - i--; - } - } - styleSheet.insertRule('canvas.emscripten { border: 1px solid black; cursor: none; }', 0); - }; - _emscripten_hide_mouse.sig = 'v'; - - var _emscripten_set_canvas_size = (width, height) => Browser.setCanvasSize(width, height); - _emscripten_set_canvas_size.sig = 'vii'; - - var _emscripten_get_canvas_size = (width, height, isFullscreen) => { - var canvas = Browser.getCanvas(); - HEAP32[((width)>>2)] = canvas.width; - HEAP32[((height)>>2)] = canvas.height; - HEAP32[((isFullscreen)>>2)] = Browser.isFullscreen ? 1 : 0; - }; - _emscripten_get_canvas_size.sig = 'vppp'; - - - - - - var _emscripten_create_worker = (url) => { - url = UTF8ToString(url); - var id = Browser.workers.length; - var info = { - worker: new Worker(url), - callbacks: [], - awaited: 0, - buffer: 0, - }; - info.worker.onmessage = function info_worker_onmessage(msg) { - if (ABORT) return; - var info = Browser.workers[id]; - if (!info) return; // worker was destroyed meanwhile - var callbackId = msg.data['callbackId']; - var callbackInfo = info.callbacks[callbackId]; - if (!callbackInfo) return; // no callback or callback removed meanwhile - // Don't trash our callback state if we expect additional calls. - if (msg.data['finalResponse']) { - info.awaited--; - info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this - runtimeKeepalivePop(); - } - var data = msg.data['data']; - if (data) { - if (!data.byteLength) data = new Uint8Array(data); - info.buffer = _realloc(info.buffer, data.length); - HEAPU8.set(data, info.buffer); - callbackInfo.func(info.buffer, data.length, callbackInfo.arg); - } else { - callbackInfo.func(0, 0, callbackInfo.arg); - } - }; - Browser.workers.push(info); - return id; - }; - _emscripten_create_worker.sig = 'ip'; - - - var _emscripten_destroy_worker = (id) => { - var info = Browser.workers[id]; - info.worker.terminate(); - _free(info.buffer); - Browser.workers[id] = null; - }; - _emscripten_destroy_worker.sig = 'vi'; - - - - var _emscripten_call_worker = (id, funcName, data, size, callback, arg) => { - funcName = UTF8ToString(funcName); - var info = Browser.workers[id]; - var callbackId = -1; - if (callback) { - // If we are waiting for a response from the worker we need to keep - // the runtime alive at least long enough to receive it. - // The corresponding runtimeKeepalivePop is in the `finalResponse` - // handler above. - runtimeKeepalivePush(); - callbackId = info.callbacks.length; - info.callbacks.push({ - func: ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */), - arg - }); - info.awaited++; - } - var transferObject = { - 'funcName': funcName, - 'callbackId': callbackId, - 'data': data ? new Uint8Array(HEAPU8.subarray((data), data + size)) : 0 - }; - if (data) { - info.worker.postMessage(transferObject, [transferObject.data.buffer]); - } else { - info.worker.postMessage(transferObject); - } - }; - _emscripten_call_worker.sig = 'vippipp'; - - var _emscripten_get_worker_queue_size = (id) => { - var info = Browser.workers[id]; - if (!info) return -1; - return info.awaited; - }; - _emscripten_get_worker_queue_size.sig = 'ii'; - - var getPreloadedImageData = (path, w, h) => { - path = PATH_FS.resolve(path); - - var canvas = /** @type {HTMLCanvasElement} */(Browser.preloadedImages[path]); - if (!canvas) return 0; - - var ctx = canvas.getContext("2d"); - var image = ctx.getImageData(0, 0, canvas.width, canvas.height); - var buf = _malloc(canvas.width * canvas.height * 4); - - HEAPU8.set(image.data, buf); - - HEAP32[((w)>>2)] = canvas.width; - HEAP32[((h)>>2)] = canvas.height; - return buf; - }; - - - - var _emscripten_get_preloaded_image_data = (path, w, h) => getPreloadedImageData(UTF8ToString(path), w, h); - _emscripten_get_preloaded_image_data.sig = 'pppp'; - - var getPreloadedImageData__data = ["$PATH_FS","malloc"]; - - - - - var _emscripten_get_preloaded_image_data_from_FILE = (file, w, h) => { - var fd = _fileno(file); - var stream = FS.getStream(fd); - if (stream) { - return getPreloadedImageData(stream.path, w, h); - } - - return 0; - }; - _emscripten_get_preloaded_image_data_from_FILE.sig = 'pppp'; - - var wget = { - wgetRequests:{ - }, - nextWgetRequestHandle:0, - getNextWgetRequestHandle() { - var handle = wget.nextWgetRequestHandle; - wget.nextWgetRequestHandle++; - return handle; - }, - }; - - - - - - - - /** - * @param {number=} mode Optionally, the mode to create in. Uses mkdir's - * default if not set. - */ - var FS_mkdirTree = (path, mode) => FS.mkdirTree(path, mode); - - - - - - var _emscripten_async_wget = (url, file, onload, onerror) => { - runtimeKeepalivePush(); - - var _url = UTF8ToString(url); - var _file = UTF8ToString(file); - _file = PATH_FS.resolve(_file); - function doCallback(callback) { - if (callback) { - runtimeKeepalivePop(); - callUserCallback(() => withStackSave(() => ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(stringToUTF8OnStack(_file)))); - } - } - var destinationDirectory = PATH.dirname(_file); - FS_preloadFile( - destinationDirectory, - PATH.basename(_file), - _url, true, true, - false, // dontCreateFile - false, // canOwn - () => { // preFinish - // if a file exists there, we overwrite it - try { - FS_unlink(_file); - } catch (e) {} - // if the destination directory does not yet exist, create it - FS_mkdirTree(destinationDirectory); - } - ).then(() => doCallback(onload)).catch(() => doCallback(onerror)); - }; - _emscripten_async_wget.sig = 'vpppp'; - - - - - - - - var _emscripten_async_wget_data = async (url, userdata, onload, onerror) => { - runtimeKeepalivePush(); - /* no need for run dependency, this is async but will not do any prepare etc. step */ - try { - var byteArray = await asyncLoad(UTF8ToString(url)); - runtimeKeepalivePop(); - callUserCallback(() => { - var buffer = _malloc(byteArray.length); - HEAPU8.set(byteArray, buffer); - ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(userdata, buffer, byteArray.length); - _free(buffer); - }); - } catch (e) { - if (onerror) { - runtimeKeepalivePop(); - callUserCallback(() => { - ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(userdata); - }); - } - } - }; - _emscripten_async_wget_data.sig = 'vpppp'; - - - - - - - - var _emscripten_async_wget2 = (url, file, request, param, userdata, onload, onerror, onprogress) => { - runtimeKeepalivePush(); - - var _url = UTF8ToString(url); - var _file = UTF8ToString(file); - _file = PATH_FS.resolve(_file); - var _request = UTF8ToString(request); - var _param = UTF8ToString(param); - var index = _file.lastIndexOf('/'); - - var http = new XMLHttpRequest(); - http.open(_request, _url, true); - http.responseType = 'arraybuffer'; - - var handle = wget.getNextWgetRequestHandle(); - - var destinationDirectory = PATH.dirname(_file); - - // LOAD - http.onload = (e) => { - runtimeKeepalivePop(); - if (http.status >= 200 && http.status < 300) { - // if a file exists there, we overwrite it - try { - FS.unlink(_file); - } catch (e) {} - // if the destination directory does not yet exist, create it - FS.mkdirTree(destinationDirectory); - - FS.createDataFile( _file.slice(0, index), _file.slice(index + 1), new Uint8Array(/** @type{ArrayBuffer}*/(http.response)), true, true, false); - if (onload) { - var sp = stackSave(); - ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, userdata, stringToUTF8OnStack(_file)); - stackRestore(sp); - } - } else { - if (onerror) ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, userdata, http.status); - } - - delete wget.wgetRequests[handle]; - }; - - // ERROR - http.onerror = (e) => { - runtimeKeepalivePop(); - if (onerror) ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, userdata, http.status); - delete wget.wgetRequests[handle]; - }; - - // PROGRESS - http.onprogress = (e) => { - if (e.lengthComputable || (e.lengthComputable === undefined && e.total != 0)) { - var percentComplete = (e.loaded / e.total)*100; - if (onprogress) ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, userdata, percentComplete); - } - }; - - // ABORT - http.onabort = (e) => { - runtimeKeepalivePop(); - delete wget.wgetRequests[handle]; - }; - - if (_request == "POST") { - //Send the proper header information along with the request - http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - http.send(_param); - } else { - http.send(null); - } - - wget.wgetRequests[handle] = http; - - return handle; - }; - _emscripten_async_wget2.sig = 'ipppppppp'; - - - - - - var _emscripten_async_wget2_data = (url, request, param, userdata, free, onload, onerror, onprogress) => { - var _url = UTF8ToString(url); - var _request = UTF8ToString(request); - var _param = UTF8ToString(param); - - var http = new XMLHttpRequest(); - http.open(_request, _url, true); - http.responseType = 'arraybuffer'; - - var handle = wget.getNextWgetRequestHandle(); - - function onerrorjs() { - if (onerror) { - var sp = stackSave(); - var statusText = 0; - if (http.statusText) { - statusText = stringToUTF8OnStack(http.statusText); - } - ((a1, a2, a3, a4) => {} /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, userdata, http.status, statusText); - stackRestore(sp); - } - } - - // LOAD - http.onload = (e) => { - if (http.status >= 200 && http.status < 300 || (http.status === 0 && _url.slice(0, 4).toLowerCase() != "http")) { - var byteArray = new Uint8Array(/** @type{ArrayBuffer} */(http.response)); - var buffer = _malloc(byteArray.length); - HEAPU8.set(byteArray, buffer); - if (onload) ((a1, a2, a3, a4) => {} /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, userdata, buffer, byteArray.length); - if (free) _free(buffer); - } else { - onerrorjs(); - } - delete wget.wgetRequests[handle]; - }; - - // ERROR - http.onerror = (e) => { - onerrorjs(); - delete wget.wgetRequests[handle]; - }; - - // PROGRESS - http.onprogress = (e) => { - if (onprogress) ((a1, a2, a3, a4) => {} /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(handle, userdata, e.loaded, e.lengthComputable || e.lengthComputable === undefined ? e.total : 0); - }; - - // ABORT - http.onabort = (e) => { - delete wget.wgetRequests[handle]; - }; - - if (_request == "POST") { - //Send the proper header information along with the request - http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - http.send(_param); - } else { - http.send(null); - } - - wget.wgetRequests[handle] = http; - - return handle; - }; - _emscripten_async_wget2_data.sig = 'ippppippp'; - - var _emscripten_async_wget2_abort = (handle) => { - var http = wget.wgetRequests[handle]; - http?.abort(); - }; - _emscripten_async_wget2_abort.sig = 'vi'; - - - - - - var ___asctime_r = (tmPtr, buf) => { - var date = { - tm_sec: HEAP32[((tmPtr)>>2)], - tm_min: HEAP32[(((tmPtr)+(4))>>2)], - tm_hour: HEAP32[(((tmPtr)+(8))>>2)], - tm_mday: HEAP32[(((tmPtr)+(12))>>2)], - tm_mon: HEAP32[(((tmPtr)+(16))>>2)], - tm_year: HEAP32[(((tmPtr)+(20))>>2)], - tm_wday: HEAP32[(((tmPtr)+(24))>>2)] - }; - var days = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]; - var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; - var s = days[date.tm_wday] + ' ' + months[date.tm_mon] + - (date.tm_mday < 10 ? ' ' : ' ') + date.tm_mday + - (date.tm_hour < 10 ? ' 0' : ' ') + date.tm_hour + - (date.tm_min < 10 ? ':0' : ':') + date.tm_min + - (date.tm_sec < 10 ? ':0' : ':') + date.tm_sec + - ' ' + (1900 + date.tm_year) + "\n"; - - // asctime_r is specced to behave in an undefined manner if the algorithm would attempt - // to write out more than 26 bytes (including the null terminator). - // See http://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime.html - // Our undefined behavior is to truncate the write to at most 26 bytes, including null terminator. - stringToUTF8(s, buf, 26); - return buf; - }; - ___asctime_r.sig = 'ppp'; - - - - - - - - - - - - var _strptime_l = (buf, format, tm, locale) => _strptime(buf, format, tm); - _strptime_l.sig = 'ppppp'; - - - - - - - - - - - - - - - - - - - - function ___syscall_shutdown(fd, how) { - try { - - getSocketFromFD(fd); - return -52; // unsupported feature - } catch (e) { - if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e; - return -e.errno; - } - } - ___syscall_shutdown.sig = 'iiiiiii'; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var __dlsym_catchup_js = (handle, symbolIndex) => { - var lib = LDSO.loadedLibsByHandle[handle]; - var symDict = lib.exports; - var symName = Object.keys(symDict)[symbolIndex]; - var sym = symDict[symName]; - var result = addFunction(sym, sym.sig); - return result; - }; - __dlsym_catchup_js.sig = 'ppi'; - - - - - - - - - - - - - - var FS_readFile = (...args) => FS.readFile(...args); - - - - var FS_root = (...args) => FS.root(...args); - - var FS_mounts = (...args) => FS.mounts(...args); - - var FS_devices = (...args) => FS.devices(...args); - - var FS_streams = (...args) => FS.streams(...args); - - var FS_nextInode = (...args) => FS.nextInode(...args); - - var FS_nameTable = (...args) => FS.nameTable(...args); - - var FS_currentPath = (...args) => FS.currentPath(...args); - - var FS_initialized = (...args) => FS.initialized(...args); - - var FS_ignorePermissions = (...args) => FS.ignorePermissions(...args); - - var FS_filesystems = (...args) => FS.filesystems(...args); - - var FS_syncFSRequests = (...args) => FS.syncFSRequests(...args); - - var FS_readFiles = (...args) => FS.readFiles(...args); - - var FS_lookupPath = (...args) => FS.lookupPath(...args); - - var FS_getPath = (...args) => FS.getPath(...args); - - var FS_hashName = (...args) => FS.hashName(...args); - - var FS_hashAddNode = (...args) => FS.hashAddNode(...args); - - var FS_hashRemoveNode = (...args) => FS.hashRemoveNode(...args); - - var FS_lookupNode = (...args) => FS.lookupNode(...args); - - var FS_createNode = (...args) => FS.createNode(...args); - - var FS_destroyNode = (...args) => FS.destroyNode(...args); - - var FS_isRoot = (...args) => FS.isRoot(...args); - - var FS_isMountpoint = (...args) => FS.isMountpoint(...args); - - var FS_isFile = (...args) => FS.isFile(...args); - - var FS_isDir = (...args) => FS.isDir(...args); - - var FS_isLink = (...args) => FS.isLink(...args); - - var FS_isChrdev = (...args) => FS.isChrdev(...args); - - var FS_isBlkdev = (...args) => FS.isBlkdev(...args); - - var FS_isFIFO = (...args) => FS.isFIFO(...args); - - var FS_isSocket = (...args) => FS.isSocket(...args); - - var FS_flagsToPermissionString = (...args) => FS.flagsToPermissionString(...args); - - var FS_nodePermissions = (...args) => FS.nodePermissions(...args); - - var FS_mayLookup = (...args) => FS.mayLookup(...args); - - var FS_mayCreate = (...args) => FS.mayCreate(...args); - - var FS_mayDelete = (...args) => FS.mayDelete(...args); - - var FS_mayOpen = (...args) => FS.mayOpen(...args); - - var FS_checkOpExists = (...args) => FS.checkOpExists(...args); - - var FS_nextfd = (...args) => FS.nextfd(...args); - - var FS_getStreamChecked = (...args) => FS.getStreamChecked(...args); - - var FS_getStream = (...args) => FS.getStream(...args); - - var FS_createStream = (...args) => FS.createStream(...args); - - var FS_closeStream = (...args) => FS.closeStream(...args); - - var FS_dupStream = (...args) => FS.dupStream(...args); - - var FS_doSetAttr = (...args) => FS.doSetAttr(...args); - - var FS_chrdev_stream_ops = (...args) => FS.chrdev_stream_ops(...args); - - var FS_major = (...args) => FS.major(...args); - - var FS_minor = (...args) => FS.minor(...args); - - var FS_makedev = (...args) => FS.makedev(...args); - - var FS_registerDevice = (...args) => FS.registerDevice(...args); - - var FS_getDevice = (...args) => FS.getDevice(...args); - - var FS_getMounts = (...args) => FS.getMounts(...args); - - var FS_syncfs = (...args) => FS.syncfs(...args); - - var FS_mount = (...args) => FS.mount(...args); - - var FS_unmount = (...args) => FS.unmount(...args); - - var FS_lookup = (...args) => FS.lookup(...args); - - var FS_mknod = (...args) => FS.mknod(...args); - - var FS_statfs = (...args) => FS.statfs(...args); - - var FS_statfsStream = (...args) => FS.statfsStream(...args); - - var FS_statfsNode = (...args) => FS.statfsNode(...args); - - var FS_create = (...args) => FS.create(...args); - - var FS_mkdir = (...args) => FS.mkdir(...args); - - var FS_mkdev = (...args) => FS.mkdev(...args); - - var FS_symlink = (...args) => FS.symlink(...args); - - var FS_rename = (...args) => FS.rename(...args); - - var FS_rmdir = (...args) => FS.rmdir(...args); - - var FS_readdir = (...args) => FS.readdir(...args); - - var FS_readlink = (...args) => FS.readlink(...args); - - var FS_stat = (...args) => FS.stat(...args); - - var FS_fstat = (...args) => FS.fstat(...args); - - var FS_lstat = (...args) => FS.lstat(...args); - - var FS_doChmod = (...args) => FS.doChmod(...args); - - var FS_chmod = (...args) => FS.chmod(...args); - - var FS_lchmod = (...args) => FS.lchmod(...args); - - var FS_fchmod = (...args) => FS.fchmod(...args); - - var FS_doChown = (...args) => FS.doChown(...args); - - var FS_chown = (...args) => FS.chown(...args); - - var FS_lchown = (...args) => FS.lchown(...args); - - var FS_fchown = (...args) => FS.fchown(...args); - - var FS_doTruncate = (...args) => FS.doTruncate(...args); - - var FS_truncate = (...args) => FS.truncate(...args); - - var FS_ftruncate = (...args) => FS.ftruncate(...args); - - var FS_utime = (...args) => FS.utime(...args); - - var FS_open = (...args) => FS.open(...args); - - var FS_close = (...args) => FS.close(...args); - - var FS_isClosed = (...args) => FS.isClosed(...args); - - var FS_llseek = (...args) => FS.llseek(...args); - - var FS_read = (...args) => FS.read(...args); - - var FS_write = (...args) => FS.write(...args); - - var FS_mmap = (...args) => FS.mmap(...args); - - var FS_msync = (...args) => FS.msync(...args); - - var FS_ioctl = (...args) => FS.ioctl(...args); - - var FS_writeFile = (...args) => FS.writeFile(...args); - - var FS_cwd = (...args) => FS.cwd(...args); - - var FS_chdir = (...args) => FS.chdir(...args); - - var FS_createDefaultDirectories = (...args) => FS.createDefaultDirectories(...args); - - var FS_createDefaultDevices = (...args) => FS.createDefaultDevices(...args); - - var FS_createSpecialDirectories = (...args) => FS.createSpecialDirectories(...args); - - var FS_createStandardStreams = (...args) => FS.createStandardStreams(...args); - - var FS_staticInit = (...args) => FS.staticInit(...args); - - var FS_init = (...args) => FS.init(...args); - - var FS_quit = (...args) => FS.quit(...args); - - var FS_findObject = (...args) => FS.findObject(...args); - - var FS_analyzePath = (...args) => FS.analyzePath(...args); - - var FS_createFile = (...args) => FS.createFile(...args); - - - var FS_forceLoadFile = (...args) => FS.forceLoadFile(...args); - - - - - - - - - - var _setNetworkCallback = (event, userData, callback) => { - function _callback(data) { - callUserCallback(() => { - if (event === 'error') { - withStackSave(() => { - var msg = stringToUTF8OnStack(data[2]); - ((a1, a2, a3, a4) => {} /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(data[0], data[1], msg, userData); - }); - } else { - ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(data, userData); - } - }); - }; - - // FIXME(sbc): This has no corresponding Pop so will currently keep the - // runtime alive indefinitely. - runtimeKeepalivePush(); - SOCKFS.on(event, callback ? _callback : null); - }; - - var _emscripten_set_socket_error_callback = (userData, callback) => - _setNetworkCallback('error', userData, callback); - _emscripten_set_socket_error_callback.sig = 'vpp'; - - var _emscripten_set_socket_open_callback = (userData, callback) => - _setNetworkCallback('open', userData, callback); - _emscripten_set_socket_open_callback.sig = 'vpp'; - - var _emscripten_set_socket_listen_callback = (userData, callback) => - _setNetworkCallback('listen', userData, callback); - _emscripten_set_socket_listen_callback.sig = 'vpp'; - - var _emscripten_set_socket_connection_callback = (userData, callback) => - _setNetworkCallback('connection', userData, callback); - _emscripten_set_socket_connection_callback.sig = 'vpp'; - - var _emscripten_set_socket_message_callback = (userData, callback) => - _setNetworkCallback('message', userData, callback); - _emscripten_set_socket_message_callback.sig = 'vpp'; - - var _emscripten_set_socket_close_callback = (userData, callback) => - _setNetworkCallback('close', userData, callback); - _emscripten_set_socket_close_callback.sig = 'vpp'; - - - - - - - - - var _emscripten_webgl_enable_ANGLE_instanced_arrays = (ctx) => webgl_enable_ANGLE_instanced_arrays(GL.contexts[ctx].GLctx); - _emscripten_webgl_enable_ANGLE_instanced_arrays.sig = 'ip'; - - - - var _emscripten_webgl_enable_OES_vertex_array_object = (ctx) => webgl_enable_OES_vertex_array_object(GL.contexts[ctx].GLctx); - _emscripten_webgl_enable_OES_vertex_array_object.sig = 'ip'; - - - - var _emscripten_webgl_enable_WEBGL_draw_buffers = (ctx) => webgl_enable_WEBGL_draw_buffers(GL.contexts[ctx].GLctx); - _emscripten_webgl_enable_WEBGL_draw_buffers.sig = 'ip'; - - - - var _emscripten_webgl_enable_WEBGL_multi_draw = (ctx) => webgl_enable_WEBGL_multi_draw(GL.contexts[ctx].GLctx); - _emscripten_webgl_enable_WEBGL_multi_draw.sig = 'ip'; - - - - var _emscripten_webgl_enable_EXT_polygon_offset_clamp = (ctx) => webgl_enable_EXT_polygon_offset_clamp(GL.contexts[ctx].GLctx); - _emscripten_webgl_enable_EXT_polygon_offset_clamp.sig = 'ip'; - - - - var _emscripten_webgl_enable_EXT_clip_control = (ctx) => webgl_enable_EXT_clip_control(GL.contexts[ctx].GLctx); - _emscripten_webgl_enable_EXT_clip_control.sig = 'ip'; - - - - var _emscripten_webgl_enable_WEBGL_polygon_mode = (ctx) => webgl_enable_WEBGL_polygon_mode(GL.contexts[ctx].GLctx); - _emscripten_webgl_enable_WEBGL_polygon_mode.sig = 'ip'; - - - - - - - - - - - - - - - - - var _glPixelStorei = _emscripten_glPixelStorei; - _glPixelStorei.sig = 'vii'; - - - var _glGetString = _emscripten_glGetString; - _glGetString.sig = 'pi'; - - - var _glGetIntegerv = _emscripten_glGetIntegerv; - _glGetIntegerv.sig = 'vip'; - - - var _glGetFloatv = _emscripten_glGetFloatv; - _glGetFloatv.sig = 'vip'; - - - var _glGetBooleanv = _emscripten_glGetBooleanv; - _glGetBooleanv.sig = 'vip'; - - - var _glDeleteTextures = _emscripten_glDeleteTextures; - _glDeleteTextures.sig = 'vip'; - - - var _glCompressedTexImage2D = _emscripten_glCompressedTexImage2D; - _glCompressedTexImage2D.sig = 'viiiiiiip'; - - - var _glCompressedTexSubImage2D = _emscripten_glCompressedTexSubImage2D; - _glCompressedTexSubImage2D.sig = 'viiiiiiiip'; - - - var _glTexImage2D = _emscripten_glTexImage2D; - _glTexImage2D.sig = 'viiiiiiiip'; - - - var _glTexSubImage2D = _emscripten_glTexSubImage2D; - _glTexSubImage2D.sig = 'viiiiiiiip'; - - - var _glReadPixels = _emscripten_glReadPixels; - _glReadPixels.sig = 'viiiiiip'; - - - var _glBindTexture = _emscripten_glBindTexture; - _glBindTexture.sig = 'vii'; - - - var _glGetTexParameterfv = _emscripten_glGetTexParameterfv; - _glGetTexParameterfv.sig = 'viip'; - - - var _glGetTexParameteriv = _emscripten_glGetTexParameteriv; - _glGetTexParameteriv.sig = 'viip'; - - - var _glTexParameterfv = _emscripten_glTexParameterfv; - _glTexParameterfv.sig = 'viip'; - - - var _glTexParameteriv = _emscripten_glTexParameteriv; - _glTexParameteriv.sig = 'viip'; - - - var _glIsTexture = _emscripten_glIsTexture; - _glIsTexture.sig = 'ii'; - - - var _glGenBuffers = _emscripten_glGenBuffers; - _glGenBuffers.sig = 'vip'; - - - var _glGenTextures = _emscripten_glGenTextures; - _glGenTextures.sig = 'vip'; - - - var _glDeleteBuffers = _emscripten_glDeleteBuffers; - _glDeleteBuffers.sig = 'vip'; - - - var _glGetBufferParameteriv = _emscripten_glGetBufferParameteriv; - _glGetBufferParameteriv.sig = 'viip'; - - - var _glBufferData = _emscripten_glBufferData; - _glBufferData.sig = 'vippi'; - - - var _glBufferSubData = _emscripten_glBufferSubData; - _glBufferSubData.sig = 'vippp'; - - - var _glGenQueriesEXT = _emscripten_glGenQueriesEXT; - - - var _glDeleteQueriesEXT = _emscripten_glDeleteQueriesEXT; - - - var _glIsQueryEXT = _emscripten_glIsQueryEXT; - - - var _glBeginQueryEXT = _emscripten_glBeginQueryEXT; - - - var _glEndQueryEXT = _emscripten_glEndQueryEXT; - - - var _glQueryCounterEXT = _emscripten_glQueryCounterEXT; - - - var _glGetQueryivEXT = _emscripten_glGetQueryivEXT; - - - var _glGetQueryObjectivEXT = _emscripten_glGetQueryObjectivEXT; - - - var _glGetQueryObjectuivEXT = _emscripten_glGetQueryObjectivEXT; - - - var _glGetQueryObjecti64vEXT = _emscripten_glGetQueryObjecti64vEXT; - - - var _glGetQueryObjectui64vEXT = _emscripten_glGetQueryObjecti64vEXT; - - - var _glIsBuffer = _emscripten_glIsBuffer; - _glIsBuffer.sig = 'ii'; - - - var _glGenRenderbuffers = _emscripten_glGenRenderbuffers; - _glGenRenderbuffers.sig = 'vip'; - - - var _glDeleteRenderbuffers = _emscripten_glDeleteRenderbuffers; - _glDeleteRenderbuffers.sig = 'vip'; - - - var _glBindRenderbuffer = _emscripten_glBindRenderbuffer; - _glBindRenderbuffer.sig = 'vii'; - - - var _glGetRenderbufferParameteriv = _emscripten_glGetRenderbufferParameteriv; - _glGetRenderbufferParameteriv.sig = 'viip'; - - - var _glIsRenderbuffer = _emscripten_glIsRenderbuffer; - _glIsRenderbuffer.sig = 'ii'; - - - var _glGetUniformfv = _emscripten_glGetUniformfv; - _glGetUniformfv.sig = 'viip'; - - - var _glGetUniformiv = _emscripten_glGetUniformiv; - _glGetUniformiv.sig = 'viip'; - - - var _glGetUniformLocation = _emscripten_glGetUniformLocation; - _glGetUniformLocation.sig = 'iip'; - - - var _glGetVertexAttribfv = _emscripten_glGetVertexAttribfv; - _glGetVertexAttribfv.sig = 'viip'; - - - var _glGetVertexAttribiv = _emscripten_glGetVertexAttribiv; - _glGetVertexAttribiv.sig = 'viip'; - - - var _glGetVertexAttribPointerv = _emscripten_glGetVertexAttribPointerv; - _glGetVertexAttribPointerv.sig = 'viip'; - - - var _glUniform1f = _emscripten_glUniform1f; - _glUniform1f.sig = 'vif'; - - - var _glUniform2f = _emscripten_glUniform2f; - _glUniform2f.sig = 'viff'; - - - var _glUniform3f = _emscripten_glUniform3f; - _glUniform3f.sig = 'vifff'; - - - var _glUniform4f = _emscripten_glUniform4f; - _glUniform4f.sig = 'viffff'; - - - var _glUniform1i = _emscripten_glUniform1i; - _glUniform1i.sig = 'vii'; - - - var _glUniform2i = _emscripten_glUniform2i; - _glUniform2i.sig = 'viii'; - - - var _glUniform3i = _emscripten_glUniform3i; - _glUniform3i.sig = 'viiii'; - - - var _glUniform4i = _emscripten_glUniform4i; - _glUniform4i.sig = 'viiiii'; - - - var _glUniform1iv = _emscripten_glUniform1iv; - _glUniform1iv.sig = 'viip'; - - - var _glUniform2iv = _emscripten_glUniform2iv; - _glUniform2iv.sig = 'viip'; - - - var _glUniform3iv = _emscripten_glUniform3iv; - _glUniform3iv.sig = 'viip'; - - - var _glUniform4iv = _emscripten_glUniform4iv; - _glUniform4iv.sig = 'viip'; - - - var _glUniform1fv = _emscripten_glUniform1fv; - _glUniform1fv.sig = 'viip'; - - - var _glUniform2fv = _emscripten_glUniform2fv; - _glUniform2fv.sig = 'viip'; - - - var _glUniform3fv = _emscripten_glUniform3fv; - _glUniform3fv.sig = 'viip'; - - - var _glUniform4fv = _emscripten_glUniform4fv; - _glUniform4fv.sig = 'viip'; - - - var _glUniformMatrix2fv = _emscripten_glUniformMatrix2fv; - _glUniformMatrix2fv.sig = 'viiip'; - - - var _glUniformMatrix3fv = _emscripten_glUniformMatrix3fv; - _glUniformMatrix3fv.sig = 'viiip'; - - - var _glUniformMatrix4fv = _emscripten_glUniformMatrix4fv; - _glUniformMatrix4fv.sig = 'viiip'; - - - var _glBindBuffer = _emscripten_glBindBuffer; - _glBindBuffer.sig = 'vii'; - - - var _glVertexAttrib1fv = _emscripten_glVertexAttrib1fv; - _glVertexAttrib1fv.sig = 'vip'; - - - var _glVertexAttrib2fv = _emscripten_glVertexAttrib2fv; - _glVertexAttrib2fv.sig = 'vip'; - - - var _glVertexAttrib3fv = _emscripten_glVertexAttrib3fv; - _glVertexAttrib3fv.sig = 'vip'; - - - var _glVertexAttrib4fv = _emscripten_glVertexAttrib4fv; - _glVertexAttrib4fv.sig = 'vip'; - - - var _glGetAttribLocation = _emscripten_glGetAttribLocation; - _glGetAttribLocation.sig = 'iip'; - - - var _glGetActiveAttrib = _emscripten_glGetActiveAttrib; - _glGetActiveAttrib.sig = 'viiipppp'; - - - var _glGetActiveUniform = _emscripten_glGetActiveUniform; - _glGetActiveUniform.sig = 'viiipppp'; - - - var _glCreateShader = _emscripten_glCreateShader; - _glCreateShader.sig = 'ii'; - - - var _glDeleteShader = _emscripten_glDeleteShader; - _glDeleteShader.sig = 'vi'; - - - var _glGetAttachedShaders = _emscripten_glGetAttachedShaders; - _glGetAttachedShaders.sig = 'viipp'; - - - var _glShaderSource = _emscripten_glShaderSource; - _glShaderSource.sig = 'viipp'; - - - var _glGetShaderSource = _emscripten_glGetShaderSource; - _glGetShaderSource.sig = 'viipp'; - - - var _glCompileShader = _emscripten_glCompileShader; - _glCompileShader.sig = 'vi'; - - - var _glGetShaderInfoLog = _emscripten_glGetShaderInfoLog; - _glGetShaderInfoLog.sig = 'viipp'; - - - var _glGetShaderiv = _emscripten_glGetShaderiv; - _glGetShaderiv.sig = 'viip'; - - - var _glGetProgramiv = _emscripten_glGetProgramiv; - _glGetProgramiv.sig = 'viip'; - - - var _glIsShader = _emscripten_glIsShader; - _glIsShader.sig = 'ii'; - - - var _glCreateProgram = _emscripten_glCreateProgram; - _glCreateProgram.sig = 'i'; - - - var _glDeleteProgram = _emscripten_glDeleteProgram; - _glDeleteProgram.sig = 'vi'; - - - var _glAttachShader = _emscripten_glAttachShader; - _glAttachShader.sig = 'vii'; - - - var _glDetachShader = _emscripten_glDetachShader; - _glDetachShader.sig = 'vii'; - - - var _glGetShaderPrecisionFormat = _emscripten_glGetShaderPrecisionFormat; - _glGetShaderPrecisionFormat.sig = 'viipp'; - - - var _glLinkProgram = _emscripten_glLinkProgram; - _glLinkProgram.sig = 'vi'; - - - var _glGetProgramInfoLog = _emscripten_glGetProgramInfoLog; - _glGetProgramInfoLog.sig = 'viipp'; - - - var _glUseProgram = _emscripten_glUseProgram; - _glUseProgram.sig = 'vi'; - - - var _glValidateProgram = _emscripten_glValidateProgram; - _glValidateProgram.sig = 'vi'; - - - var _glIsProgram = _emscripten_glIsProgram; - _glIsProgram.sig = 'ii'; - - - var _glBindAttribLocation = _emscripten_glBindAttribLocation; - _glBindAttribLocation.sig = 'viip'; - - - var _glBindFramebuffer = _emscripten_glBindFramebuffer; - _glBindFramebuffer.sig = 'vii'; - - - var _glGenFramebuffers = _emscripten_glGenFramebuffers; - _glGenFramebuffers.sig = 'vip'; - - - var _glDeleteFramebuffers = _emscripten_glDeleteFramebuffers; - _glDeleteFramebuffers.sig = 'vip'; - - - var _glFramebufferRenderbuffer = _emscripten_glFramebufferRenderbuffer; - _glFramebufferRenderbuffer.sig = 'viiii'; - - - var _glFramebufferTexture2D = _emscripten_glFramebufferTexture2D; - _glFramebufferTexture2D.sig = 'viiiii'; - - - var _glGetFramebufferAttachmentParameteriv = _emscripten_glGetFramebufferAttachmentParameteriv; - _glGetFramebufferAttachmentParameteriv.sig = 'viiip'; - - - var _glIsFramebuffer = _emscripten_glIsFramebuffer; - _glIsFramebuffer.sig = 'ii'; - - - var _glGenVertexArrays = _emscripten_glGenVertexArrays; - _glGenVertexArrays.sig = 'vip'; - - - var _glDeleteVertexArrays = _emscripten_glDeleteVertexArrays; - _glDeleteVertexArrays.sig = 'vip'; - - - var _glBindVertexArray = _emscripten_glBindVertexArray; - _glBindVertexArray.sig = 'vi'; - - - var _glIsVertexArray = _emscripten_glIsVertexArray; - _glIsVertexArray.sig = 'ii'; - - var _emscripten_glVertexPointer = (size, type, stride, ptr) => - abort('Legacy GL function (glVertexPointer) called. If you want legacy GL emulation, you need to compile with -sLEGACY_GL_EMULATION to enable legacy GL emulation.'); - _emscripten_glVertexPointer.sig = 'viiip'; - - var _glVertexPointer = _emscripten_glVertexPointer; - _glVertexPointer.sig = 'viiip'; - - var _emscripten_glMatrixMode = () => - abort('Legacy GL function (glMatrixMode) called. If you want legacy GL emulation, you need to compile with -sLEGACY_GL_EMULATION to enable legacy GL emulation.'); - _emscripten_glMatrixMode.sig = 'vi'; - - var _glMatrixMode = _emscripten_glMatrixMode; - _glMatrixMode.sig = 'vi'; - - var _emscripten_glBegin = () => - abort('Legacy GL function (glBegin) called. If you want legacy GL emulation, you need to compile with -sLEGACY_GL_EMULATION to enable legacy GL emulation.'); - _emscripten_glBegin.sig = 'vi'; - - var _glBegin = _emscripten_glBegin; - _glBegin.sig = 'vi'; - - var _emscripten_glLoadIdentity = () => - abort('Legacy GL function (glLoadIdentity) called. If you want legacy GL emulation, you need to compile with -sLEGACY_GL_EMULATION to enable legacy GL emulation.'); - _emscripten_glLoadIdentity.sig = 'v'; - - var _glLoadIdentity = _emscripten_glLoadIdentity; - _glLoadIdentity.sig = 'v'; - - - var _glGenVertexArraysOES = _emscripten_glGenVertexArrays; - _glGenVertexArraysOES.sig = 'vip'; - - - var _glDeleteVertexArraysOES = _emscripten_glDeleteVertexArrays; - _glDeleteVertexArraysOES.sig = 'vip'; - - - var _glBindVertexArrayOES = _emscripten_glBindVertexArray; - _glBindVertexArrayOES.sig = 'vi'; - - - var _glIsVertexArrayOES = _emscripten_glIsVertexArray; - _glIsVertexArrayOES.sig = 'ii'; - - - var _glVertexAttribPointer = _emscripten_glVertexAttribPointer; - _glVertexAttribPointer.sig = 'viiiiip'; - - - var _glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray; - _glEnableVertexAttribArray.sig = 'vi'; - - - var _glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray; - _glDisableVertexAttribArray.sig = 'vi'; - - - var _glDrawArrays = _emscripten_glDrawArrays; - _glDrawArrays.sig = 'viii'; - - - var _glDrawElements = _emscripten_glDrawElements; - _glDrawElements.sig = 'viiip'; - - - var _glShaderBinary = _emscripten_glShaderBinary; - _glShaderBinary.sig = 'vipipi'; - - - var _glReleaseShaderCompiler = _emscripten_glReleaseShaderCompiler; - _glReleaseShaderCompiler.sig = 'v'; - - - var _glGetError = _emscripten_glGetError; - _glGetError.sig = 'i'; - - - var _glVertexAttribDivisor = _emscripten_glVertexAttribDivisor; - _glVertexAttribDivisor.sig = 'vii'; - - - var _glDrawArraysInstanced = _emscripten_glDrawArraysInstanced; - _glDrawArraysInstanced.sig = 'viiii'; - - - var _glDrawElementsInstanced = _emscripten_glDrawElementsInstanced; - _glDrawElementsInstanced.sig = 'viiipi'; - - - var _emscripten_glVertexAttribDivisorNV = _emscripten_glVertexAttribDivisor; - - var _glVertexAttribDivisorNV = _emscripten_glVertexAttribDivisor; - - - var _emscripten_glDrawArraysInstancedNV = _emscripten_glDrawArraysInstanced; - - var _glDrawArraysInstancedNV = _emscripten_glDrawArraysInstanced; - - - var _emscripten_glDrawElementsInstancedNV = _emscripten_glDrawElementsInstanced; - - var _glDrawElementsInstancedNV = _emscripten_glDrawElementsInstanced; - - - var _emscripten_glVertexAttribDivisorEXT = _emscripten_glVertexAttribDivisor; - - var _glVertexAttribDivisorEXT = _emscripten_glVertexAttribDivisor; - - - var _emscripten_glDrawArraysInstancedEXT = _emscripten_glDrawArraysInstanced; - - var _glDrawArraysInstancedEXT = _emscripten_glDrawArraysInstanced; - - - var _emscripten_glDrawElementsInstancedEXT = _emscripten_glDrawElementsInstanced; - - var _glDrawElementsInstancedEXT = _emscripten_glDrawElementsInstanced; - - - var _emscripten_glVertexAttribDivisorARB = _emscripten_glVertexAttribDivisor; - - var _glVertexAttribDivisorARB = _emscripten_glVertexAttribDivisor; - - - var _emscripten_glDrawArraysInstancedARB = _emscripten_glDrawArraysInstanced; - - var _glDrawArraysInstancedARB = _emscripten_glDrawArraysInstanced; - - - var _emscripten_glDrawElementsInstancedARB = _emscripten_glDrawElementsInstanced; - - var _glDrawElementsInstancedARB = _emscripten_glDrawElementsInstanced; - - - var _glVertexAttribDivisorANGLE = _emscripten_glVertexAttribDivisor; - - - var _glDrawArraysInstancedANGLE = _emscripten_glDrawArraysInstanced; - - - var _glDrawElementsInstancedANGLE = _emscripten_glDrawElementsInstanced; - - - var _glDrawBuffers = _emscripten_glDrawBuffers; - _glDrawBuffers.sig = 'vip'; - - - var _emscripten_glDrawBuffersEXT = _emscripten_glDrawBuffers; - - var _glDrawBuffersEXT = _emscripten_glDrawBuffers; - - - var _glDrawBuffersWEBGL = _emscripten_glDrawBuffers; - - - var _glColorMask = _emscripten_glColorMask; - _glColorMask.sig = 'viiii'; - - - var _glDepthMask = _emscripten_glDepthMask; - _glDepthMask.sig = 'vi'; - - - var _glSampleCoverage = _emscripten_glSampleCoverage; - _glSampleCoverage.sig = 'vfi'; - - - var _emscripten_glMultiDrawArraysWEBGL = (mode, firsts, counts, drawcount) => { - GLctx.multiDrawWebgl['multiDrawArraysWEBGL']( - mode, - HEAP32, - ((firsts)>>2), - HEAP32, - ((counts)>>2), - drawcount); - }; - _emscripten_glMultiDrawArraysWEBGL.sig = 'vippi'; - var _emscripten_glMultiDrawArrays = _emscripten_glMultiDrawArraysWEBGL; - _emscripten_glMultiDrawArrays.sig = 'vippi'; - - var _glMultiDrawArrays = _emscripten_glMultiDrawArraysWEBGL; - _glMultiDrawArrays.sig = 'vippi'; - - - var _emscripten_glMultiDrawArraysANGLE = _emscripten_glMultiDrawArraysWEBGL; - - var _glMultiDrawArraysANGLE = _emscripten_glMultiDrawArraysWEBGL; - - - var _glMultiDrawArraysWEBGL = _emscripten_glMultiDrawArraysWEBGL; - - - var _emscripten_glMultiDrawArraysInstancedWEBGL = (mode, firsts, counts, instanceCounts, drawcount) => { - GLctx.multiDrawWebgl['multiDrawArraysInstancedWEBGL']( - mode, - HEAP32, - ((firsts)>>2), - HEAP32, - ((counts)>>2), - HEAP32, - ((instanceCounts)>>2), - drawcount); - }; - _emscripten_glMultiDrawArraysInstancedWEBGL.sig = 'vipppi'; - var _emscripten_glMultiDrawArraysInstancedANGLE = _emscripten_glMultiDrawArraysInstancedWEBGL; - - var _glMultiDrawArraysInstancedANGLE = _emscripten_glMultiDrawArraysInstancedWEBGL; - - - var _glMultiDrawArraysInstancedWEBGL = _emscripten_glMultiDrawArraysInstancedWEBGL; - - - var _emscripten_glMultiDrawElementsWEBGL = (mode, counts, type, offsets, drawcount) => { - GLctx.multiDrawWebgl['multiDrawElementsWEBGL']( - mode, - HEAP32, - ((counts)>>2), - type, - HEAP32, - ((offsets)>>2), - drawcount); - }; - _emscripten_glMultiDrawElementsWEBGL.sig = 'vipipi'; - var _emscripten_glMultiDrawElements = _emscripten_glMultiDrawElementsWEBGL; - _emscripten_glMultiDrawElements.sig = 'vipipi'; - - var _glMultiDrawElements = _emscripten_glMultiDrawElementsWEBGL; - _glMultiDrawElements.sig = 'vipipi'; - - - var _emscripten_glMultiDrawElementsANGLE = _emscripten_glMultiDrawElementsWEBGL; - - var _glMultiDrawElementsANGLE = _emscripten_glMultiDrawElementsWEBGL; - - - var _glMultiDrawElementsWEBGL = _emscripten_glMultiDrawElementsWEBGL; - - - var _emscripten_glMultiDrawElementsInstancedWEBGL = (mode, counts, type, offsets, instanceCounts, drawcount) => { - GLctx.multiDrawWebgl['multiDrawElementsInstancedWEBGL']( - mode, - HEAP32, - ((counts)>>2), - type, - HEAP32, - ((offsets)>>2), - HEAP32, - ((instanceCounts)>>2), - drawcount); - }; - _emscripten_glMultiDrawElementsInstancedWEBGL.sig = 'vipippi'; - var _emscripten_glMultiDrawElementsInstancedANGLE = _emscripten_glMultiDrawElementsInstancedWEBGL; - - var _glMultiDrawElementsInstancedANGLE = _emscripten_glMultiDrawElementsInstancedWEBGL; - - - var _glMultiDrawElementsInstancedWEBGL = _emscripten_glMultiDrawElementsInstancedWEBGL; - - - var _glPolygonOffsetClampEXT = _emscripten_glPolygonOffsetClampEXT; - - - var _glClipControlEXT = _emscripten_glClipControlEXT; - - - var _glPolygonModeWEBGL = _emscripten_glPolygonModeWEBGL; - - - var _glFinish = _emscripten_glFinish; - _glFinish.sig = 'v'; - - - var _glFlush = _emscripten_glFlush; - _glFlush.sig = 'v'; - - var _emscripten_glClearDepth = (x0) => GLctx.clearDepth(x0); - _emscripten_glClearDepth.sig = 'vd'; - - var _glClearDepth = _emscripten_glClearDepth; - _glClearDepth.sig = 'vd'; - - - var _glClearDepthf = _emscripten_glClearDepthf; - _glClearDepthf.sig = 'vf'; - - - var _glDepthFunc = _emscripten_glDepthFunc; - _glDepthFunc.sig = 'vi'; - - - var _glEnable = _emscripten_glEnable; - _glEnable.sig = 'vi'; - - - var _glDisable = _emscripten_glDisable; - _glDisable.sig = 'vi'; - - - var _glFrontFace = _emscripten_glFrontFace; - _glFrontFace.sig = 'vi'; - - - var _glCullFace = _emscripten_glCullFace; - _glCullFace.sig = 'vi'; - - - var _glClear = _emscripten_glClear; - _glClear.sig = 'vi'; - - - var _glLineWidth = _emscripten_glLineWidth; - _glLineWidth.sig = 'vf'; - - - var _glClearStencil = _emscripten_glClearStencil; - _glClearStencil.sig = 'vi'; - - - var _glStencilMask = _emscripten_glStencilMask; - _glStencilMask.sig = 'vi'; - - - var _glCheckFramebufferStatus = _emscripten_glCheckFramebufferStatus; - _glCheckFramebufferStatus.sig = 'ii'; - - - var _glGenerateMipmap = _emscripten_glGenerateMipmap; - _glGenerateMipmap.sig = 'vi'; - - - var _glActiveTexture = _emscripten_glActiveTexture; - _glActiveTexture.sig = 'vi'; - - - var _glBlendEquation = _emscripten_glBlendEquation; - _glBlendEquation.sig = 'vi'; - - - var _glIsEnabled = _emscripten_glIsEnabled; - _glIsEnabled.sig = 'ii'; - - - var _glBlendFunc = _emscripten_glBlendFunc; - _glBlendFunc.sig = 'vii'; - - - var _glBlendEquationSeparate = _emscripten_glBlendEquationSeparate; - _glBlendEquationSeparate.sig = 'vii'; - - var _emscripten_glDepthRange = (x0, x1) => GLctx.depthRange(x0, x1); - _emscripten_glDepthRange.sig = 'vdd'; - - var _glDepthRange = _emscripten_glDepthRange; - _glDepthRange.sig = 'vdd'; - - - var _glDepthRangef = _emscripten_glDepthRangef; - _glDepthRangef.sig = 'vff'; - - - var _glStencilMaskSeparate = _emscripten_glStencilMaskSeparate; - _glStencilMaskSeparate.sig = 'vii'; - - - var _glHint = _emscripten_glHint; - _glHint.sig = 'vii'; - - - var _glPolygonOffset = _emscripten_glPolygonOffset; - _glPolygonOffset.sig = 'vff'; - - - var _glVertexAttrib1f = _emscripten_glVertexAttrib1f; - _glVertexAttrib1f.sig = 'vif'; - - - var _glTexParameteri = _emscripten_glTexParameteri; - _glTexParameteri.sig = 'viii'; - - - var _glTexParameterf = _emscripten_glTexParameterf; - _glTexParameterf.sig = 'viif'; - - - var _glVertexAttrib2f = _emscripten_glVertexAttrib2f; - _glVertexAttrib2f.sig = 'viff'; - - - var _glStencilFunc = _emscripten_glStencilFunc; - _glStencilFunc.sig = 'viii'; - - - var _glStencilOp = _emscripten_glStencilOp; - _glStencilOp.sig = 'viii'; - - - var _glViewport = _emscripten_glViewport; - _glViewport.sig = 'viiii'; - - - var _glClearColor = _emscripten_glClearColor; - _glClearColor.sig = 'vffff'; - - - var _glScissor = _emscripten_glScissor; - _glScissor.sig = 'viiii'; - - - var _glVertexAttrib3f = _emscripten_glVertexAttrib3f; - _glVertexAttrib3f.sig = 'vifff'; - - - var _glRenderbufferStorage = _emscripten_glRenderbufferStorage; - _glRenderbufferStorage.sig = 'viiii'; - - - var _glBlendFuncSeparate = _emscripten_glBlendFuncSeparate; - _glBlendFuncSeparate.sig = 'viiii'; - - - var _glBlendColor = _emscripten_glBlendColor; - _glBlendColor.sig = 'vffff'; - - - var _glStencilFuncSeparate = _emscripten_glStencilFuncSeparate; - _glStencilFuncSeparate.sig = 'viiii'; - - - var _glStencilOpSeparate = _emscripten_glStencilOpSeparate; - _glStencilOpSeparate.sig = 'viiii'; - - - var _glVertexAttrib4f = _emscripten_glVertexAttrib4f; - _glVertexAttrib4f.sig = 'viffff'; - - - var _glCopyTexImage2D = _emscripten_glCopyTexImage2D; - _glCopyTexImage2D.sig = 'viiiiiiii'; - - - var _glCopyTexSubImage2D = _emscripten_glCopyTexSubImage2D; - _glCopyTexSubImage2D.sig = 'viiiiiiii'; - - var writeGLArray = (arr, dst, dstLength, heapType) => { - var len = arr.length; - var writeLength = dstLength < len ? dstLength : len; - var heap = heapType ? HEAPF32 : HEAP32; - // Works because HEAPF32 and HEAP32 have the same bytes-per-element - dst = ((dst)>>2); - for (var i = 0; i < writeLength; ++i) { - heap[dst + i] = arr[i]; - } - return len; - }; - - var webglPowerPreferences = ["default","low-power","high-performance"]; - - - - - var _emscripten_webgl_do_create_context = (target, attributes) => { - var attr32 = ((attributes)>>2); - var powerPreference = HEAP32[attr32 + (8>>2)]; - var contextAttributes = { - 'alpha': !!HEAP8[attributes + 0], - 'depth': !!HEAP8[attributes + 1], - 'stencil': !!HEAP8[attributes + 2], - 'antialias': !!HEAP8[attributes + 3], - 'premultipliedAlpha': !!HEAP8[attributes + 4], - 'preserveDrawingBuffer': !!HEAP8[attributes + 5], - 'powerPreference': webglPowerPreferences[powerPreference], - 'failIfMajorPerformanceCaveat': !!HEAP8[attributes + 12], - // The following are not predefined WebGL context attributes in the WebGL specification, so the property names can be minified by Closure. - majorVersion: HEAP32[attr32 + (16>>2)], - minorVersion: HEAP32[attr32 + (20>>2)], - enableExtensionsByDefault: HEAP8[attributes + 24], - explicitSwapControl: HEAP8[attributes + 25], - proxyContextToMainThread: HEAP32[attr32 + (28>>2)], - renderViaOffscreenBackBuffer: HEAP8[attributes + 32] - }; - - var canvas = findCanvasEventTarget(target); - - if (!canvas) { - return 0; - } - - if (contextAttributes.explicitSwapControl) { - return 0; - } - - var contextHandle = GL.createContext(canvas, contextAttributes); - return contextHandle; - }; - _emscripten_webgl_do_create_context.sig = 'ppp'; - var _emscripten_webgl_create_context = _emscripten_webgl_do_create_context; - _emscripten_webgl_create_context.sig = 'ppp'; - - - var _emscripten_webgl_do_get_current_context = () => GL.currentContext ? GL.currentContext.handle : 0; - _emscripten_webgl_do_get_current_context.sig = 'p'; - var _emscripten_webgl_get_current_context = _emscripten_webgl_do_get_current_context; - _emscripten_webgl_get_current_context.sig = 'p'; - - - var _emscripten_webgl_do_commit_frame = () => { - if (!GL.currentContext || !GL.currentContext.GLctx) { - return -3; - } - - if (!GL.currentContext.attributes.explicitSwapControl) { - return -3; - } - // We would do GL.currentContext.GLctx.commit(); here, but the current implementation - // in browsers has removed it - swap is implicit, so this function is a no-op for now - // (until/unless the spec changes). - return 0; - }; - _emscripten_webgl_do_commit_frame.sig = 'i'; - var _emscripten_webgl_commit_frame = _emscripten_webgl_do_commit_frame; - _emscripten_webgl_commit_frame.sig = 'i'; - - - var _emscripten_webgl_make_context_current = (contextHandle) => { - var success = GL.makeContextCurrent(contextHandle); - return success ? 0 : -5; - }; - _emscripten_webgl_make_context_current.sig = 'ip'; - - - var _emscripten_webgl_get_drawing_buffer_size = (contextHandle, width, height) => { - var GLContext = GL.getContext(contextHandle); - - if (!GLContext || !GLContext.GLctx || !width || !height) { - return -5; - } - HEAP32[((width)>>2)] = GLContext.GLctx.drawingBufferWidth; - HEAP32[((height)>>2)] = GLContext.GLctx.drawingBufferHeight; - return 0; - }; - _emscripten_webgl_get_drawing_buffer_size.sig = 'ippp'; - - - - var _emscripten_webgl_get_context_attributes = (c, a) => { - if (!a) return -5; - c = GL.contexts[c]; - if (!c) return -3; - var t = c.GLctx?.getContextAttributes(); - if (!t) return -3; - - HEAP8[a] = t.alpha; - HEAP8[(a)+(1)] = t.depth; - HEAP8[(a)+(2)] = t.stencil; - HEAP8[(a)+(3)] = t.antialias; - HEAP8[(a)+(4)] = t.premultipliedAlpha; - HEAP8[(a)+(5)] = t.preserveDrawingBuffer; - var power = t['powerPreference'] && webglPowerPreferences.indexOf(t['powerPreference']); - HEAP32[(((a)+(8))>>2)] = power; - HEAP8[(a)+(12)] = t.failIfMajorPerformanceCaveat; - HEAP32[(((a)+(16))>>2)] = c.version; - HEAP32[(((a)+(20))>>2)] = 0; - HEAP8[(a)+(24)] = c.attributes.enableExtensionsByDefault; - return 0; - }; - _emscripten_webgl_get_context_attributes.sig = 'ipp'; - - var _emscripten_webgl_destroy_context = (contextHandle) => { - if (GL.currentContext == contextHandle) GL.currentContext = 0; - GL.deleteContext(contextHandle); - }; - _emscripten_webgl_destroy_context.sig = 'ip'; - - - - - - - - - - var _emscripten_webgl_enable_extension = (contextHandle, extension) => { - var context = GL.getContext(contextHandle); - var extString = UTF8ToString(extension); - if (extString.startsWith('GL_')) extString = extString.slice(3); // Allow enabling extensions both with "GL_" prefix and without. - - // Switch-board that pulls in code for all GL extensions, even if those are not used :/ - // Build with -sGL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS=0 to avoid this. - - // Obtain function entry points to WebGL 1 extension related functions. - if (extString == 'ANGLE_instanced_arrays') webgl_enable_ANGLE_instanced_arrays(GLctx); - if (extString == 'OES_vertex_array_object') webgl_enable_OES_vertex_array_object(GLctx); - if (extString == 'WEBGL_draw_buffers') webgl_enable_WEBGL_draw_buffers(GLctx); - - if (extString == 'WEBGL_multi_draw') webgl_enable_WEBGL_multi_draw(GLctx); - if (extString == 'EXT_polygon_offset_clamp') webgl_enable_EXT_polygon_offset_clamp(GLctx); - if (extString == 'EXT_clip_control') webgl_enable_EXT_clip_control(GLctx); - if (extString == 'WEBGL_polygon_mode') webgl_enable_WEBGL_polygon_mode(GLctx); - - var ext = context.GLctx.getExtension(extString); - return !!ext; - }; - _emscripten_webgl_enable_extension.sig = 'ipp'; - - 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. - 0; - _emscripten_supports_offscreencanvas.sig = 'i'; - - - - var registerWebGlEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { - - var webGlEventHandlerFunc = (e = event) => { - if (((a1, a2, a3) => {} /* a dynamic function call to signature iiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(eventTypeId, 0, userData)) e.preventDefault(); - }; - - var eventHandler = { - target: findEventTarget(target), - eventTypeString, - callbackfunc, - handlerFunc: webGlEventHandlerFunc, - useCapture - }; - JSEvents.registerOrRemoveHandler(eventHandler); - }; - - - var _emscripten_set_webglcontextlost_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => { - registerWebGlEventCallback(target, userData, useCapture, callbackfunc, 31, "webglcontextlost", targetThread); - return 0; - }; - _emscripten_set_webglcontextlost_callback_on_thread.sig = 'ippipp'; - - - var _emscripten_set_webglcontextrestored_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => { - registerWebGlEventCallback(target, userData, useCapture, callbackfunc, 32, "webglcontextrestored", targetThread); - return 0; - }; - _emscripten_set_webglcontextrestored_callback_on_thread.sig = 'ippipp'; - - var _emscripten_is_webgl_context_lost = (contextHandle) => - !GL.contexts[contextHandle] || GL.contexts[contextHandle].GLctx.isContextLost(); - _emscripten_is_webgl_context_lost.sig = 'ip'; - - - var _emscripten_webgl_get_supported_extensions = () => - stringToNewUTF8(GLctx.getSupportedExtensions().join(' ')); - _emscripten_webgl_get_supported_extensions.sig = 'p'; - - var _emscripten_webgl_get_program_parameter_d = (program, param) => - GLctx.getProgramParameter(GL.programs[program], param); - _emscripten_webgl_get_program_parameter_d.sig = 'dii'; - - - var _emscripten_webgl_get_program_info_log_utf8 = (program) => - stringToNewUTF8(GLctx.getProgramInfoLog(GL.programs[program])); - _emscripten_webgl_get_program_info_log_utf8.sig = 'pi'; - - var _emscripten_webgl_get_shader_parameter_d = (shader, param) => - GLctx.getShaderParameter(GL.shaders[shader], param); - _emscripten_webgl_get_shader_parameter_d.sig = 'dii'; - - - var _emscripten_webgl_get_shader_info_log_utf8 = (shader) => - stringToNewUTF8(GLctx.getShaderInfoLog(GL.shaders[shader])); - _emscripten_webgl_get_shader_info_log_utf8.sig = 'pi'; - - - var _emscripten_webgl_get_shader_source_utf8 = (shader) => - stringToNewUTF8(GLctx.getShaderSource(GL.shaders[shader])); - _emscripten_webgl_get_shader_source_utf8.sig = 'pi'; - - var _emscripten_webgl_get_vertex_attrib_d = (index, param) => - GLctx.getVertexAttrib(index, param); - _emscripten_webgl_get_vertex_attrib_d.sig = 'dii'; - - var _emscripten_webgl_get_vertex_attrib_o = (index, param) => { - var obj = GLctx.getVertexAttrib(index, param); - return obj?.name; - }; - _emscripten_webgl_get_vertex_attrib_o.sig = 'iii'; - - - var _emscripten_webgl_get_vertex_attrib_v = (index, param, dst, dstLength, dstType) => - writeGLArray(GLctx.getVertexAttrib(index, param), dst, dstLength, dstType); - _emscripten_webgl_get_vertex_attrib_v.sig = 'iiipii'; - - - var _emscripten_webgl_get_uniform_d = (program, location) => - GLctx.getUniform(GL.programs[program], webglGetUniformLocation(location)); - _emscripten_webgl_get_uniform_d.sig = 'dii'; - - - - var _emscripten_webgl_get_uniform_v = (program, location, dst, dstLength, dstType) => - writeGLArray(GLctx.getUniform(GL.programs[program], webglGetUniformLocation(location)), dst, dstLength, dstType); - _emscripten_webgl_get_uniform_v.sig = 'iiipii'; - - - var _emscripten_webgl_get_parameter_v = (param, dst, dstLength, dstType) => - writeGLArray(GLctx.getParameter(param), dst, dstLength, dstType); - _emscripten_webgl_get_parameter_v.sig = 'iipii'; - - var _emscripten_webgl_get_parameter_d = (param) => GLctx.getParameter(param); - _emscripten_webgl_get_parameter_d.sig = 'di'; - - var _emscripten_webgl_get_parameter_o = (param) => { - var obj = GLctx.getParameter(param); - return obj?.name; - }; - _emscripten_webgl_get_parameter_o.sig = 'ii'; - - - var _emscripten_webgl_get_parameter_utf8 = (param) => stringToNewUTF8(GLctx.getParameter(param)); - _emscripten_webgl_get_parameter_utf8.sig = 'pi'; - - - var _emscripten_webgl_get_parameter_i64v = (param, dst) => writeI53ToI64(dst, GLctx.getParameter(param)); - _emscripten_webgl_get_parameter_i64v.sig = 'vip'; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var _glutPostRedisplay = () => { - if (GLUT.displayFunc && !GLUT.requestedAnimationFrame) { - GLUT.requestedAnimationFrame = true; - MainLoop.requestAnimationFrame(() => { - GLUT.requestedAnimationFrame = false; - MainLoop.runIter(() => (() => {} /* a dynamic function call to signature v, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)()); - }); - } - }; - _glutPostRedisplay.sig = 'v'; - - var GLUT = { - initTime:null, - idleFunc:null, - displayFunc:null, - keyboardFunc:null, - keyboardUpFunc:null, - specialFunc:null, - specialUpFunc:null, - reshapeFunc:null, - motionFunc:null, - passiveMotionFunc:null, - mouseFunc:null, - buttons:0, - modifiers:0, - initWindowWidth:256, - initWindowHeight:256, - initDisplayMode:18, - windowX:0, - windowY:0, - windowWidth:0, - windowHeight:0, - requestedAnimationFrame:false, - saveModifiers:(event) => { - GLUT.modifiers = 0; - if (event['shiftKey']) - GLUT.modifiers += 1; /* GLUT_ACTIVE_SHIFT */ - if (event['ctrlKey']) - GLUT.modifiers += 2; /* GLUT_ACTIVE_CTRL */ - if (event['altKey']) - GLUT.modifiers += 4; /* GLUT_ACTIVE_ALT */ - }, - onMousemove:(event) => { - /* Send motion event only if the motion changed, prevents - * spamming our app with uncessary callback call. It does happen in - * Chrome on Windows. - */ - var lastX = Browser.mouseX; - var lastY = Browser.mouseY; - Browser.calculateMouseEvent(event); - var newX = Browser.mouseX; - var newY = Browser.mouseY; - if (newX == lastX && newY == lastY) return; - - if (GLUT.buttons == 0 && event.target == Browser.getCanvas() && GLUT.passiveMotionFunc) { - event.preventDefault(); - GLUT.saveModifiers(event); - ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(lastX, lastY); - } else if (GLUT.buttons != 0 && GLUT.motionFunc) { - event.preventDefault(); - GLUT.saveModifiers(event); - ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(lastX, lastY); - } - }, - getSpecialKey:(keycode) => { - var key = null; - switch (keycode) { - case 8: key = 120 /* backspace */; break; - case 46: key = 111 /* delete */; break; - - case 0x70 /*DOM_VK_F1*/: key = 1 /* GLUT_KEY_F1 */; break; - case 0x71 /*DOM_VK_F2*/: key = 2 /* GLUT_KEY_F2 */; break; - case 0x72 /*DOM_VK_F3*/: key = 3 /* GLUT_KEY_F3 */; break; - case 0x73 /*DOM_VK_F4*/: key = 4 /* GLUT_KEY_F4 */; break; - case 0x74 /*DOM_VK_F5*/: key = 5 /* GLUT_KEY_F5 */; break; - case 0x75 /*DOM_VK_F6*/: key = 6 /* GLUT_KEY_F6 */; break; - case 0x76 /*DOM_VK_F7*/: key = 7 /* GLUT_KEY_F7 */; break; - case 0x77 /*DOM_VK_F8*/: key = 8 /* GLUT_KEY_F8 */; break; - case 0x78 /*DOM_VK_F9*/: key = 9 /* GLUT_KEY_F9 */; break; - case 0x79 /*DOM_VK_F10*/: key = 10 /* GLUT_KEY_F10 */; break; - case 0x7a /*DOM_VK_F11*/: key = 11 /* GLUT_KEY_F11 */; break; - case 0x7b /*DOM_VK_F12*/: key = 12 /* GLUT_KEY_F12 */; break; - case 0x25 /*DOM_VK_LEFT*/: key = 100 /* GLUT_KEY_LEFT */; break; - case 0x26 /*DOM_VK_UP*/: key = 101 /* GLUT_KEY_UP */; break; - case 0x27 /*DOM_VK_RIGHT*/: key = 102 /* GLUT_KEY_RIGHT */; break; - case 0x28 /*DOM_VK_DOWN*/: key = 103 /* GLUT_KEY_DOWN */; break; - case 0x21 /*DOM_VK_PAGE_UP*/: key = 104 /* GLUT_KEY_PAGE_UP */; break; - case 0x22 /*DOM_VK_PAGE_DOWN*/: key = 105 /* GLUT_KEY_PAGE_DOWN */; break; - case 0x24 /*DOM_VK_HOME*/: key = 106 /* GLUT_KEY_HOME */; break; - case 0x23 /*DOM_VK_END*/: key = 107 /* GLUT_KEY_END */; break; - case 0x2d /*DOM_VK_INSERT*/: key = 108 /* GLUT_KEY_INSERT */; break; - - case 16 /*DOM_VK_SHIFT*/: - case 0x05 /*DOM_VK_LEFT_SHIFT*/: - key = 112 /* GLUT_KEY_SHIFT_L */; - break; - case 0x06 /*DOM_VK_RIGHT_SHIFT*/: - key = 113 /* GLUT_KEY_SHIFT_R */; - break; - - case 17 /*DOM_VK_CONTROL*/: - case 0x03 /*DOM_VK_LEFT_CONTROL*/: - key = 114 /* GLUT_KEY_CONTROL_L */; - break; - case 0x04 /*DOM_VK_RIGHT_CONTROL*/: - key = 115 /* GLUT_KEY_CONTROL_R */; - break; - - case 18 /*DOM_VK_ALT*/: - case 0x02 /*DOM_VK_LEFT_ALT*/: - key = 116 /* GLUT_KEY_ALT_L */; - break; - case 0x01 /*DOM_VK_RIGHT_ALT*/: - key = 117 /* GLUT_KEY_ALT_R */; - break; - }; - return key; - }, - getASCIIKey:(event) => { - if (event['ctrlKey'] || event['altKey'] || event['metaKey']) return null; - - var keycode = event['keyCode']; - - /* The exact list is soooo hard to find in a canonical place! */ - - if (48 <= keycode && keycode <= 57) - return keycode; // numeric TODO handle shift? - if (65 <= keycode && keycode <= 90) - return event['shiftKey'] ? keycode : keycode + 32; - if (96 <= keycode && keycode <= 105) - return keycode - 48; // numpad numbers - if (106 <= keycode && keycode <= 111) - return keycode - 106 + 42; // *,+-./ TODO handle shift? - - switch (keycode) { - case 9: // tab key - case 13: // return key - case 27: // escape - case 32: // space - case 61: // equal - return keycode; - } - - var s = event['shiftKey']; - switch (keycode) { - case 186: return s ? 58 : 59; // colon / semi-colon - case 187: return s ? 43 : 61; // add / equal (these two may be wrong) - case 188: return s ? 60 : 44; // less-than / comma - case 189: return s ? 95 : 45; // dash - case 190: return s ? 62 : 46; // greater-than / period - case 191: return s ? 63 : 47; // forward slash - case 219: return s ? 123 : 91; // open bracket - case 220: return s ? 124 : 47; // back slash - case 221: return s ? 125 : 93; // close bracket - case 222: return s ? 34 : 39; // single quote - } - - return null; - }, - onKeydown:(event) => { - if (GLUT.specialFunc || GLUT.keyboardFunc) { - var key = GLUT.getSpecialKey(event['keyCode']); - if (key !== null) { - if (GLUT.specialFunc) { - event.preventDefault(); - GLUT.saveModifiers(event); - ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(key, Browser.mouseX, Browser.mouseY); - } - } else { - key = GLUT.getASCIIKey(event); - if (key !== null && GLUT.keyboardFunc) { - event.preventDefault(); - GLUT.saveModifiers(event); - ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(key, Browser.mouseX, Browser.mouseY); - } - } - } - }, - onKeyup:(event) => { - if (GLUT.specialUpFunc || GLUT.keyboardUpFunc) { - var key = GLUT.getSpecialKey(event['keyCode']); - if (key !== null) { - if (GLUT.specialUpFunc) { - event.preventDefault (); - GLUT.saveModifiers(event); - ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(key, Browser.mouseX, Browser.mouseY); - } - } else { - key = GLUT.getASCIIKey(event); - if (key !== null && GLUT.keyboardUpFunc) { - event.preventDefault (); - GLUT.saveModifiers(event); - ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(key, Browser.mouseX, Browser.mouseY); - } - } - } - }, - touchHandler:(event) => { - if (event.target != Browser.getCanvas()) { - return; - } - - var touches = event.changedTouches, - main = touches[0], - type = ""; - - switch (event.type) { - case "touchstart": type = "mousedown"; break; - case "touchmove": type = "mousemove"; break; - case "touchend": type = "mouseup"; break; - default: return; - } - - var simulatedEvent = document.createEvent("MouseEvent"); - simulatedEvent.initMouseEvent(type, true, true, window, 1, - main.screenX, main.screenY, - main.clientX, main.clientY, false, - false, false, false, 0/*main*/, null); - - main.target.dispatchEvent(simulatedEvent); - event.preventDefault(); - }, - onMouseButtonDown:(event) => { - Browser.calculateMouseEvent(event); - - GLUT.buttons |= (1 << event['button']); - - if (event.target == Browser.getCanvas() && GLUT.mouseFunc) { - try { - event.target.setCapture(); - } catch (e) {} - event.preventDefault(); - GLUT.saveModifiers(event); - ((a1, a2, a3, a4) => {} /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(event['button'], 0/*GLUT_DOWN*/, Browser.mouseX, Browser.mouseY); - } - }, - onMouseButtonUp:(event) => { - Browser.calculateMouseEvent(event); - - GLUT.buttons &= ~(1 << event['button']); - - if (GLUT.mouseFunc) { - event.preventDefault(); - GLUT.saveModifiers(event); - ((a1, a2, a3, a4) => {} /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(event['button'], 1/*GLUT_UP*/, Browser.mouseX, Browser.mouseY); - } - }, - onMouseWheel:(event) => { - Browser.calculateMouseEvent(event); - - // cross-browser wheel delta - var e = window.event || event; // old IE support - // Note the minus sign that flips browser wheel direction (positive direction scrolls page down) to native wheel direction (positive direction is mouse wheel up) - var delta = -Browser.getMouseWheelDelta(event); - delta = (delta == 0) ? 0 : (delta > 0 ? Math.max(delta, 1) : Math.min(delta, -1)); // Quantize to integer so that minimum scroll is at least +/- 1. - - var button = 3; // wheel up - if (delta < 0) { - button = 4; // wheel down - } - - if (GLUT.mouseFunc) { - event.preventDefault(); - GLUT.saveModifiers(event); - ((a1, a2, a3, a4) => {} /* a dynamic function call to signature viiii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(button, 0/*GLUT_DOWN*/, Browser.mouseX, Browser.mouseY); - } - }, - onFullscreenEventChange:(event) => { - var width; - var height; - if (getFullscreenElement()) { - width = screen["width"]; - height = screen["height"]; - } else { - width = GLUT.windowWidth; - height = GLUT.windowHeight; - // TODO set position - document.removeEventListener('fullscreenchange', GLUT.onFullscreenEventChange, true); - document.removeEventListener('mozfullscreenchange', GLUT.onFullscreenEventChange, true); - document.removeEventListener('webkitfullscreenchange', GLUT.onFullscreenEventChange, true); - } - Browser.setCanvasSize(width, height, true); // N.B. GLUT.reshapeFunc is also registered as a canvas resize callback. - // Just call it once here. - /* Can't call _glutReshapeWindow as that requests cancelling fullscreen. */ - if (GLUT.reshapeFunc) { - // out("GLUT.reshapeFunc (from FS): " + width + ", " + height); - ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(width, height); - } - _glutPostRedisplay(); - }, - onResize:() => { - // Update canvas size to clientWidth and clientHeight, which include CSS scaling - var canvas = Browser.getCanvas(); - Browser.setCanvasSize(canvas.clientWidth, canvas.clientHeight, /*noUpdates*/false); - }, - }; - - var _glutGetModifiers = () => GLUT.modifiers; - _glutGetModifiers.sig = 'i'; - - - - var _glutInit = (argcp, argv) => { - // Ignore arguments - GLUT.initTime = Date.now(); - - var isTouchDevice = 'ontouchstart' in document.documentElement; - if (isTouchDevice) { - // onMouseButtonDown, onMouseButtonUp and onMousemove handlers - // depend on Browser.mouseX / Browser.mouseY fields. Those fields - // don't get updated by touch events. So register a touchHandler - // function that translates the touch events to mouse events. - - // GLUT doesn't support touch, mouse only, so from touch events we - // are only looking at single finger touches to emulate left click, - // so we can use workaround and convert all touch events in mouse - // events. See touchHandler. - window.addEventListener('touchmove', GLUT.touchHandler, true); - window.addEventListener('touchstart', GLUT.touchHandler, true); - window.addEventListener('touchend', GLUT.touchHandler, true); - } - - window.addEventListener('keydown', GLUT.onKeydown, true); - window.addEventListener('keyup', GLUT.onKeyup, true); - window.addEventListener('mousemove', GLUT.onMousemove, true); - window.addEventListener('mousedown', GLUT.onMouseButtonDown, true); - window.addEventListener('mouseup', GLUT.onMouseButtonUp, true); - // IE9, Chrome, Safari, Opera - window.addEventListener('mousewheel', GLUT.onMouseWheel, true); - // Firefox - window.addEventListener('DOMMouseScroll', GLUT.onMouseWheel, true); - - // Resize callback stage 1: update canvas which notifies resizeListeners - window.addEventListener('resize', GLUT.onResize, true); - - // Resize callback stage 2: updateResizeListeners notifies reshapeFunc - Browser.resizeListeners.push((width, height) => { - if (GLUT.reshapeFunc) { - ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(width, height); - } - }); - - addOnExit(() => { - if (isTouchDevice) { - window.removeEventListener('touchmove', GLUT.touchHandler, true); - window.removeEventListener('touchstart', GLUT.touchHandler, true); - window.removeEventListener('touchend', GLUT.touchHandler, true); - } - - window.removeEventListener('keydown', GLUT.onKeydown, true); - window.removeEventListener('keyup', GLUT.onKeyup, true); - window.removeEventListener('mousemove', GLUT.onMousemove, true); - window.removeEventListener('mousedown', GLUT.onMouseButtonDown, true); - window.removeEventListener('mouseup', GLUT.onMouseButtonUp, true); - // IE9, Chrome, Safari, Opera - window.removeEventListener('mousewheel', GLUT.onMouseWheel, true); - // Firefox - window.removeEventListener('DOMMouseScroll', GLUT.onMouseWheel, true); - - window.removeEventListener('resize', GLUT.onResize, true); - - var canvas = Browser.getCanvas(); - canvas.width = canvas.height = 1; - }); - }; - _glutInit.sig = 'vpp'; - - var _glutInitWindowSize = (width, height) => { - Browser.setCanvasSize( GLUT.initWindowWidth = width, - GLUT.initWindowHeight = height ); - }; - _glutInitWindowSize.sig = 'vii'; - - var _glutInitWindowPosition = (x, y) => {}; - _glutInitWindowPosition.sig = 'vii'; - - var _glutGet = (type) => { - switch (type) { - case 100: /* GLUT_WINDOW_X */ - return 0; /* TODO */ - case 101: /* GLUT_WINDOW_Y */ - return 0; /* TODO */ - case 102: /* GLUT_WINDOW_WIDTH */ - return Browser.getCanvas().width; - case 103: /* GLUT_WINDOW_HEIGHT */ - return Browser.getCanvas().height; - case 200: /* GLUT_SCREEN_WIDTH */ - return Browser.getCanvas().width; - case 201: /* GLUT_SCREEN_HEIGHT */ - return Browser.getCanvas().height; - case 500: /* GLUT_INIT_WINDOW_X */ - return 0; /* TODO */ - case 501: /* GLUT_INIT_WINDOW_Y */ - return 0; /* TODO */ - case 502: /* GLUT_INIT_WINDOW_WIDTH */ - return GLUT.initWindowWidth; - case 503: /* GLUT_INIT_WINDOW_HEIGHT */ - return GLUT.initWindowHeight; - case 700: /* GLUT_ELAPSED_TIME */ - var now = Date.now(); - return now - GLUT.initTime; - case 0x0069: /* GLUT_WINDOW_STENCIL_SIZE */ - return GLctx.getContextAttributes().stencil ? 8 : 0; - case 0x006A: /* GLUT_WINDOW_DEPTH_SIZE */ - return GLctx.getContextAttributes().depth ? 8 : 0; - case 0x006E: /* GLUT_WINDOW_ALPHA_SIZE */ - return GLctx.getContextAttributes().alpha ? 8 : 0; - case 0x0078: /* GLUT_WINDOW_NUM_SAMPLES */ - return GLctx.getContextAttributes().antialias ? 1 : 0; - - default: - abort("glutGet(" + type + ") not implemented yet"); - } - }; - _glutGet.sig = 'ii'; - - - var _glutIdleFunc = (func) => { - function callback() { - if (GLUT.idleFunc) { - (() => {} /* a dynamic function call to signature v, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(); - safeSetTimeout(callback, 4); // HTML spec specifies a 4ms minimum delay on the main thread; workers might get more, but we standardize here - } - } - if (!GLUT.idleFunc) { - safeSetTimeout(callback, 0); - } - GLUT.idleFunc = func; - }; - _glutIdleFunc.sig = 'vp'; - - - var _glutTimerFunc = (msec, func, value) => - safeSetTimeout(() => ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(value), msec); - _glutTimerFunc.sig = 'vipi'; - - var _glutDisplayFunc = (func) => { - GLUT.displayFunc = func; - }; - _glutDisplayFunc.sig = 'vp'; - - var _glutKeyboardFunc = (func) => { - GLUT.keyboardFunc = func; - }; - _glutKeyboardFunc.sig = 'vp'; - - var _glutKeyboardUpFunc = (func) => { - GLUT.keyboardUpFunc = func; - }; - _glutKeyboardUpFunc.sig = 'vp'; - - var _glutSpecialFunc = (func) => { - GLUT.specialFunc = func; - }; - _glutSpecialFunc.sig = 'vp'; - - var _glutSpecialUpFunc = (func) => { - GLUT.specialUpFunc = func; - }; - _glutSpecialUpFunc.sig = 'vp'; - - var _glutReshapeFunc = (func) => { - GLUT.reshapeFunc = func; - }; - _glutReshapeFunc.sig = 'vp'; - - var _glutMotionFunc = (func) => { - GLUT.motionFunc = func; - }; - _glutMotionFunc.sig = 'vp'; - - var _glutPassiveMotionFunc = (func) => { - GLUT.passiveMotionFunc = func; - }; - _glutPassiveMotionFunc.sig = 'vp'; - - var _glutMouseFunc = (func) => { - GLUT.mouseFunc = func; - }; - _glutMouseFunc.sig = 'vp'; - - var _glutSetCursor = (cursor) => { - var cursorStyle = 'auto'; - switch (cursor) { - case 0x0000: /* GLUT_CURSOR_RIGHT_ARROW */ - // No equivalent css cursor style, fallback to 'auto' - break; - case 0x0001: /* GLUT_CURSOR_LEFT_ARROW */ - // No equivalent css cursor style, fallback to 'auto' - break; - case 0x0002: /* GLUT_CURSOR_INFO */ - cursorStyle = 'pointer'; - break; - case 0x0003: /* GLUT_CURSOR_DESTROY */ - // No equivalent css cursor style, fallback to 'auto' - break; - case 0x0004: /* GLUT_CURSOR_HELP */ - cursorStyle = 'help'; - break; - case 0x0005: /* GLUT_CURSOR_CYCLE */ - // No equivalent css cursor style, fallback to 'auto' - break; - case 0x0006: /* GLUT_CURSOR_SPRAY */ - // No equivalent css cursor style, fallback to 'auto' - break; - case 0x0007: /* GLUT_CURSOR_WAIT */ - cursorStyle = 'wait'; - break; - case 0x0008: /* GLUT_CURSOR_TEXT */ - cursorStyle = 'text'; - break; - case 0x0009: /* GLUT_CURSOR_CROSSHAIR */ - case 0x0066: /* GLUT_CURSOR_FULL_CROSSHAIR */ - cursorStyle = 'crosshair'; - break; - case 0x000A: /* GLUT_CURSOR_UP_DOWN */ - cursorStyle = 'ns-resize'; - break; - case 0x000B: /* GLUT_CURSOR_LEFT_RIGHT */ - cursorStyle = 'ew-resize'; - break; - case 0x000C: /* GLUT_CURSOR_TOP_SIDE */ - cursorStyle = 'n-resize'; - break; - case 0x000D: /* GLUT_CURSOR_BOTTOM_SIDE */ - cursorStyle = 's-resize'; - break; - case 0x000E: /* GLUT_CURSOR_LEFT_SIDE */ - cursorStyle = 'w-resize'; - break; - case 0x000F: /* GLUT_CURSOR_RIGHT_SIDE */ - cursorStyle = 'e-resize'; - break; - case 0x0010: /* GLUT_CURSOR_TOP_LEFT_CORNER */ - cursorStyle = 'nw-resize'; - break; - case 0x0011: /* GLUT_CURSOR_TOP_RIGHT_CORNER */ - cursorStyle = 'ne-resize'; - break; - case 0x0012: /* GLUT_CURSOR_BOTTOM_RIGHT_CORNER */ - cursorStyle = 'se-resize'; - break; - case 0x0013: /* GLUT_CURSOR_BOTTOM_LEFT_CORNER */ - cursorStyle = 'sw-resize'; - break; - case 0x0064: /* GLUT_CURSOR_INHERIT */ - break; - case 0x0065: /* GLUT_CURSOR_NONE */ - cursorStyle = 'none'; - break; - default: - abort("glutSetCursor: Unknown cursor type: " + cursor); - } - Browser.getCanvas().style.cursor = cursorStyle; - }; - _glutSetCursor.sig = 'vi'; - - - var _glutCreateWindow = (name) => { - var contextAttributes = { - antialias: ((GLUT.initDisplayMode & 0x0080 /*GLUT_MULTISAMPLE*/) != 0), - depth: ((GLUT.initDisplayMode & 0x0010 /*GLUT_DEPTH*/) != 0), - stencil: ((GLUT.initDisplayMode & 0x0020 /*GLUT_STENCIL*/) != 0), - alpha: ((GLUT.initDisplayMode & 0x0008 /*GLUT_ALPHA*/) != 0) - }; - if (!Browser.createContext(Browser.getCanvas(), /*useWebGL=*/true, /*setInModule=*/true, contextAttributes)) { - return 0; // failure - } - return 1; // a new GLUT window ID for the created context - }; - _glutCreateWindow.sig = 'ip'; - - - var _glutDestroyWindow = (name) => { - delete Module['ctx']; - return 1; - }; - _glutDestroyWindow.sig = 'vi'; - - - - var _glutReshapeWindow = (width, height) => { - Browser.exitFullscreen(); - Browser.setCanvasSize(width, height, true); // N.B. GLUT.reshapeFunc is also registered as a canvas resize callback. - // Just call it once here. - if (GLUT.reshapeFunc) { - ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(width, height); - } - _glutPostRedisplay(); - }; - _glutReshapeWindow.sig = 'vii'; - - - - var _glutPositionWindow = (x, y) => { - Browser.exitFullscreen(); - /* TODO */ - _glutPostRedisplay(); - }; - _glutPositionWindow.sig = 'vii'; - - - var _glutFullScreen = () => { - GLUT.windowX = 0; // TODO - GLUT.windowY = 0; // TODO - var canvas = Browser.getCanvas(); - GLUT.windowWidth = canvas.width; - GLUT.windowHeight = canvas.height; - document.addEventListener('fullscreenchange', GLUT.onFullscreenEventChange, true); - document.addEventListener('mozfullscreenchange', GLUT.onFullscreenEventChange, true); - document.addEventListener('webkitfullscreenchange', GLUT.onFullscreenEventChange, true); - Browser.requestFullscreen(/*lockPointer=*/false, /*resizeCanvas=*/false); - }; - _glutFullScreen.sig = 'v'; - - var _glutInitDisplayMode = (mode) => GLUT.initDisplayMode = mode; - _glutInitDisplayMode.sig = 'vi'; - - var _glutSwapBuffers = () => {}; - _glutSwapBuffers.sig = 'v'; - - - - - var _glutMainLoop = () => { - // Do an initial resize, since there's no window resize event on startup - GLUT.onResize(); - _glutPostRedisplay(); - throw 'unwind'; - }; - _glutMainLoop.sig = 'v'; - - var _XOpenDisplay = (name) => 1; - _XOpenDisplay.sig = 'pp'; - - var _XCreateWindow = (display, parent, x, y, width, height, border_width, depth, class_, visual, valuemask, attributes) => { - // All we can do is set the width and height - Browser.setCanvasSize(width, height); - return 2; - }; - _XCreateWindow.sig = 'pppiiiiiiippp'; - - var _XChangeWindowAttributes = (display, window, valuemask, attributes) => {}; - _XChangeWindowAttributes.sig = 'ipppp'; - - var _XSetWMHints = (display, win, hints) => {}; - _XSetWMHints.sig = 'ippp'; - - var _XMapWindow = (display, win) => {}; - _XMapWindow.sig = 'ipp'; - - var _XStoreName = (display, win, name) => {}; - _XStoreName.sig = 'ippp'; - - var _XInternAtom = (display, name_, hmm) => 0; - _XInternAtom.sig = 'pppi'; - - var _XSendEvent = (display, win, propagate, event_mask, even_send) => {}; - _XSendEvent.sig = 'ippipp'; - - var _XPending = (display) => 0; - _XPending.sig = 'ip'; - - - var EGL = { - errorCode:12288, - defaultDisplayInitialized:false, - currentContext:0, - currentReadSurface:0, - currentDrawSurface:0, - contextAttributes:{ - alpha:false, - depth:false, - stencil:false, - antialias:false, - }, - stringCache:{ - }, - setErrorCode(code) { - EGL.errorCode = code; - }, - chooseConfig(display, attribList, config, config_size, numConfigs) { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - - if (attribList) { - // read attribList if it is non-null - for (;;) { - var param = HEAP32[((attribList)>>2)]; - if (param == 0x3021 /*EGL_ALPHA_SIZE*/) { - var alphaSize = HEAP32[(((attribList)+(4))>>2)]; - EGL.contextAttributes.alpha = (alphaSize > 0); - } else if (param == 0x3025 /*EGL_DEPTH_SIZE*/) { - var depthSize = HEAP32[(((attribList)+(4))>>2)]; - EGL.contextAttributes.depth = (depthSize > 0); - } else if (param == 0x3026 /*EGL_STENCIL_SIZE*/) { - var stencilSize = HEAP32[(((attribList)+(4))>>2)]; - EGL.contextAttributes.stencil = (stencilSize > 0); - } else if (param == 0x3031 /*EGL_SAMPLES*/) { - var samples = HEAP32[(((attribList)+(4))>>2)]; - EGL.contextAttributes.antialias = (samples > 0); - } else if (param == 0x3032 /*EGL_SAMPLE_BUFFERS*/) { - var samples = HEAP32[(((attribList)+(4))>>2)]; - EGL.contextAttributes.antialias = (samples == 1); - } else if (param == 0x3100 /*EGL_CONTEXT_PRIORITY_LEVEL_IMG*/) { - var requestedPriority = HEAP32[(((attribList)+(4))>>2)]; - EGL.contextAttributes.lowLatency = (requestedPriority != 0x3103 /*EGL_CONTEXT_PRIORITY_LOW_IMG*/); - } else if (param == 0x3038 /*EGL_NONE*/) { - break; - } - attribList += 8; - } - } - - if ((!config || !config_size) && !numConfigs) { - EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); - return 0; - } - if (numConfigs) { - HEAP32[((numConfigs)>>2)] = 1; // Total number of supported configs: 1. - } - if (config && config_size > 0) { - HEAPU32[((config)>>2)] = 62002; - } - - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1; - }, - }; - - var _eglGetDisplay = (nativeDisplayType) => { - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - // Emscripten EGL implementation "emulates" X11, and eglGetDisplay is - // expected to accept/receive a pointer to an X11 Display object (or - // EGL_DEFAULT_DISPLAY). - if (nativeDisplayType != 0 /* EGL_DEFAULT_DISPLAY */ && nativeDisplayType != 1 /* see library_xlib.js */) { - return 0; // EGL_NO_DISPLAY - } - return 62000; - }; - _eglGetDisplay.sig = 'pp'; - - var _eglInitialize = (display, majorVersion, minorVersion) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - if (majorVersion) { - HEAP32[((majorVersion)>>2)] = 1; // Advertise EGL Major version: '1' - } - if (minorVersion) { - HEAP32[((minorVersion)>>2)] = 4; // Advertise EGL Minor version: '4' - } - EGL.defaultDisplayInitialized = true; - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1; - }; - _eglInitialize.sig = 'ippp'; - - var _eglTerminate = (display) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - EGL.currentContext = 0; - EGL.currentReadSurface = 0; - EGL.currentDrawSurface = 0; - EGL.defaultDisplayInitialized = false; - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1; - }; - _eglTerminate.sig = 'ip'; - - var _eglGetConfigs = (display, configs, config_size, numConfigs) => - EGL.chooseConfig(display, 0, configs, config_size, numConfigs); - _eglGetConfigs.sig = 'ippip'; - - var _eglChooseConfig = (display, attrib_list, configs, config_size, numConfigs) => - EGL.chooseConfig(display, attrib_list, configs, config_size, numConfigs); - _eglChooseConfig.sig = 'ipppip'; - - var _eglGetConfigAttrib = (display, config, attribute, value) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - if (config != 62002) { - EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */); - return 0; - } - if (!value) { - EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); - return 0; - } - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - switch (attribute) { - case 0x3020: // EGL_BUFFER_SIZE - HEAP32[((value)>>2)] = EGL.contextAttributes.alpha ? 32 : 24; - return 1; - case 0x3021: // EGL_ALPHA_SIZE - HEAP32[((value)>>2)] = EGL.contextAttributes.alpha ? 8 : 0; - return 1; - case 0x3022: // EGL_BLUE_SIZE - HEAP32[((value)>>2)] = 8; - return 1; - case 0x3023: // EGL_GREEN_SIZE - HEAP32[((value)>>2)] = 8; - return 1; - case 0x3024: // EGL_RED_SIZE - HEAP32[((value)>>2)] = 8; - return 1; - case 0x3025: // EGL_DEPTH_SIZE - HEAP32[((value)>>2)] = EGL.contextAttributes.depth ? 24 : 0; - return 1; - case 0x3026: // EGL_STENCIL_SIZE - HEAP32[((value)>>2)] = EGL.contextAttributes.stencil ? 8 : 0; - return 1; - case 0x3027: // EGL_CONFIG_CAVEAT - // We can return here one of EGL_NONE (0x3038), EGL_SLOW_CONFIG (0x3050) or EGL_NON_CONFORMANT_CONFIG (0x3051). - HEAP32[((value)>>2)] = 0x3038; - return 1; - case 0x3028: // EGL_CONFIG_ID - HEAP32[((value)>>2)] = 62002; - return 1; - case 0x3029: // EGL_LEVEL - HEAP32[((value)>>2)] = 0; - return 1; - case 0x302A: // EGL_MAX_PBUFFER_HEIGHT - HEAP32[((value)>>2)] = 4096; - return 1; - case 0x302B: // EGL_MAX_PBUFFER_PIXELS - HEAP32[((value)>>2)] = 16777216; - return 1; - case 0x302C: // EGL_MAX_PBUFFER_WIDTH - HEAP32[((value)>>2)] = 4096; - return 1; - case 0x302D: // EGL_NATIVE_RENDERABLE - HEAP32[((value)>>2)] = 0; - return 1; - case 0x302E: // EGL_NATIVE_VISUAL_ID - HEAP32[((value)>>2)] = 0; - return 1; - case 0x302F: // EGL_NATIVE_VISUAL_TYPE - HEAP32[((value)>>2)] = 0x3038; - return 1; - case 0x3031: // EGL_SAMPLES - HEAP32[((value)>>2)] = EGL.contextAttributes.antialias ? 4 : 0; - return 1; - case 0x3032: // EGL_SAMPLE_BUFFERS - HEAP32[((value)>>2)] = EGL.contextAttributes.antialias ? 1 : 0; - return 1; - case 0x3033: // EGL_SURFACE_TYPE - HEAP32[((value)>>2)] = 0x4; - return 1; - case 0x3034: // EGL_TRANSPARENT_TYPE - // If this returns EGL_TRANSPARENT_RGB (0x3052), transparency is used through color-keying. No such thing applies to Emscripten canvas. - HEAP32[((value)>>2)] = 0x3038; - return 1; - case 0x3035: // EGL_TRANSPARENT_BLUE_VALUE - case 0x3036: // EGL_TRANSPARENT_GREEN_VALUE - case 0x3037: // EGL_TRANSPARENT_RED_VALUE - // "If EGL_TRANSPARENT_TYPE is EGL_NONE, then the values for EGL_TRANSPARENT_RED_VALUE, EGL_TRANSPARENT_GREEN_VALUE, and EGL_TRANSPARENT_BLUE_VALUE are undefined." - HEAP32[((value)>>2)] = -1; - return 1; - case 0x3039: // EGL_BIND_TO_TEXTURE_RGB - case 0x303A: // EGL_BIND_TO_TEXTURE_RGBA - HEAP32[((value)>>2)] = 0; - return 1; - case 0x303B: // EGL_MIN_SWAP_INTERVAL - HEAP32[((value)>>2)] = 0; - return 1; - case 0x303C: // EGL_MAX_SWAP_INTERVAL - HEAP32[((value)>>2)] = 1; - return 1; - case 0x303D: // EGL_LUMINANCE_SIZE - case 0x303E: // EGL_ALPHA_MASK_SIZE - HEAP32[((value)>>2)] = 0; - return 1; - case 0x303F: // EGL_COLOR_BUFFER_TYPE - // EGL has two types of buffers: EGL_RGB_BUFFER and EGL_LUMINANCE_BUFFER. - HEAP32[((value)>>2)] = 0x308E; - return 1; - case 0x3040: // EGL_RENDERABLE_TYPE - // A bit combination of EGL_OPENGL_ES_BIT,EGL_OPENVG_BIT,EGL_OPENGL_ES2_BIT and EGL_OPENGL_BIT. - HEAP32[((value)>>2)] = 0x4; - return 1; - case 0x3042: // EGL_CONFORMANT - // "EGL_CONFORMANT is a mask indicating if a client API context created with respect to the corresponding EGLConfig will pass the required conformance tests for that API." - HEAP32[((value)>>2)] = 0; - return 1; - default: - EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */); - return 0; - } - }; - _eglGetConfigAttrib.sig = 'ippip'; - - var _eglCreateWindowSurface = (display, config, win, attrib_list) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - if (config != 62002) { - EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */); - return 0; - } - // TODO: Examine attrib_list! Parameters that can be present there are: - // - EGL_RENDER_BUFFER (must be EGL_BACK_BUFFER) - // - EGL_VG_COLORSPACE (can't be set) - // - EGL_VG_ALPHA_FORMAT (can't be set) - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 62006; /* Magic ID for Emscripten 'default surface' */ - }; - _eglCreateWindowSurface.sig = 'pppip'; - - var _eglDestroySurface = (display, surface) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - if (surface != 62006 /* Magic ID for the only EGLSurface supported by Emscripten */) { - EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */); - return 1; - } - if (EGL.currentReadSurface == surface) { - EGL.currentReadSurface = 0; - } - if (EGL.currentDrawSurface == surface) { - EGL.currentDrawSurface = 0; - } - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1; /* Magic ID for Emscripten 'default surface' */ - }; - _eglDestroySurface.sig = 'ipp'; - - - var _eglCreateContext = (display, config, hmm, contextAttribs) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - - // EGL 1.4 spec says default EGL_CONTEXT_CLIENT_VERSION is GLES1, but this is not supported by Emscripten. - // So user must pass EGL_CONTEXT_CLIENT_VERSION == 2 to initialize EGL. - var glesContextVersion = 1; - for (;;) { - var param = HEAP32[((contextAttribs)>>2)]; - if (param == 0x3098 /*EGL_CONTEXT_CLIENT_VERSION*/) { - glesContextVersion = HEAP32[(((contextAttribs)+(4))>>2)]; - } else if (param == 0x3038 /*EGL_NONE*/) { - break; - } else { - /* EGL1.4 specifies only EGL_CONTEXT_CLIENT_VERSION as supported attribute */ - EGL.setErrorCode(0x3004 /*EGL_BAD_ATTRIBUTE*/); - return 0; - } - contextAttribs += 8; - } - if (glesContextVersion != 2) { - EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */); - return 0; /* EGL_NO_CONTEXT */ - } - - EGL.contextAttributes.majorVersion = glesContextVersion - 1; // WebGL 1 is GLES 2, WebGL2 is GLES3 - EGL.contextAttributes.minorVersion = 0; - - EGL.context = GL.createContext(Browser.getCanvas(), EGL.contextAttributes); - - if (EGL.context != 0) { - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - - // Run callbacks so that GL emulation works - GL.makeContextCurrent(EGL.context); - Browser.useWebGL = true; - Browser.moduleContextCreatedCallbacks.forEach((callback) => callback()); - - // Note: This function only creates a context, but it shall not make it active. - GL.makeContextCurrent(null); - return 62004; - } else { - EGL.setErrorCode(0x3009 /* EGL_BAD_MATCH */); // By the EGL 1.4 spec, an implementation that does not support GLES2 (WebGL in this case), this error code is set. - return 0; /* EGL_NO_CONTEXT */ - } - }; - _eglCreateContext.sig = 'ppppp'; - - - var _eglDestroyContext = (display, context) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - if (context != 62004) { - EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */); - return 0; - } - - GL.deleteContext(EGL.context); - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - if (EGL.currentContext == context) { - EGL.currentContext = 0; - } - return 1 /* EGL_TRUE */; - }; - _eglDestroyContext.sig = 'ipp'; - - var _eglQuerySurface = (display, surface, attribute, value) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - if (surface != 62006 /* Magic ID for Emscripten 'default surface' */) { - EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */); - return 0; - } - if (!value) { - EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); - return 0; - } - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - switch (attribute) { - case 0x3028: // EGL_CONFIG_ID - HEAP32[((value)>>2)] = 62002; - return 1; - case 0x3058: // EGL_LARGEST_PBUFFER - // Odd EGL API: If surface is not a pbuffer surface, 'value' should not be written to. It's not specified as an error, so true should(?) be returned. - // Existing Android implementation seems to do so at least. - return 1; - case 0x3057: // EGL_WIDTH - HEAP32[((value)>>2)] = Browser.getCanvas().width; - return 1; - case 0x3056: // EGL_HEIGHT - HEAP32[((value)>>2)] = Browser.getCanvas().height; - return 1; - case 0x3090: // EGL_HORIZONTAL_RESOLUTION - HEAP32[((value)>>2)] = -1; - return 1; - case 0x3091: // EGL_VERTICAL_RESOLUTION - HEAP32[((value)>>2)] = -1; - return 1; - case 0x3092: // EGL_PIXEL_ASPECT_RATIO - HEAP32[((value)>>2)] = -1; - return 1; - case 0x3086: // EGL_RENDER_BUFFER - // The main surface is bound to the visible canvas window - it's always backbuffered. - // Alternative to EGL_BACK_BUFFER would be EGL_SINGLE_BUFFER. - HEAP32[((value)>>2)] = 0x3084; - return 1; - case 0x3099: // EGL_MULTISAMPLE_RESOLVE - HEAP32[((value)>>2)] = 0x309A; - return 1; - case 0x3093: // EGL_SWAP_BEHAVIOR - // The two possibilities are EGL_BUFFER_PRESERVED and EGL_BUFFER_DESTROYED. Slightly unsure which is the - // case for browser environment, but advertise the 'weaker' behavior to be sure. - HEAP32[((value)>>2)] = 0x3095; - return 1; - case 0x3080: // EGL_TEXTURE_FORMAT - case 0x3081: // EGL_TEXTURE_TARGET - case 0x3082: // EGL_MIPMAP_TEXTURE - case 0x3083: // EGL_MIPMAP_LEVEL - // This is a window surface, not a pbuffer surface. Spec: - // "Querying EGL_TEXTURE_FORMAT, EGL_TEXTURE_TARGET, EGL_MIPMAP_TEXTURE, or EGL_MIPMAP_LEVEL for a non-pbuffer surface is not an error, but value is not modified." - // So pass-through. - return 1; - default: - EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */); - return 0; - } - }; - _eglQuerySurface.sig = 'ippip'; - - var _eglQueryContext = (display, context, attribute, value) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy. - if (context != 62004) { - EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */); - return 0; - } - if (!value) { - EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); - return 0; - } - - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - switch (attribute) { - case 0x3028: // EGL_CONFIG_ID - HEAP32[((value)>>2)] = 62002; - return 1; - case 0x3097: // EGL_CONTEXT_CLIENT_TYPE - HEAP32[((value)>>2)] = 0x30A0; - return 1; - case 0x3098: // EGL_CONTEXT_CLIENT_VERSION - HEAP32[((value)>>2)] = EGL.contextAttributes.majorVersion + 1; - return 1; - case 0x3086: // EGL_RENDER_BUFFER - // The context is bound to the visible canvas window - it's always backbuffered. - // Alternative to EGL_BACK_BUFFER would be EGL_SINGLE_BUFFER. - HEAP32[((value)>>2)] = 0x3084; - return 1; - default: - EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */); - return 0; - } - }; - _eglQueryContext.sig = 'ippip'; - - var _eglGetError = () => EGL.errorCode; - _eglGetError.sig = 'i'; - - - var _eglQueryString = (display, name) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy. - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - if (EGL.stringCache[name]) return EGL.stringCache[name]; - var ret; - switch (name) { - case 0x3053 /* EGL_VENDOR */: ret = stringToNewUTF8("Emscripten"); break; - case 0x3054 /* EGL_VERSION */: ret = stringToNewUTF8("1.4 Emscripten EGL"); break; - case 0x3055 /* EGL_EXTENSIONS */: ret = stringToNewUTF8(""); break; // Currently not supporting any EGL extensions. - case 0x308D /* EGL_CLIENT_APIS */: ret = stringToNewUTF8("OpenGL_ES"); break; - default: - EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); - return 0; - } - EGL.stringCache[name] = ret; - return ret; - }; - _eglQueryString.sig = 'ppi'; - - var _eglBindAPI = (api) => { - if (api == 0x30A0 /* EGL_OPENGL_ES_API */) { - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1; - } - // if (api == 0x30A1 /* EGL_OPENVG_API */ || api == 0x30A2 /* EGL_OPENGL_API */) { - EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); - return 0; - }; - _eglBindAPI.sig = 'ii'; - - var _eglQueryAPI = () => { - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 0x30A0; // EGL_OPENGL_ES_API - }; - _eglQueryAPI.sig = 'i'; - - var _eglWaitClient = () => { - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1; - }; - _eglWaitClient.sig = 'i'; - - var _eglWaitNative = (nativeEngineId) => { - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1; - }; - _eglWaitNative.sig = 'ii'; - - - var _eglWaitGL = _eglWaitClient; - _eglWaitGL.sig = 'i'; - - - var _eglSwapInterval = (display, interval) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0; - } - if (interval == 0) _emscripten_set_main_loop_timing(0, 0); - else _emscripten_set_main_loop_timing(1, interval); - - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1; - }; - _eglSwapInterval.sig = 'ipi'; - - - var _eglMakeCurrent = (display, draw, read, context) => { - if (display != 62000) { - EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); - return 0 /* EGL_FALSE */; - } - //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy. - if (context != 0 && context != 62004) { - EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */); - return 0; - } - if ((read != 0 && read != 62006) || (draw != 0 && draw != 62006 /* Magic ID for Emscripten 'default surface' */)) { - EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */); - return 0; - } - - GL.makeContextCurrent(context ? EGL.context : null); - - EGL.currentContext = context; - EGL.currentDrawSurface = draw; - EGL.currentReadSurface = read; - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1 /* EGL_TRUE */; - }; - _eglMakeCurrent.sig = 'ipppp'; - - var _eglGetCurrentContext = () => EGL.currentContext; - _eglGetCurrentContext.sig = 'p'; - - var _eglGetCurrentSurface = (readdraw) => { - if (readdraw == 0x305A /* EGL_READ */) { - return EGL.currentReadSurface; - } else if (readdraw == 0x3059 /* EGL_DRAW */) { - return EGL.currentDrawSurface; - } else { - EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); - return 0 /* EGL_NO_SURFACE */; - } - }; - _eglGetCurrentSurface.sig = 'pi'; - - var _eglGetCurrentDisplay = () => EGL.currentContext ? 62000 : 0; - _eglGetCurrentDisplay.sig = 'p'; - - - var _eglSwapBuffers = (dpy, surface) => { - - if (!EGL.defaultDisplayInitialized) { - EGL.setErrorCode(0x3001 /* EGL_NOT_INITIALIZED */); - } else if (!GLctx) { - EGL.setErrorCode(0x3002 /* EGL_BAD_ACCESS */); - } else if (GLctx.isContextLost()) { - EGL.setErrorCode(0x300E /* EGL_CONTEXT_LOST */); - } else { - // According to documentation this does an implicit flush. - // Due to discussion at https://github.com/emscripten-core/emscripten/pull/1871 - // the flush was removed since this _may_ result in slowing code down. - //_glFlush(); - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1 /* EGL_TRUE */; - } - return 0 /* EGL_FALSE */; - }; - _eglSwapBuffers.sig = 'ipp'; - - var _eglReleaseThread = () => { - // Equivalent to eglMakeCurrent with EGL_NO_CONTEXT and EGL_NO_SURFACE. - EGL.currentContext = 0; - EGL.currentReadSurface = 0; - EGL.currentDrawSurface = 0; - // EGL spec v1.4 p.55: - // "calling eglGetError immediately following a successful call to eglReleaseThread should not be done. - // Such a call will return EGL_SUCCESS - but will also result in reallocating per-thread state." - EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); - return 1 /* EGL_TRUE */; - }; - _eglReleaseThread.sig = 'i'; - - var _uuid_clear = (uu) => zeroMemory(uu, 16); - _uuid_clear.sig = 'vp'; - - var _uuid_compare = (uu1, uu2) => _memcmp(uu1, uu2, 16); - _uuid_compare.sig = 'ipp'; - - var _uuid_copy = (dst, src) => _memcpy(dst, src, 16); - _uuid_copy.sig = 'vpp'; - - - var _uuid_generate = (out) => { - // void uuid_generate(uuid_t out); - var uuid = new Uint8Array(16); - randomFill(uuid); - - // Makes uuid compliant to RFC-4122 - uuid[6] = (uuid[6] & 0x0F) | 0x40; // uuid version - uuid[8] = (uuid[8] & 0x3F) | 0x80; // uuid variant - writeArrayToMemory(uuid, out); - }; - _uuid_generate.sig = 'vp'; - - var _uuid_is_null = (uu) => { - // int uuid_is_null(const uuid_t uu); - for (var i = 0; i < 4; i++, uu = (uu+4)|0) { - var val = HEAP32[((uu)>>2)]; - if (val) { - return 0; - } - } - return 1; - }; - _uuid_is_null.sig = 'ip'; - - var _uuid_parse = (inp, uu) => { - // int uuid_parse(const char *in, uuid_t uu); - inp = UTF8ToString(inp); - if (inp.length === 36) { - var i = 0; - var uuid = new Array(16); - inp.toLowerCase().replace(/[0-9a-f]{2}/g, function(byte) { - if (i < 16) { - uuid[i++] = parseInt(byte, 16); - } - }); - - if (i < 16) { - return -1; - } - writeArrayToMemory(uuid, uu); - return 0; - } - return -1; - }; - _uuid_parse.sig = 'ipp'; - - /** @param {number|boolean=} upper */ - var _uuid_unparse = (uu, out, upper) => { - // void uuid_unparse(const uuid_t uu, char *out); - var i = 0; - var uuid = 'xxxx-xx-xx-xx-xxxxxx'.replace(/[x]/g, function(c) { - var r = upper ? (HEAPU8[(uu)+(i)]).toString(16).toUpperCase() : - (HEAPU8[(uu)+(i)]).toString(16); - r = (r.length === 1) ? '0' + r : r; // Zero pad single digit hex values - i++; - return r; - }); - stringToUTF8(uuid, out, 37); // Always fixed 36 bytes of ASCII characters and a trailing \0. - }; - _uuid_unparse.sig = 'vpp'; - - var _uuid_unparse_lower = (uu, out) => { - // void uuid_unparse_lower(const uuid_t uu, char *out); - _uuid_unparse(uu, out); - }; - _uuid_unparse_lower.sig = 'vpp'; - - var _uuid_unparse_upper = (uu, out) => { - // void uuid_unparse_upper(const uuid_t uu, char *out); - _uuid_unparse(uu, out, true); - }; - _uuid_unparse_upper.sig = 'vpp'; - - var _uuid_type = (uu) => 4; - _uuid_type.sig = 'ip'; - - var _uuid_variant = (uu) => 1; - _uuid_variant.sig = 'ip'; - - - - - - var GLEW = { - isLinaroFork:1, - extensions:null, - error:{ - 0:null, - 1:null, - 2:null, - 3:null, - 4:null, - 5:null, - 6:null, - 7:null, - 8:null, - }, - version:{ - 1:null, - 2:null, - 3:null, - 4:null, - }, - errorStringConstantFromCode(error) { - if (GLEW.isLinaroFork) { - switch (error) { - case 4:return "OpenGL ES lib expected, found OpenGL lib"; // GLEW_ERROR_NOT_GLES_VERSION - case 5:return "OpenGL lib expected, found OpenGL ES lib"; // GLEW_ERROR_GLES_VERSION - case 6:return "Missing EGL version"; // GLEW_ERROR_NO_EGL_VERSION - case 7:return "EGL 1.1 and up are supported"; // GLEW_ERROR_EGL_VERSION_10_ONLY - default:break; - } - } - - switch (error) { - case 0:return "No error"; // GLEW_OK || GLEW_NO_ERROR - case 1:return "Missing GL version"; // GLEW_ERROR_NO_GL_VERSION - case 2:return "GL 1.1 and up are supported"; // GLEW_ERROR_GL_VERSION_10_ONLY - case 3:return "GLX 1.2 and up are supported"; // GLEW_ERROR_GLX_VERSION_11_ONLY - default:return null; - } - }, - errorString(error) { - if (!GLEW.error[error]) { - var string = GLEW.errorStringConstantFromCode(error); - if (!string) { - string = "Unknown error"; - error = 8; // prevent array from growing more than this - } - GLEW.error[error] = stringToNewUTF8(string); - } - return GLEW.error[error]; - }, - versionStringConstantFromCode(name) { - switch (name) { - case 1:return "1.10.0"; // GLEW_VERSION - case 2:return "1"; // GLEW_VERSION_MAJOR - case 3:return "10"; // GLEW_VERSION_MINOR - case 4:return "0"; // GLEW_VERSION_MICRO - default:return null; - } - }, - versionString(name) { - if (!GLEW.version[name]) { - var string = GLEW.versionStringConstantFromCode(name); - if (!string) - return 0; - GLEW.version[name] = stringToNewUTF8(string); - } - return GLEW.version[name]; - }, - extensionIsSupported(name) { - GLEW.extensions ||= webglGetExtensions(); - - if (GLEW.extensions.includes(name)) - return 1; - - // extensions from GLEmulations do not come unprefixed - // so, try with prefix - return (GLEW.extensions.includes("GL_" + name)); - }, - }; - - var _glewInit = () => 0; - _glewInit.sig = 'i'; - - - var _glewIsSupported = (name) => { - var exts = UTF8ToString(name).split(' '); - for (var ext of exts) { - if (!GLEW.extensionIsSupported(ext)) return 0; - } - return 1; - }; - _glewIsSupported.sig = 'ip'; - - - var _glewGetExtension = (name) => GLEW.extensionIsSupported(UTF8ToString(name)); - _glewGetExtension.sig = 'ip'; - - var _glewGetErrorString = (error) => GLEW.errorString(error); - _glewGetErrorString.sig = 'pi'; - - var _glewGetString = (name) => GLEW.versionString(name); - _glewGetString.sig = 'pi'; - - var IDBStore = { - indexedDB() { - return indexedDB; - }, - DB_VERSION:22, - DB_STORE_NAME:"FILE_DATA", - dbs:{ - }, - blobs:[0], - getDB(name, callback) { - // check the cache first - var db = IDBStore.dbs[name]; - if (db) { - return callback(null, db); - } - var req; - try { - req = IDBStore.indexedDB().open(name, IDBStore.DB_VERSION); - } catch (e) { - return callback(e); - } - req.onupgradeneeded = (e) => { - var db = /** @type {IDBDatabase} */ (e.target.result); - var transaction = e.target.transaction; - var fileStore; - if (db.objectStoreNames.contains(IDBStore.DB_STORE_NAME)) { - fileStore = transaction.objectStore(IDBStore.DB_STORE_NAME); - } else { - fileStore = db.createObjectStore(IDBStore.DB_STORE_NAME); - } - }; - req.onsuccess = () => { - db = /** @type {IDBDatabase} */ (req.result); - // add to the cache - IDBStore.dbs[name] = db; - callback(null, db); - }; - req.onerror = function(event) { - callback(event.target.error || 'unknown error'); - event.preventDefault(); - }; - }, - getStore(dbName, type, callback) { - IDBStore.getDB(dbName, (error, db) => { - if (error) return callback(error); - var transaction = db.transaction([IDBStore.DB_STORE_NAME], type); - transaction.onerror = (event) => { - callback(event.target.error || 'unknown error'); - event.preventDefault(); - }; - var store = transaction.objectStore(IDBStore.DB_STORE_NAME); - callback(null, store); - }); - }, - getFile(dbName, id, callback) { - IDBStore.getStore(dbName, 'readonly', (err, store) => { - if (err) return callback(err); - var req = store.get(id); - req.onsuccess = (event) => { - var result = event.target.result; - if (!result) { - return callback(`file ${id} not found`); - } - return callback(null, result); - }; - req.onerror = callback; - }); - }, - setFile(dbName, id, data, callback) { - IDBStore.getStore(dbName, 'readwrite', (err, store) => { - if (err) return callback(err); - var req = store.put(data, id); - req.onsuccess = (event) => callback(); - req.onerror = callback; - }); - }, - deleteFile(dbName, id, callback) { - IDBStore.getStore(dbName, 'readwrite', (err, store) => { - if (err) return callback(err); - var req = store.delete(id); - req.onsuccess = (event) => callback(); - req.onerror = callback; - }); - }, - existsFile(dbName, id, callback) { - IDBStore.getStore(dbName, 'readonly', (err, store) => { - if (err) return callback(err); - var req = store.count(id); - req.onsuccess = (event) => callback(null, event.target.result > 0); - req.onerror = callback; - }); - }, - clearStore(dbName, callback) { - IDBStore.getStore(dbName, 'readwrite', (err, store) => { - if (err) return callback(err); - var req = store.clear(); - req.onsuccess = (event) => callback(); - req.onerror = callback; - }); - }, - }; - - - - - - - - - var _emscripten_idb_async_load = (db, id, arg, onload, onerror) => { - runtimeKeepalivePush();; - IDBStore.getFile(UTF8ToString(db), UTF8ToString(id), (error, byteArray) => { - runtimeKeepalivePop(); - callUserCallback(() => { - if (error) { - if (onerror) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - return; - } - var buffer = _malloc(byteArray.length); - HEAPU8.set(byteArray, buffer); - ((a1, a2, a3) => {} /* a dynamic function call to signature viii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg, buffer, byteArray.length); - _free(buffer); - }); - }); - }; - _emscripten_idb_async_load.sig = 'vppppp'; - - - - - - - var _emscripten_idb_async_store = (db, id, ptr, num, arg, onstore, onerror) => { - // note that we copy the data here, as these are async operatins - changes - // to HEAPU8 meanwhile should not affect us! - runtimeKeepalivePush();; - IDBStore.setFile(UTF8ToString(db), UTF8ToString(id), new Uint8Array(HEAPU8.subarray(ptr, ptr+num)), (error) => { - runtimeKeepalivePop(); - callUserCallback(() => { - if (error) { - if (onerror) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - return; - } - if (onstore) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - }); - }); - }; - _emscripten_idb_async_store.sig = 'vpppippp'; - - - - - - - var _emscripten_idb_async_delete = (db, id, arg, ondelete, onerror) => { - runtimeKeepalivePush();; - IDBStore.deleteFile(UTF8ToString(db), UTF8ToString(id), (error) => { - runtimeKeepalivePop(); - callUserCallback(() => { - if (error) { - if (onerror) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - return; - } - if (ondelete) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - }); - }); - }; - _emscripten_idb_async_delete.sig = 'vppppp'; - - - - - - - var _emscripten_idb_async_exists = (db, id, arg, oncheck, onerror) => { - runtimeKeepalivePush();; - IDBStore.existsFile(UTF8ToString(db), UTF8ToString(id), (error, exists) => { - runtimeKeepalivePop(); - callUserCallback(() => { - if (error) { - if (onerror) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - return; - } - if (oncheck) ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg, exists); - }); - }); - }; - _emscripten_idb_async_exists.sig = 'vppppp'; - - - - - - - var _emscripten_idb_async_clear = (db, arg, onclear, onerror) => { - runtimeKeepalivePush();; - IDBStore.clearStore(UTF8ToString(db), (error) => { - runtimeKeepalivePop(); - callUserCallback(() => { - if (error) { - if (onerror) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - return; - } - if (onclear) ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(arg); - }); - }); - }; - _emscripten_idb_async_clear.sig = 'vpppp'; - - - - var _emscripten_idb_load = (db, id, pbuffer, pnum, perror) => Asyncify.handleSleep((wakeUp) => { - IDBStore.getFile(UTF8ToString(db), UTF8ToString(id), (error, byteArray) => { - if (error) { - HEAP32[((perror)>>2)] = 1; - wakeUp(); - return; - } - var buffer = _malloc(byteArray.length); // must be freed by the caller! - HEAPU8.set(byteArray, buffer); - HEAPU32[((pbuffer)>>2)] = buffer; - HEAP32[((pnum)>>2)] = byteArray.length; - HEAP32[((perror)>>2)] = 0; - wakeUp(); - }); - }); - _emscripten_idb_load.sig = 'vppppp'; - _emscripten_idb_load.isAsync = true; - - - var _emscripten_idb_store = (db, id, ptr, num, perror) => Asyncify.handleSleep((wakeUp) => { - IDBStore.setFile(UTF8ToString(db), UTF8ToString(id), new Uint8Array(HEAPU8.subarray(ptr, ptr+num)), (error) => { - // Closure warns about storing booleans in TypedArrays. - /** @suppress{checkTypes} */ - HEAP32[((perror)>>2)] = !!error; - wakeUp(); - }); - }); - _emscripten_idb_store.sig = 'vpppip'; - _emscripten_idb_store.isAsync = true; - - - var _emscripten_idb_delete = (db, id, perror) => Asyncify.handleSleep((wakeUp) => { - IDBStore.deleteFile(UTF8ToString(db), UTF8ToString(id), (error) => { - /** @suppress{checkTypes} */ - HEAP32[((perror)>>2)] = !!error; - wakeUp(); - }); - }); - _emscripten_idb_delete.sig = 'vppp'; - _emscripten_idb_delete.isAsync = true; - - - var _emscripten_idb_exists = (db, id, pexists, perror) => Asyncify.handleSleep((wakeUp) => { - IDBStore.existsFile(UTF8ToString(db), UTF8ToString(id), (error, exists) => { - /** @suppress{checkTypes} */ - HEAP32[((pexists)>>2)] = !!exists; - /** @suppress{checkTypes} */ - HEAP32[((perror)>>2)] = !!error; - wakeUp(); - }); - }); - _emscripten_idb_exists.sig = 'vpppp'; - _emscripten_idb_exists.isAsync = true; - - - var _emscripten_idb_clear = (db, perror) => Asyncify.handleSleep((wakeUp) => { - IDBStore.clearStore(UTF8ToString(db), (error) => { - /** @suppress{checkTypes} */ - HEAP32[((perror)>>2)] = !!error; - wakeUp(); - }); - }); - _emscripten_idb_clear.sig = 'vpp'; - _emscripten_idb_clear.isAsync = true; - - - var _emscripten_idb_load_blob = (db, id, pblob, perror) => Asyncify.handleSleep((wakeUp) => { - IDBStore.pending = (msg) => { - IDBStore.pending = null; - var blob = msg.blob; - if (!blob) { - HEAP32[((perror)>>2)] = 1; - wakeUp(); - return; - } - var blobId = IDBStore.blobs.length; - IDBStore.blobs.push(blob); - HEAP32[((pblob)>>2)] = blobId; - wakeUp(); - }; - postMessage({ - target: 'IDBStore', - method: 'loadBlob', - db: UTF8ToString(db), - id: UTF8ToString(id) - }); - }); - _emscripten_idb_load_blob.sig = 'vpppp'; - _emscripten_idb_load_blob.isAsync = true; - - - var _emscripten_idb_store_blob = (db, id, ptr, num, perror) => Asyncify.handleSleep((wakeUp) => { - IDBStore.pending = (msg) => { - IDBStore.pending = null; - HEAP32[((perror)>>2)] = !!msg.error; - wakeUp(); - }; - postMessage({ - target: 'IDBStore', - method: 'storeBlob', - db: UTF8ToString(db), - id: UTF8ToString(id), - blob: new Blob([new Uint8Array(HEAPU8.subarray(ptr, ptr+num))]) - }); - }); - _emscripten_idb_store_blob.sig = 'vpppip'; - _emscripten_idb_store_blob.isAsync = true; - - var _emscripten_idb_read_from_blob = (blobId, start, num, buffer) => { - var blob = IDBStore.blobs[blobId]; - if (!blob) return 1; - if (start+num > blob.size) return 2; - var byteArray = (new FileReaderSync()).readAsArrayBuffer(blob.slice(start, start+num)); - HEAPU8.set(new Uint8Array(byteArray), buffer); - return 0; - }; - _emscripten_idb_read_from_blob.sig = 'viiip'; - - var _emscripten_idb_free_blob = (blobId) => { - IDBStore.blobs[blobId] = null; - }; - _emscripten_idb_free_blob.sig = 'vi'; - - - - - - var _emscripten_scan_registers = (func) => { - return Asyncify.handleSleep((wakeUp) => { - // We must first unwind, so things are spilled to the stack. Then while - // we are pausing we do the actual scan. After that we can resume. Note - // how using a timeout here avoids unbounded call stack growth, which - // could happen if we tried to scan the stack immediately after unwinding. - safeSetTimeout(() => { - var stackBegin = Asyncify.currData + 12; - var stackEnd = HEAPU32[((Asyncify.currData)>>2)]; - ((a1, a2) => {} /* a dynamic function call to signature vii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(stackBegin, stackEnd); - wakeUp(); - }, 0); - }); - }; - _emscripten_scan_registers.sig = 'vp'; - _emscripten_scan_registers.isAsync = true; - - async function __load_secondary_module() { - // Mark the module as loading for the wasm module (so it doesn't try to load it again). - wasmExports['load_secondary_module_status'].value = 1; - var imports = {'primary': wasmRawExports}; - // Replace '.wasm' suffix with '.deferred.wasm'. - var deferred = wasmBinaryFile.slice(0, -5) + '.deferred.wasm'; - await instantiateAsync(null, deferred, imports); - } - __load_secondary_module.sig = 'v'; - __load_secondary_module.isAsync = true; - - - - var Fibers = { - nextFiber:0, - trampolineRunning:false, - trampoline() { - if (!Fibers.trampolineRunning && Fibers.nextFiber) { - Fibers.trampolineRunning = true; - do { - var fiber = Fibers.nextFiber; - Fibers.nextFiber = 0; - Fibers.finishContextSwitch(fiber); - } while (Fibers.nextFiber); - Fibers.trampolineRunning = false; - } - }, - finishContextSwitch(newFiber) { - var stack_base = HEAPU32[((newFiber)>>2)]; - var stack_max = HEAPU32[(((newFiber)+(4))>>2)]; - _emscripten_stack_set_limits(stack_base, stack_max); - - stackRestore(HEAPU32[(((newFiber)+(8))>>2)]); - - var entryPoint = HEAPU32[(((newFiber)+(12))>>2)]; - - if (entryPoint !== 0) { - Asyncify.currData = null; - HEAPU32[(((newFiber)+(12))>>2)] = 0; - - var userData = HEAPU32[(((newFiber)+(16))>>2)]; - ((a1) => {} /* a dynamic function call to signature vi, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(userData); - } else { - var asyncifyData = newFiber + 20; - Asyncify.currData = asyncifyData; - - Asyncify.state = Asyncify.State.Rewinding; - _asyncify_start_rewind(asyncifyData); - Asyncify.doRewind(asyncifyData); - } - }, - }; - - - - var _emscripten_fiber_swap = (oldFiber, newFiber) => { - if (ABORT) return; - if (Asyncify.state === Asyncify.State.Normal) { - Asyncify.state = Asyncify.State.Unwinding; - - var asyncifyData = oldFiber + 20; - Asyncify.setDataRewindFunc(asyncifyData); - Asyncify.currData = asyncifyData; - - _asyncify_start_unwind(asyncifyData); - - var stackTop = stackSave(); - HEAPU32[(((oldFiber)+(8))>>2)] = stackTop; - - Fibers.nextFiber = newFiber; - } else { - Asyncify.state = Asyncify.State.Normal; - _asyncify_stop_rewind(); - Asyncify.currData = null; - } - }; - _emscripten_fiber_swap.sig = 'vpp'; - _emscripten_fiber_swap.isAsync = true; - - - - var _SDL_GetTicks = () => (Date.now() - SDL.startTime)|0; - _SDL_GetTicks.sig = 'i'; - - var _SDL_LockSurface = (surf) => { - var surfData = SDL.surfaces[surf]; - - surfData.locked++; - if (surfData.locked > 1) return 0; - - if (!surfData.buffer) { - surfData.buffer = _malloc(surfData.width * surfData.height * 4); - HEAPU32[(((surf)+(20))>>2)] = surfData.buffer; - } - - // Mark in C/C++-accessible SDL structure - // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ... - // So we have fields all of the same size, and 5 of them before us. - // TODO: Use macros like in library.js - HEAPU32[(((surf)+(20))>>2)] = surfData.buffer; - - if (surf == SDL.screen && Module.screenIsReadOnly && surfData.image) return 0; - - if (SDL.defaults.discardOnLock) { - if (!surfData.image) { - surfData.image = surfData.ctx.createImageData(surfData.width, surfData.height); - } - if (!SDL.defaults.opaqueFrontBuffer) return; - } else { - surfData.image = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height); - } - - // Emulate desktop behavior and kill alpha values on the locked surface. (very costly!) Set SDL.defaults.opaqueFrontBuffer = false - // if you don't want this. - if (surf == SDL.screen && SDL.defaults.opaqueFrontBuffer) { - var data = surfData.image.data; - var num = data.length; - for (var i = 0; i < num/4; i++) { - data[i*4+3] = 255; // opacity, as canvases blend alpha - } - } - - if (SDL.defaults.copyOnLock && !SDL.defaults.discardOnLock) { - // Copy pixel data to somewhere accessible to 'C/C++' - if (surfData.isFlagSet(2097152)) { - // If this is needed then - // we should compact the data from 32bpp to 8bpp index. - // I think best way to implement this is use - // additional colorMap hash (color->index). - // Something like this: - // - // var size = surfData.width * surfData.height; - // var data = ''; - // for (var i = 0; i>2)], - y: HEAP32[(((rect)+(4))>>2)], - w: HEAP32[(((rect)+(8))>>2)], - h: HEAP32[(((rect)+(12))>>2)] - }; - }, - updateRect(rect, r) { - HEAP32[((rect)>>2)] = r.x; - HEAP32[(((rect)+(4))>>2)] = r.y; - HEAP32[(((rect)+(8))>>2)] = r.w; - HEAP32[(((rect)+(12))>>2)] = r.h; - }, - intersectionOfRects(first, second) { - var leftX = Math.max(first.x, second.x); - var leftY = Math.max(first.y, second.y); - var rightX = Math.min(first.x + first.w, second.x + second.w); - var rightY = Math.min(first.y + first.h, second.y + second.h); - - return { - x: leftX, - y: leftY, - w: Math.max(leftX, rightX) - leftX, - h: Math.max(leftY, rightY) - leftY - } - }, - checkPixelFormat(fmt) { - }, - loadColorToCSSRGB(color) { - var rgba = HEAP32[((color)>>2)]; - return 'rgb(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ')'; - }, - loadColorToCSSRGBA(color) { - var rgba = HEAP32[((color)>>2)]; - return 'rgba(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ',' + (((rgba >> 24)&255)/255) + ')'; - }, - translateColorToCSSRGBA:(rgba) => - 'rgba(' + (rgba&0xff) + ',' + (rgba>>8 & 0xff) + ',' + (rgba>>16 & 0xff) + ',' + (rgba>>>24)/0xff + ')', - translateRGBAToCSSRGBA:(r, g, b, a) => - 'rgba(' + (r&0xff) + ',' + (g&0xff) + ',' + (b&0xff) + ',' + (a&0xff)/255 + ')', - translateRGBAToColor:(r, g, b, a) => r | g << 8 | b << 16 | a << 24, - makeSurface(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) { - var is_SDL_HWSURFACE = flags & 134217729; - var is_SDL_HWPALETTE = flags & 2097152; - var is_SDL_OPENGL = flags & 67108864; - - var surf = _malloc(60); - var pixelFormat = _malloc(44); - // surface with SDL_HWPALETTE flag is 8bpp surface (1 byte) - var bpp = is_SDL_HWPALETTE ? 1 : 4; - var buffer = 0; - - // preemptively initialize this for software surfaces, - // otherwise it will be lazily initialized inside of SDL_LockSurface - if (!is_SDL_HWSURFACE && !is_SDL_OPENGL) { - buffer = _malloc(width * height * 4); - } - - HEAP32[((surf)>>2)] = flags; - HEAPU32[(((surf)+(4))>>2)] = pixelFormat; - HEAP32[(((surf)+(8))>>2)] = width; - HEAP32[(((surf)+(12))>>2)] = height; - HEAP32[(((surf)+(16))>>2)] = width * bpp; // assuming RGBA or indexed for now, - // since that is what ImageData gives us in browsers - HEAPU32[(((surf)+(20))>>2)] = buffer; - - var canvas = Browser.getCanvas(); - HEAP32[(((surf)+(36))>>2)] = 0; - HEAP32[(((surf)+(40))>>2)] = 0; - HEAP32[(((surf)+(44))>>2)] = canvas.width; - HEAP32[(((surf)+(48))>>2)] = canvas.height; - - HEAP32[(((surf)+(56))>>2)] = 1; - - HEAP32[((pixelFormat)>>2)] = -2042224636; - HEAP32[(((pixelFormat)+(4))>>2)] = 0;// TODO - HEAP8[(pixelFormat)+(8)] = bpp * 8; - HEAP8[(pixelFormat)+(9)] = bpp; - - HEAP32[(((pixelFormat)+(12))>>2)] = rmask || 0x000000ff; - HEAP32[(((pixelFormat)+(16))>>2)] = gmask || 0x0000ff00; - HEAP32[(((pixelFormat)+(20))>>2)] = bmask || 0x00ff0000; - HEAP32[(((pixelFormat)+(24))>>2)] = amask || 0xff000000; - - // Decide if we want to use WebGL or not - SDL.GL = SDL.GL || is_SDL_OPENGL; - if (!usePageCanvas) { - if (SDL.canvasPool.length > 0) { - canvas = SDL.canvasPool.pop(); - } else { - canvas = document.createElement('canvas'); - } - canvas.width = width; - canvas.height = height; - } - - var webGLContextAttributes = { - antialias: ((SDL.glAttributes[13] != 0) && (SDL.glAttributes[14] > 1)), - depth: (SDL.glAttributes[6] > 0), - stencil: (SDL.glAttributes[7] > 0), - alpha: (SDL.glAttributes[3] > 0) - }; - - var ctx = Browser.createContext(canvas, is_SDL_OPENGL, usePageCanvas, webGLContextAttributes); - - SDL.surfaces[surf] = { - width, - height, - canvas, - ctx, - surf, - buffer, - pixelFormat, - alpha: 255, - flags, - locked: 0, - usePageCanvas, - source, - - isFlagSet: (flag) => flags & flag - }; - - return surf; - }, - copyIndexedColorData(surfData, rX, rY, rW, rH) { - // HWPALETTE works with palette - // set by SDL_SetColors - if (!surfData.colors) { - return; - } - - var canvas = Browser.getCanvas(); - var fullWidth = canvas.width; - var fullHeight = canvas.height; - - var startX = rX || 0; - var startY = rY || 0; - var endX = (rW || (fullWidth - startX)) + startX; - var endY = (rH || (fullHeight - startY)) + startY; - - var buffer = surfData.buffer; - - if (!surfData.image.data32) { - surfData.image.data32 = new Uint32Array(surfData.image.data.buffer); - } - var data32 = surfData.image.data32; - - var colors32 = surfData.colors32; - - for (var y = startY; y < endY; ++y) { - var base = y * fullWidth; - for (var x = startX; x < endX; ++x) { - data32[base + x] = colors32[HEAPU8[(buffer)+(base + x)]]; - } - } - }, - freeSurface(surf) { - var refcountPointer = surf + 56; - var refcount = HEAP32[((refcountPointer)>>2)]; - if (refcount > 1) { - HEAP32[((refcountPointer)>>2)] = refcount - 1; - return; - } - - var info = SDL.surfaces[surf]; - if (!info.usePageCanvas && info.canvas) SDL.canvasPool.push(info.canvas); - _free(info.buffer); - _free(info.pixelFormat); - _free(surf); - SDL.surfaces[surf] = null; - - if (surf === SDL.screen) { - SDL.screen = null; - } - }, - blitSurface(src, srcrect, dst, dstrect, scale) { - var srcData = SDL.surfaces[src]; - var dstData = SDL.surfaces[dst]; - var sr, dr; - if (srcrect) { - sr = SDL.loadRect(srcrect); - } else { - sr = { x: 0, y: 0, w: srcData.width, h: srcData.height }; - } - if (dstrect) { - dr = SDL.loadRect(dstrect); - } else { - dr = { x: 0, y: 0, w: srcData.width, h: srcData.height }; - } - if (dstData.clipRect) { - var widthScale = (!scale || sr.w === 0) ? 1 : sr.w / dr.w; - var heightScale = (!scale || sr.h === 0) ? 1 : sr.h / dr.h; - - dr = SDL.intersectionOfRects(dstData.clipRect, dr); - - sr.w = dr.w * widthScale; - sr.h = dr.h * heightScale; - - if (dstrect) { - SDL.updateRect(dstrect, dr); - } - } - var blitw, blith; - if (scale) { - blitw = dr.w; blith = dr.h; - } else { - blitw = sr.w; blith = sr.h; - } - if (sr.w === 0 || sr.h === 0 || blitw === 0 || blith === 0) { - return 0; - } - var oldAlpha = dstData.ctx.globalAlpha; - dstData.ctx.globalAlpha = srcData.alpha/255; - dstData.ctx.drawImage(srcData.canvas, sr.x, sr.y, sr.w, sr.h, dr.x, dr.y, blitw, blith); - dstData.ctx.globalAlpha = oldAlpha; - if (dst != SDL.screen) { - // XXX As in IMG_Load, for compatibility we write out |pixels| - warnOnce('WARNING: copying canvas data to memory for compatibility'); - _SDL_LockSurface(dst); - dstData.locked--; // The surface is not actually locked in this hack - } - return 0; - }, - downFingers:{ - }, - savedKeydown:null, - receiveEvent(event) { - function unpressAllPressedKeys() { - // Un-press all pressed keys: TODO - for (var keyCode of Object.values(SDL.keyboardMap)) { - SDL.events.push({ - type: 'keyup', - keyCode, - }); - } - }; - switch (event.type) { - case 'touchstart': - case 'touchmove': { - event.preventDefault(); - - var touches = []; - - // Clear out any touchstart events that we've already processed - if (event.type === 'touchstart') { - for (var touch of event.touches) { - if (SDL.downFingers[touch.identifier] != true) { - SDL.downFingers[touch.identifier] = true; - touches.push(touch); - } - } - } else { - touches = event.touches; - } - - var firstTouch = touches[0]; - if (firstTouch) { - if (event.type == 'touchstart') { - SDL.DOMButtons[0] = 1; - } - var mouseEventType; - switch (event.type) { - case 'touchstart': mouseEventType = 'mousedown'; break; - case 'touchmove': mouseEventType = 'mousemove'; break; - } - var mouseEvent = { - type: mouseEventType, - button: 0, - pageX: firstTouch.clientX, - pageY: firstTouch.clientY - }; - SDL.events.push(mouseEvent); - } - - for (var touch of touches) { - SDL.events.push({ - type: event.type, - touch - }); - }; - break; - } - case 'touchend': { - event.preventDefault(); - - // Remove the entry in the SDL.downFingers hash - // because the finger is no longer down. - for (var touch of event.changedTouches) { - if (SDL.downFingers[touch.identifier] === true) { - delete SDL.downFingers[touch.identifier]; - } - } - - var mouseEvent = { - type: 'mouseup', - button: 0, - pageX: event.changedTouches[0].clientX, - pageY: event.changedTouches[0].clientY - }; - SDL.DOMButtons[0] = 0; - SDL.events.push(mouseEvent); - - for (var touch of event.changedTouches) { - SDL.events.push({ - type: 'touchend', - touch - }); - }; - break; - } - case 'DOMMouseScroll': - case 'mousewheel': - case 'wheel': - // Flip the wheel direction to translate from browser wheel direction - // (+:down) to SDL direction (+:up) - var delta = -Browser.getMouseWheelDelta(event); - // Quantize to integer so that minimum scroll is at least +/- 1. - delta = (delta == 0) ? 0 : (delta > 0 ? Math.max(delta, 1) : Math.min(delta, -1)); - - // Simulate old-style SDL events representing mouse wheel input as buttons - // Subtract one since JS->C marshalling is defined to add one back. - var button = (delta > 0 ? 4 : 5) - 1; - SDL.events.push({ type: 'mousedown', button, pageX: event.pageX, pageY: event.pageY }); - SDL.events.push({ type: 'mouseup', button, pageX: event.pageX, pageY: event.pageY }); - - // Pass a delta motion event. - SDL.events.push({ type: 'wheel', deltaX: 0, deltaY: delta }); - // If we don't prevent this, then 'wheel' event will be sent again by - // the browser as 'DOMMouseScroll' and we will receive this same event - // the second time. - event.preventDefault(); - break; - case 'mousemove': - if (SDL.DOMButtons[0] === 1) { - SDL.events.push({ - type: 'touchmove', - touch: { - identifier: 0, - deviceID: -1, - pageX: event.pageX, - pageY: event.pageY - } - }); - } - if (Browser.pointerLock) { - // workaround for firefox bug 750111 - if ('mozMovementX' in event) { - event['movementX'] = event['mozMovementX']; - event['movementY'] = event['mozMovementY']; - } - // workaround for Firefox bug 782777 - if (event['movementX'] == 0 && event['movementY'] == 0) { - // ignore a mousemove event if it doesn't contain any movement info - // (without pointer lock, we infer movement from pageX/pageY, so this check is unnecessary) - event.preventDefault(); - return; - } - } - // fall through - case 'keydown': - case 'keyup': - case 'keypress': - case 'mousedown': - case 'mouseup': - // If we preventDefault on keydown events, the subsequent keypress events - // won't fire. However, it's fine (and in some cases necessary) to - // preventDefault for keys that don't generate a character. Otherwise, - // preventDefault is the right thing to do in general. - if (event.type !== 'keydown' || (!SDL.unicode && !SDL.textInput) || (event.key == 'Backspace' || event.key == 'Tab')) { - event.preventDefault(); - } - - if (event.type == 'mousedown') { - SDL.DOMButtons[event.button] = 1; - SDL.events.push({ - type: 'touchstart', - touch: { - identifier: 0, - deviceID: -1, - pageX: event.pageX, - pageY: event.pageY - } - }); - } else if (event.type == 'mouseup') { - // ignore extra ups, can happen if we leave the canvas while pressing down, then return, - // since we add a mouseup in that case - if (!SDL.DOMButtons[event.button]) { - return; - } - - SDL.events.push({ - type: 'touchend', - touch: { - identifier: 0, - deviceID: -1, - pageX: event.pageX, - pageY: event.pageY - } - }); - SDL.DOMButtons[event.button] = 0; - } - - // We can only request fullscreen as the result of user input. - // Due to this limitation, we toggle a boolean on keydown which - // SDL_WM_ToggleFullScreen will check and subsequently set another - // flag indicating for us to request fullscreen on the following - // keyup. This isn't perfect, but it enables SDL_WM_ToggleFullScreen - // to work as the result of a keypress (which is an extremely - // common use case). - if (event.type === 'keydown' || event.type === 'mousedown') { - SDL.canRequestFullscreen = true; - } else if (event.type === 'keyup' || event.type === 'mouseup') { - if (SDL.isRequestingFullscreen) { - Module['requestFullscreen'](/*lockPointer=*/true, /*resizeCanvas=*/true); - SDL.isRequestingFullscreen = false; - } - SDL.canRequestFullscreen = false; - } - - // SDL expects a unicode character to be passed to its keydown events. - // Unfortunately, the browser APIs only provide a charCode property on - // keypress events, so we must backfill in keydown events with their - // subsequent keypress event's charCode. - if (event.type === 'keypress' && SDL.savedKeydown) { - // charCode is read-only - SDL.savedKeydown.keypressCharCode = event.charCode; - SDL.savedKeydown = null; - } else if (event.type === 'keydown') { - SDL.savedKeydown = event; - } - - // Don't push keypress events unless SDL_StartTextInput has been called. - if (event.type !== 'keypress' || SDL.textInput) { - SDL.events.push(event); - } - break; - case 'mouseout': - // Un-press all pressed mouse buttons, because we might miss the release outside of the canvas - for (var i = 0; i < 3; i++) { - if (SDL.DOMButtons[i]) { - SDL.events.push({ - type: 'mouseup', - button: i, - pageX: event.pageX, - pageY: event.pageY - }); - SDL.DOMButtons[i] = 0; - } - } - event.preventDefault(); - break; - case 'focus': - SDL.events.push(event); - event.preventDefault(); - break; - case 'blur': - SDL.events.push(event); - unpressAllPressedKeys(); - event.preventDefault(); - break; - case 'visibilitychange': - SDL.events.push({ - type: 'visibilitychange', - visible: !document.hidden - }); - unpressAllPressedKeys(); - event.preventDefault(); - break; - case 'unload': - if (MainLoop.runner) { - SDL.events.push(event); - // Force-run a main event loop, since otherwise this event will never be caught! - MainLoop.runner(); - } - return; - case 'resize': - SDL.events.push(event); - // manually triggered resize event doesn't have a preventDefault member - if (event.preventDefault) { - event.preventDefault(); - } - break; - } - if (SDL.events.length >= 10000) { - err('SDL event queue full, dropping events'); - SDL.events = SDL.events.slice(0, 10000); - } - // If we have a handler installed, this will push the events to the app - // instead of the app polling for them. - SDL.flushEventsToHandler(); - return; - }, - lookupKeyCodeForEvent(event) { - var code = event.keyCode; - if (code >= 65 && code <= 90) { // ASCII A-Z - code += 32; // make lowercase for SDL - } else { - // Look up DOM code in the keyCodes table with fallback for ASCII codes - // which can match between DOM codes and SDL keycodes (allows keyCodes - // to be smaller). - code = SDL.keyCodes[code] || (code < 128 ? code : 0); - // If this is one of the modifier keys (224 | 1<<10 - 227 | 1<<10), and the event specifies that it is - // a right key, add 4 to get the right key SDL key code. - if (event.location === 2 /*KeyboardEvent.DOM_KEY_LOCATION_RIGHT*/ && code >= (224 | 1<<10) && code <= (227 | 1<<10)) { - code += 4; - } - } - return code; - }, - handleEvent(event) { - if (event.handled) return; - event.handled = true; - - switch (event.type) { - case 'touchstart': - case 'touchend': - case 'touchmove': { - Browser.calculateMouseEvent(event); - break; - } - case 'keydown': - case 'keyup': { - var down = event.type === 'keydown'; - var code = SDL.lookupKeyCodeForEvent(event); - // Ignore key events that we don't (yet) map to SDL keys - if (!code) return; - // Assigning a boolean to HEAP8, that's alright but Closure would like to warn about it. - // TODO(https://github.com/emscripten-core/emscripten/issues/16311): - // This is kind of ugly hack. Perhaps we can find a better way? - /** @suppress{checkTypes} */ - HEAP8[(SDL.keyboardState)+(code)] = down; - // TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED - SDL.modState = - (HEAP8[(SDL.keyboardState)+(1248)] ? 64 : 0) | - (HEAP8[(SDL.keyboardState)+(1249)] ? 1 : 0) | - (HEAP8[(SDL.keyboardState)+(1250)] ? 256 : 0) | - (HEAP8[(SDL.keyboardState)+(1252)] ? 128 : 0) | - (HEAP8[(SDL.keyboardState)+(1253)] ? 2 : 0) | - (HEAP8[(SDL.keyboardState)+(1254)] ? 512 : 0); - if (down) { - SDL.keyboardMap[code] = event.keyCode; // save the DOM input, which we can use to unpress it during blur - } else { - delete SDL.keyboardMap[code]; - } - - break; - } - case 'mousedown': - case 'mouseup': - if (event.type == 'mousedown') { - // SDL_BUTTON(x) is defined as (1 << ((x)-1)). SDL buttons are 1-3, - // and DOM buttons are 0-2, so this means that the below formula is - // correct. - SDL.buttonState |= 1 << event.button; - } else if (event.type == 'mouseup') { - SDL.buttonState &= ~(1 << event.button); - } - // fall through - case 'mousemove': { - Browser.calculateMouseEvent(event); - break; - } - } - }, - flushEventsToHandler() { - if (!SDL.eventHandler) return; - - while (SDL.pollEvent(SDL.eventHandlerTemp)) { - ((a1, a2) => {} /* a dynamic function call to signature iii, but there are no exported function pointers with that signature, so this path should never be taken. Build with ASSERTIONS enabled to validate. */)(SDL.eventHandlerContext, SDL.eventHandlerTemp); - } - }, - pollEvent(ptr) { - if (SDL.initFlags & 512 && SDL.joystickEventState) { - // If SDL_INIT_JOYSTICK was supplied AND the joystick system is configured - // to automatically query for events, query for joystick events. - SDL.queryJoysticks(); - } - if (ptr) { - while (SDL.events.length > 0) { - if (SDL.makeCEvent(SDL.events.shift(), ptr) !== false) return 1; - } - return 0; - } - // XXX: somewhat risky in that we do not check if the event is real or not - // (makeCEvent returns false) if no pointer supplied - return SDL.events.length > 0; - }, - makeCEvent(event, ptr) { - if (typeof event == 'number') { - // This is a pointer to a copy of a native C event that was SDL_PushEvent'ed - _memcpy(ptr, event, 28); - _free(event); // the copy is no longer needed - return; - } - - SDL.handleEvent(event); - - switch (event.type) { - case 'keydown': case 'keyup': { - var down = event.type === 'keydown'; - var key = SDL.lookupKeyCodeForEvent(event); - // Ignore key events that we don't (yet) map to SDL keys - if (!key) return false; - var scan; - if (key >= 1024) { - scan = key - 1024; - } else { - scan = SDL.scanCodes[key] || key; - } - - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP8[(ptr)+(8)] = down ? 1 : 0; - HEAP8[(ptr)+(9)] = 0; // TODO - HEAP32[(((ptr)+(12))>>2)] = scan; - HEAP32[(((ptr)+(16))>>2)] = key; - HEAP16[(((ptr)+(20))>>1)] = SDL.modState; - // some non-character keys (e.g. backspace and tab) won't have keypressCharCode set, fill in with the keyCode. - HEAP32[(((ptr)+(24))>>2)] = event.keypressCharCode || key; - - break; - } - case 'keypress': { - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - // Not filling in windowID for now - stringToUTF8(String.fromCharCode(event.charCode), ptr + 8, 4); - break; - } - case 'mousedown': case 'mouseup': case 'mousemove': { - if (event.type != 'mousemove') { - var down = event.type === 'mousedown'; - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP32[(((ptr)+(4))>>2)] = 0; - HEAP32[(((ptr)+(8))>>2)] = 0; - HEAP32[(((ptr)+(12))>>2)] = 0; - HEAP8[(ptr)+(16)] = event.button+1; // DOM buttons are 0-2, SDL 1-3 - HEAP8[(ptr)+(17)] = down ? 1 : 0; - HEAP32[(((ptr)+(20))>>2)] = Browser.mouseX; - HEAP32[(((ptr)+(24))>>2)] = Browser.mouseY; - } else { - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP32[(((ptr)+(4))>>2)] = 0; - HEAP32[(((ptr)+(8))>>2)] = 0; - HEAP32[(((ptr)+(12))>>2)] = 0; - HEAP32[(((ptr)+(16))>>2)] = SDL.buttonState; - HEAP32[(((ptr)+(20))>>2)] = Browser.mouseX; - HEAP32[(((ptr)+(24))>>2)] = Browser.mouseY; - HEAP32[(((ptr)+(28))>>2)] = Browser.mouseMovementX; - HEAP32[(((ptr)+(32))>>2)] = Browser.mouseMovementY; - } - break; - } - case 'wheel': { - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP32[(((ptr)+(16))>>2)] = event.deltaX; - HEAP32[(((ptr)+(20))>>2)] = event.deltaY; - break; - } - case 'touchstart': case 'touchend': case 'touchmove': { - var touch = event.touch; - if (!Browser.touches[touch.identifier]) break; - var canvas = Browser.getCanvas(); - var x = Browser.touches[touch.identifier].x / canvas.width; - var y = Browser.touches[touch.identifier].y / canvas.height; - var lx = Browser.lastTouches[touch.identifier].x / canvas.width; - var ly = Browser.lastTouches[touch.identifier].y / canvas.height; - var dx = x - lx; - var dy = y - ly; - if (touch['deviceID'] === undefined) touch.deviceID = SDL.TOUCH_DEFAULT_ID; - if (dx === 0 && dy === 0 && event.type === 'touchmove') return false; // don't send these if nothing happened - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP32[(((ptr)+(4))>>2)] = _SDL_GetTicks(); - HEAP64[(((ptr)+(8))>>3)] = BigInt(touch.deviceID); - HEAP64[(((ptr)+(16))>>3)] = BigInt(touch.identifier); - HEAPF32[(((ptr)+(24))>>2)] = x; - HEAPF32[(((ptr)+(28))>>2)] = y; - HEAPF32[(((ptr)+(32))>>2)] = dx; - HEAPF32[(((ptr)+(36))>>2)] = dy; - if (touch.force !== undefined) { - HEAPF32[(((ptr)+(40))>>2)] = touch.force; - } else { // No pressure data, send a digital 0/1 pressure. - HEAPF32[(((ptr)+(40))>>2)] = event.type == "touchend" ? 0 : 1; - } - break; - } - case 'unload': { - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - break; - } - case 'resize': { - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP32[(((ptr)+(4))>>2)] = event.w; - HEAP32[(((ptr)+(8))>>2)] = event.h; - break; - } - case 'joystick_button_up': case 'joystick_button_down': { - var state = event.type === 'joystick_button_up' ? 0 : 1; - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP8[(ptr)+(4)] = event.index; - HEAP8[(ptr)+(5)] = event.button; - HEAP8[(ptr)+(6)] = state; - break; - } - case 'joystick_axis_motion': { - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP8[(ptr)+(4)] = event.index; - HEAP8[(ptr)+(5)] = event.axis; - HEAP32[(((ptr)+(8))>>2)] = SDL.joystickAxisValueConversion(event.value); - break; - } - case 'focus': { - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP32[(((ptr)+(4))>>2)] = 0; - HEAP8[(ptr)+(8)] = 12; - break; - } - case 'blur': { - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP32[(((ptr)+(4))>>2)] = 0; - HEAP8[(ptr)+(8)] = 13; - break; - } - case 'visibilitychange': { - var visibilityEventID = event.visible ? 1 : 2; - HEAP32[((ptr)>>2)] = SDL.DOMEventToSDLEvent[event.type]; - HEAP32[(((ptr)+(4))>>2)] = 0; - HEAP8[(ptr)+(8)] = visibilityEventID; - break; - } - default: abort('Unhandled SDL event: ' + event.type); - } - }, - makeFontString(height, fontName) { - if (fontName.charAt(0) != "'" && fontName.charAt(0) != '"') { - // https://developer.mozilla.org/ru/docs/Web/CSS/font-family - // Font family names containing whitespace should be quoted. - // BTW, quote all font names is easier than searching spaces - fontName = '"' + fontName + '"'; - } - return height + 'px ' + fontName + ', serif'; - }, - estimateTextWidth(fontData, text) { - var h = fontData.size; - var fontString = SDL.makeFontString(h, fontData.name); - var tempCtx = SDL.ttfContext; - tempCtx.font = fontString; - var ret = tempCtx.measureText(text).width | 0; - return ret; - }, - allocateChannels(num) { // called from Mix_AllocateChannels and init - if (SDL.numChannels >= num && num != 0) return; - SDL.numChannels = num; - SDL.channels = []; - for (var i = 0; i < num; i++) { - SDL.channels[i] = { - audio: null, - volume: 1.0 - }; - } - }, - setGetVolume(info, volume) { - if (!info) return 0; - var ret = info.volume * 128; // MIX_MAX_VOLUME - if (volume != -1) { - info.volume = Math.min(Math.max(volume, 0), 128) / 128; - if (info.audio) { - try { - info.audio.volume = info.volume; // For